[evolution] EWebKitEditor: Switch to JavaScriptCore API



commit fcd4768b2ef888e01705ed6256cc7bb7e0dbd16e
Author: Milan Crha <mcrha redhat com>
Date:   Mon Apr 27 15:06:56 2020 +0200

    EWebKitEditor: Switch to JavaScriptCore API
    
    The C DOM API had been deprecated since WebKitGTK 2.22. This is
    the editor rewrite to use the new API.

 CMakeLists.txt                                     |     2 +-
 data/webkit/CMakeLists.txt                         |     3 +
 data/webkit/e-convert.js                           |   949 +-
 data/webkit/e-editor.js                            |  5840 ++++++
 data/webkit/e-selection.js                         |   435 +
 data/webkit/e-undo-redo.js                         |   940 +
 data/webkit/e-web-view.js                          |    21 +-
 .../evolution-util/evolution-util-docs.sgml.in     |     1 -
 src/composer/e-composer-actions.c                  |    36 +-
 src/composer/e-composer-private.c                  |     2 +
 src/composer/e-composer-private.h                  |     3 +
 src/composer/e-msg-composer.c                      |   661 +-
 src/composer/e-msg-composer.h                      |     2 +
 src/e-util/CMakeLists.txt                          |     3 +-
 src/e-util/e-content-editor.c                      |   838 +-
 src/e-util/e-content-editor.h                      |   226 +-
 src/e-util/e-html-editor-actions.c                 |   223 +-
 src/e-util/e-html-editor-actions.h                 |    10 +-
 src/e-util/e-html-editor-cell-dialog.c             |     4 +-
 src/e-util/e-html-editor-find-dialog.c             |     4 +-
 src/e-util/e-html-editor-hrule-dialog.c            |    34 +-
 src/e-util/e-html-editor-image-dialog.c            |     4 +-
 src/e-util/e-html-editor-link-dialog.c             |    17 +-
 src/e-util/e-html-editor-manager.ui                |     8 +-
 src/e-util/e-html-editor-page-dialog.c             |    70 +-
 src/e-util/e-html-editor-paragraph-dialog.c        |    10 +
 src/e-util/e-html-editor-private.h                 |    11 +-
 src/e-util/e-html-editor-replace-dialog.c          |     4 +-
 src/e-util/e-html-editor-spell-check-dialog.c      |    18 +-
 src/e-util/e-html-editor-table-dialog.c            |    43 +-
 src/e-util/e-html-editor.c                         |   531 +-
 src/e-util/e-html-editor.h                         |    26 +-
 src/e-util/e-mail-signature-editor.c               |   178 +-
 src/e-util/e-misc-utils.c                          |   270 -
 src/e-util/e-misc-utils.h                          |    35 -
 src/e-util/e-spell-checker.c                       |     4 +
 src/e-util/e-util-enums.h                          |    83 +-
 src/e-util/e-util.h                                |     1 -
 src/e-util/e-web-extension-container.c             |  1280 --
 src/e-util/e-web-extension-container.h             |   103 -
 src/e-util/e-web-view.c                            |     1 -
 src/e-util/test-html-editor-units-bugs.c           |   618 +-
 src/e-util/test-html-editor-units-bugs.h           |     2 +
 src/e-util/test-html-editor-units-utils.c          |   320 +-
 src/e-util/test-html-editor-units-utils.h          |     4 +-
 src/e-util/test-html-editor-units.c                |  5664 +++++-
 src/e-util/test-html-editor.c                      |   207 +-
 src/e-util/test-web-view-jsc.c                     |  1025 +-
 src/mail/e-cid-request.c                           |    68 +-
 src/mail/e-cid-request.h                           |    37 +
 src/mail/e-mail-display.c                          |    70 +-
 src/mail/e-mail-notes.c                            |   171 +-
 .../composer-to-meeting/e-composer-to-meeting.c    |   120 +-
 src/modules/webkit-editor/e-webkit-editor.c        |  5516 +++---
 .../webkit-editor/web-extension/CMakeLists.txt     |    32 +-
 .../web-extension/e-composer-dom-functions.c       |   610 -
 .../web-extension/e-composer-dom-functions.h       |    46 -
 .../web-extension/e-dialogs-dom-functions.c        |  1489 --
 .../web-extension/e-dialogs-dom-functions.h        |   134 -
 .../webkit-editor/web-extension/e-dom-utils.c      |   621 -
 .../webkit-editor/web-extension/e-dom-utils.h      |    91 -
 .../web-extension/e-editor-dom-functions.c         | 18336 -------------------
 .../web-extension/e-editor-dom-functions.h         |   393 -
 .../webkit-editor/web-extension/e-editor-page.c    |  1047 --
 .../webkit-editor/web-extension/e-editor-page.h    |   223 -
 .../web-extension/e-editor-undo-redo-manager.c     |  2990 ---
 .../web-extension/e-editor-undo-redo-manager.h     |   184 -
 .../web-extension/e-editor-web-extension-main.c    |    45 -
 .../web-extension/e-editor-web-extension-names.h   |    25 -
 .../web-extension/e-editor-web-extension.c         |  2851 +--
 .../web-extension/e-editor-web-extension.h         |    12 -
 src/plugins/external-editor/external-editor.c      |    75 +-
 src/web-extensions/CMakeLists.txt                  |     1 +
 src/web-extensions/e-web-extension.c               |    26 +-
 74 files changed, 19707 insertions(+), 36280 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index edc7a280cc..678e822cb4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,7 +82,7 @@ set(gsettings_desktop_schemas_minimum_version 2.91.92)
 set(libpst_minimum_version 0.6.54)
 set(libxml_minimum_version 2.7.3)
 set(shared_mime_info_minimum_version 0.22)
-set(webkit2gtk_minimum_version 2.24.0)
+set(webkit2gtk_minimum_version 2.28.0)
 
 # Optional Packages
 set(champlain_minimum_version 0.12)
diff --git a/data/webkit/CMakeLists.txt b/data/webkit/CMakeLists.txt
index 37832ac9df..3397bd03c7 100644
--- a/data/webkit/CMakeLists.txt
+++ b/data/webkit/CMakeLists.txt
@@ -1,5 +1,8 @@
 set(DATA_FILES
        e-convert.js
+       e-editor.js
+       e-selection.js
+       e-undo-redo.js
        e-web-view.js
        webview.css
        webview-print.css
diff --git a/data/webkit/e-convert.js b/data/webkit/e-convert.js
index 4e67b92dc2..7f7056e45a 100644
--- a/data/webkit/e-convert.js
+++ b/data/webkit/e-convert.js
@@ -1,12 +1,957 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2019 Red Hat (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/>.
+ */
+
 'use strict';
 
 /* semi-convention: private functions start with lower-case letter,
    public functions start with upper-case letter. */
 
-function EvoConvertToPlainText(element)
+var EvoConvert = {
+       MIN_PARAGRAPH_WIDTH : 5, // in characters
+       MIN_OL_WIDTH : 6, // includes ". " at the end
+       TAB_WIDTH : 8, // in characters
+
+       ALIGN_LEFT : 0,
+       ALIGN_CENTER : 1,
+       ALIGN_RIGHT : 2,
+       ALIGN_JUSTIFY : 3,
+
+       NOWRAP_CHAR_START : "\x01",
+       NOWRAP_CHAR_END : "\x02"
+};
+
+EvoConvert.GetOLMaxLetters = function(type, levels)
+{
+       if (type && type.toUpperCase() == "I") {
+               if (levels < 2)
+                       return 1;
+               if (levels < 3)
+                       return 2;
+               if (levels < 8)
+                       return 3;
+               if (levels < 18)
+                       return 4;
+               if (levels < 28)
+                       return 5;
+               if (levels < 38)
+                       return 6;
+               if (levels < 88)
+                       return 7
+               if (levels < 188)
+                       return 8;
+               if (levels < 288)
+                       return 9;
+               if (levels < 388)
+                       return 10;
+               if (levels < 888)
+                       return 11;
+               return 12;
+       } else if (type && type.toUpperCase() == "A") {
+               if (levels < 27)
+                       return 1;
+               if (levels < 703)
+                       return 2;
+               if (levels < 18279)
+                       return 3;
+               return 4;
+       } else {
+               if (levels < 10)
+                       return 1;
+               if (levels < 100)
+                       return 2;
+               if (levels < 1000)
+                       return 3;
+               if (levels < 10000)
+                       return 4;
+               return 5;
+       }
+}
+
+EvoConvert.getOLIndexValue = function(type, value)
+{
+       var str = "";
+
+       if (type == "A" || type == "a") { // alpha
+               var add = type.charCodeAt(0);
+
+               do {
+                       str = String.fromCharCode(((value - 1) % 26) + add) + str;
+                       value = Math.floor((value - 1) / 26);
+               } while (value);
+       } else if (type == "I" || type == "i") { // roman
+               var base = "IVXLCDM";
+               var b, r, add = 0;
+
+               if (value > 3999) {
+                       str = "?";
+               } else {
+                       if (type == "i")
+                               base = base.toLowerCase();
+
+                       for (b = 0; value > 0 && b < 7 - 1; b += 2, value = Math.floor(value / 10)) {
+                               r = value % 10;
+                               if (r != 0) {
+                                       if (r < 4) {
+                                               for (; r; r--)
+                                                       str = String.fromCharCode(base.charCodeAt(b) + add) + 
str;
+                                       } else if (r == 4) {
+                                               str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
+                                               str = String.fromCharCode(base.charCodeAt(b) + add) + str;
+                                       } else if (r == 5) {
+                                               str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
+                                       } else if (r < 9) {
+                                               for (; r > 5; r--)
+                                                       str = String.fromCharCode(base.charCodeAt(b) + add) + 
str;
+                                               str = String.fromCharCode(base.charCodeAt(b + 1) + add) + str;
+                                       } else if (r == 9) {
+                                               str = String.fromCharCode(base.charCodeAt(b + 2) + add) + str;
+                                               str = String.fromCharCode(base.charCodeAt(b) + add) + str;
+                                       }
+                               }
+                       }
+               }
+       } else { // numeric
+               str = "" + value;
+       }
+
+       return str;
+}
+
+EvoConvert.getComputedOrNodeStyle = function(node)
+{
+       if (!node)
+               return null;
+
+       var parent = node;
+
+       while (parent && !(parent === document.body)) {
+               parent = parent.parentElement;
+       }
+
+       if (parent)
+               return window.getComputedStyle(node);
+
+       return node.style;
+}
+
+EvoConvert.replaceList = function(element, tagName)
+{
+       var ll, lists, type = null;
+
+       if (tagName == "OL")
+               type = "";
+
+       lists = element.getElementsByTagName(tagName);
+
+       for (ll = lists.length - 1; ll >= 0; ll--) {
+               var list;
+
+               list = lists.item(ll);
+
+               if (!list)
+                       continue;
+
+               var style = EvoConvert.getComputedOrNodeStyle(list), ltr, ii, prefixSuffix, indent;
+
+               if (!style)
+                       style = list.style;
+
+               ltr = !style || style.direction != "rtl";
+
+               if (type == null) {
+                       var level = 0, parent = list;
+
+                       for (parent = list.parentElement; parent; parent = parent.parentElement) {
+                               if (parent.tagName == "UL" || parent.tagName == "OL")
+                                       level++;
+                       }
+
+                       switch (level % 3) {
+                       default:
+                       case 0:
+                               prefixSuffix = " * ";
+                               break;
+                       case 1:
+                               prefixSuffix = " - ";
+                               break;
+                       case 2:
+                               prefixSuffix = " + ";
+                               break;
+                       }
+
+                       indent = 3;
+               } else {
+                       type = list.getAttribute("type");
+
+                       if (!type)
+                               type = "";
+
+                       var nChildren = 0, child;
+                       for (ii = 0; ii < list.children.length; ii++) {
+                               child = list.children.item(ii);
+                               if (child.tagName == "LI")
+                                       nChildren++;
+                       }
+
+                       prefixSuffix = ltr ? ". " : " .";
+                       indent = EvoConvert.GetOLMaxLetters(type, nChildren) + prefixSuffix.length;
+                       if (indent < EvoConvert.MIN_OL_WIDTH)
+                               indent = EvoConvert.MIN_OL_WIDTH;
+               }
+
+               if (list.hasAttribute("x-evo-extra-indent")) {
+                       var tmp = list.getAttribute("x-evo-extra-indent");
+
+                       if (tmp) {
+                               tmp = parseInt(tmp);
+
+                               if (!Number.isInteger(tmp))
+                                       tmp = 0;
+                       } else {
+                               tmp = 0;
+                       }
+
+                       indent += tmp;
+               }
+
+               var liCount = 0;
+
+               for (ii = 0; ii < list.children.length; ii++) {
+                       var child = list.children.item(ii), node;
+
+                       if (!child)
+                               continue;
+
+                       // nested lists
+                       if (child.tagName == "DIV" && child.hasAttribute("x-evo-extra-indent") && 
child.hasAttribute("x-evo-li-text")) {
+                               node = child.cloneNode(true);
+
+                               var tmp = child.getAttribute("x-evo-extra-indent");
+
+                               if (tmp) {
+                                       tmp = parseInt(tmp);
+
+                                       if (!Number.isInteger(tmp))
+                                               tmp = 0;
+                               } else {
+                                       tmp = 0;
+                               }
+
+                               node.setAttribute("x-evo-extra-indent", indent + tmp);
+
+                               tmp = node.getAttribute("x-evo-li-text");
+                               if (ltr)
+                                       tmp = " ".repeat(indent) + tmp;
+                               else
+                                       tmp = tmp + " ".repeat(indent);
+
+                               node.setAttribute("x-evo-li-text", tmp);
+                       } else if (child.tagName == "LI") {
+                               liCount++;
+
+                               node = document.createElement("DIV");
+                               if (list.style.width.endsWith("ch")) {
+                                       var width = parseInt(list.style.width.slice(0, -2));
+
+                                       if (Number.isInteger(width))
+                                               node.style.width = "" + width + "ch";
+                               }
+                               node.style.textAlign = list.style.testAlign;
+                               node.style.direction = list.style.direction;
+                               node.style.marginLeft = list.style.marginLeft;
+                               node.style.marginRight = list.style.marginRight;
+                               node.setAttribute("x-evo-extra-indent", indent);
+                               node.innerText = child.innerText;
+
+                               if (type == null) {
+                                       node.setAttribute("x-evo-li-text", prefixSuffix);
+                               } else {
+                                       var prefix;
+
+                                       prefix = EvoConvert.getOLIndexValue(type, liCount);
+
+                                       while (prefix.length + 2 /* prefixSuffix.length */ < indent) {
+                                               prefix = ltr ? " " + prefix : prefix + " ";
+                                       }
+
+                                       node.setAttribute("x-evo-li-text", ltr ? prefix + prefixSuffix : 
prefixSuffix + prefix);
+                               }
+                       } else {
+                               node = child.cloneNode(true);
+
+                               if (node.tagName == "UL" || node.tagName == "OL") {
+                                       var tmp = child.getAttribute("x-evo-extra-indent");
+
+                                       if (tmp) {
+                                               tmp = parseInt(tmp);
+
+                                               if (!Number.isInteger(tmp))
+                                                       tmp = 0;
+                                       } else {
+                                               tmp = 0;
+                                       }
+
+                                       node.setAttribute("x-evo-extra-indent", indent + tmp);
+                               }
+                       }
+
+                       list.parentNode.insertBefore(node, list);
+               }
+
+               list.parentNode.removeChild(list);
+       }
+}
+
+EvoConvert.calcLineLetters = function(line)
+{
+       var len;
+
+       if (line.search("\t") >= 0) {
+               var jj;
+
+               len = 0;
+
+               for (jj = 0; jj < line.length; jj++) {
+                       if (line.charAt(jj) == "\t") {
+                               len = len - (len % EvoConvert.TAB_SIZE) + EvoConvert.TAB_SIZE;
+                       } else {
+                               len++;
+                       }
+               }
+       } else {
+               len = line.length;
+       }
+
+       return len;
+}
+
+EvoConvert.getQuotePrefix = function(quoteLevel, ltr)
+{
+       var prefix = "";
+
+       if (quoteLevel > 0) {
+               prefix = ltr ? "> " : " <";
+               prefix = prefix.repeat(quoteLevel);
+       }
+
+       return prefix;
+}
+
+EvoConvert.formatParagraph = function(str, ltr, align, indent, whiteSpace, wrapWidth, extraIndent, liText, 
quoteLevel)
+{
+       if (!str || str == "")
+               return liText ? liText : str;
+
+       var lines = [], ii;
+
+       // wrap the string first
+       if (wrapWidth > 0) {
+               var worker = {
+                       collapseWhiteSpace : whiteSpace != "pre" && whiteSpace != "pre-wrap",
+                       collapseEndingWhiteSpace : whiteSpace != "pre",
+                       canWrap : whiteSpace != "nowrap",
+                       charWrap : whiteSpace == "pre",
+                       inAnchor : 0,
+                       ignoreLineLetters : 0, // used for EvoConvert.NOWRAP_CHAR_START and 
EvoConvert.NOWRAP_CHAR_END, which should be skipped in width calculation
+                       useWrapWidth : wrapWidth,
+                       spacesFrom : -1, // in 'str'
+                       lastSpace : -1, // in this->line
+                       lineLetters : 0,
+                       line : "",
+
+                       maybeRecalcIgnoreLineLetters : function() {
+                               if (this.ignoreLineLetters) {
+                                       var ii, len = this.line.length, chr;
+
+                                       this.ignoreLineLetters = 0;
+
+                                       for (ii = 0; ii < len; ii++) {
+                                               chr = this.line[ii];
+
+                                               if (chr == EvoConvert.NOWRAP_CHAR_START || chr == 
EvoConvert.NOWRAP_CHAR_END)
+                                                       this.ignoreLineLetters++;
+                                       }
+                               }
+                       },
+
+                       mayConsumeWhitespaceAfterWrap : function(str, ii) {
+                               return ii > 0 && this.line == "" && str[ii - 1] == EvoConvert.NOWRAP_CHAR_END;
+                       },
+
+                       isInUnwrapPart : function() {
+                               if (this.inAnchor)
+                                       return true;
+
+                               if (this.line[0] == EvoConvert.NOWRAP_CHAR_START)
+                                       return this.line.indexOf(EvoConvert.NOWRAP_CHAR_END) < 0;
+
+                               return false;
+                       },
+
+                       shouldWrap : function(nextChar) {
+                               return this.canWrap && (!this.collapseWhiteSpace || nextChar != '\n') &&
+                                       (!this.isInUnwrapPart() || this.lastSpace != -1) && (this.lineLetters 
- this.ignoreLineLetters > this.useWrapWidth || (
+                                       ((!this.charWrap && (nextChar == " " || nextChar == "\t") && 
this.lineLetters - this.ignoreLineLetters > this.useWrapWidth) ||
+                                       ((this.charWrap || (nextChar != " " && nextChar != "\t")) && 
this.lineLetters - this.ignoreLineLetters == this.useWrapWidth)) && (
+                                       this.lastSpace == -1/* || this.lastSpace == this.line.length*/)));
+                       },
+
+                       commitSpaces : function(ii) {
+                               if (this.spacesFrom != -1 && (!this.canWrap || this.line.length - 
this.ignoreLineLetters <= this.useWrapWidth)) {
+                                       var spaces;
+
+                                       spaces = ii - this.spacesFrom;
+
+                                       if (this.canWrap && this.line.length - this.ignoreLineLetters + 
spaces > this.useWrapWidth)
+                                               spaces = this.useWrapWidth - this.line.length + 
this.ignoreLineLetters;
+
+                                       if (!this.canWrap || (this.line.length - this.ignoreLineLetters + 
spaces <= this.useWrapWidth) && spaces >= 0) {
+                                               if (this.collapseWhiteSpace && (!extraIndent || lines.length))
+                                                       this.line += " ";
+                                               else
+                                                       this.line += " ".repeat(spaces);
+                                       }
+
+                                       this.spacesFrom = -1;
+                                       this.lastSpace = this.line.length;
+                               } else if (this.spacesFrom != -1) {
+                                       this.lastSpace = this.line.length;
+                               }
+                       },
+
+                       commit : function(ii, force) {
+                               this.commitSpaces(ii);
+
+                               var didWrap = false;
+
+                               if (this.canWrap && this.lastSpace != -1 && this.lineLetters - 
this.ignoreLineLetters > this.useWrapWidth) {
+                                       lines[lines.length] = this.line.substr(0, this.lastSpace);
+                                       this.line = this.line.substr(this.lastSpace);
+                                       this.maybeRecalcIgnoreLineLetters();
+                                       didWrap = true;
+                               } else if (!this.isInUnwrapPart() && this.useWrapWidth != -1 && 
this.lineLetters - this.ignoreLineLetters > this.useWrapWidth) {
+                                       var jj;
+
+                                       if (this.charWrap) {
+                                               var subLineLetters = 0, ignoreSubLineLetters = 0, chr;
+
+                                               for(jj = 0; jj < this.line.length; jj++) {
+                                                       chr = this.line.charAt(jj);
+
+                                                       if (chr == "\t") {
+                                                               subLineLetters = subLineLetters - 
((subLineLetters - ignoreSubLineLetters) % EvoConvert.TAB_SIZE) + EvoConvert.TAB_SIZE;
+                                                       } else {
+                                                               subLineLetters++;
+                                                       }
+
+                                                       if (chr == EvoConvert.NOWRAP_CHAR_START || chr == 
EvoConvert.NOWRAP_CHAR_END)
+                                                               ignoreSubLineLetters++;
+
+                                                       if (subLineLetters - ignoreSubLineLetters >= 
this.useWrapWidth)
+                                                               break;
+                                               }
+                                       } else {
+                                               jj = this.line.length;
+                                       }
+
+                                       lines[lines.length] = this.line.substr(0, jj);
+                                       this.line = this.line.substr(jj);
+                                       this.maybeRecalcIgnoreLineLetters();
+                                       didWrap = true;
+                               } else {
+                                       lines[lines.length] = this.line;
+                                       this.line = "";
+                                       this.ignoreLineLetters = 0;
+                               }
+
+                               if (this.canWrap && this.collapseEndingWhiteSpace && didWrap && 
lines[lines.length - 1].endsWith(" ")) {
+                                       if (lines[lines.length - 1].length == 1)
+                                               lines.length = lines.length - 1;
+                                       else
+                                               lines[lines.length - 1] = lines[lines.length - 1].substr(0, 
lines[lines.length - 1].length - 1);
+                               }
+
+                               if (force && this.line) {
+                                       lines[lines.length] = this.line;
+                                       this.line = "";
+                                       this.ignoreLineLetters = 0;
+                               }
+
+                               this.lineLetters = this.canWrap ? EvoConvert.calcLineLetters(this.line) : 
this.line.length;
+                               this.spacesFrom = -1;
+                               this.lastSpace = -1;
+                       }
+               };
+
+               if (worker.useWrapWidth < EvoConvert.MIN_PARAGRAPH_WIDTH)
+                       worker.useWrapWidth = EvoConvert.MIN_PARAGRAPH_WIDTH;
+
+               var chr;
+
+               for (ii = 0; ii < str.length; ii++) {
+                       chr = str.charAt(ii);
+
+                       if (chr == EvoConvert.NOWRAP_CHAR_START)
+                               worker.inAnchor++;
+
+                       if (chr == "\n") {
+                               if (!worker.mayConsumeWhitespaceAfterWrap(str, ii))
+                                       worker.commit(ii, true);
+                       } else if (!worker.charWrap && !worker.collapseWhiteSpace && chr == "\t") {
+                               if (worker.shouldWrap(str[ii + 1]))
+                                       worker.commit(ii, false);
+                               else
+                                       worker.commitSpaces(ii);
+
+                               var add = chr; // this expands the tab, instead of leaving it '\t'...  " 
".repeat(EvoConvert.TAB_WIDTH - ((worker.lineLetters - worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH));
+
+                               worker.lineLetters = worker.lineLetters - ((worker.lineLetters - 
worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
+
+                               if (worker.shouldWrap(str[ii + 1]))
+                                       worker.commit(ii, false);
+
+                               worker.line += add;
+                               worker.lineLetters += add.length;
+                       } else if (!worker.charWrap && (chr == " " || chr == "\t")) {
+                               var setSpacesFrom = false;
+
+                               if (chr == '\t') {
+                                       worker.lineLetters = worker.lineLetters - ((worker.lineLetters - 
worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
+                                       setSpacesFrom = true;
+                               } else if ((worker.spacesFrom == -1 && worker.line != "") || 
(!worker.collapseWhiteSpace && !worker.mayConsumeWhitespaceAfterWrap(str, ii))) {
+                                       worker.lineLetters++;
+                                       setSpacesFrom = true;
+                               }
+
+                               // all spaces at the end of paragraph line are ignored
+                               if (setSpacesFrom && worker.spacesFrom == -1)
+                                       worker.spacesFrom = ii;
+                       } else {
+                               worker.commitSpaces(ii);
+                               worker.line += chr;
+
+                               if (chr == "\t")
+                                       worker.lineLetters = worker.lineLetters - ((worker.lineLetters - 
worker.ignoreLineLetters) % EvoConvert.TAB_WIDTH) + EvoConvert.TAB_WIDTH;
+                               else
+                                       worker.lineLetters++;
+
+                               if (chr == EvoConvert.NOWRAP_CHAR_START || chr == EvoConvert.NOWRAP_CHAR_END)
+                                       worker.ignoreLineLetters++;
+
+                               if (chr == EvoConvert.NOWRAP_CHAR_END && worker.inAnchor)
+                                       worker.inAnchor--;
+
+                               if (worker.shouldWrap(str[ii + 1]))
+                                       worker.commit(ii, false);
+                       }
+               }
+
+               while (worker.line.length || worker.spacesFrom != -1 || !lines.length) {
+                       worker.commit(ii, false);
+               }
+       } else {
+               if (str.endsWith("\n"))
+                       str = str.substr(0, str.length - 1);
+
+               lines = str.split("\n");
+       }
+
+       var extraIndentStr = extraIndent > 0 ? " ".repeat(extraIndent) : null;
+
+       if (wrapWidth <= 0) {
+               for (ii = 0; ii < lines.length; ii++) {
+                       var len = EvoConvert.calcLineLetters(lines[ii]);
+
+                       if (wrapWidth < len)
+                               wrapWidth = len;
+               }
+       }
+
+       str = "";
+
+       for (ii = 0; ii < lines.length; ii++) {
+               var line = lines[ii];
+
+               if ((!ltr && align == EvoConvert.ALIGN_LEFT) ||
+                   (ltr && align == EvoConvert.ALIGN_RIGHT)) {
+                       var len = EvoConvert.calcLineLetters(line);
+
+                       if (len < wrapWidth) {
+                               var add = " ".repeat(wrapWidth - len);
+
+                               if (ltr)
+                                       line = add + line;
+                               else
+                                       line = line + add;
+                       }
+               } else if (align == EvoConvert.ALIGN_CENTER) {
+                       var len = EvoConvert.calcLineLetters(line);
+
+                       if (len < wrapWidth) {
+                               var add = " ".repeat((wrapWidth - len) / 2);
+
+                               if (ltr)
+                                       line = add + line;
+                               else
+                                       line = line + add;
+                       }
+               } else if (align == EvoConvert.ALIGN_JUSTIFY && ii + 1 < lines.length) {
+                       var len = EvoConvert.calcLineLetters(line);
+
+                       if (len < wrapWidth) {
+                               var words = line.split(" ");
+
+                               if (words.length > 1) {
+                                       var nAdd = (wrapWidth - len);
+                                       var add = " ".repeat(nAdd / (words.length - 1) >= 1 ? nAdd / 
(words.length - 1) : nAdd), jj;
+
+                                       for (jj = 0; jj < words.length - 1 && nAdd > 0; jj++) {
+                                               words[jj] = words[jj] + add;
+                                               nAdd -= add.length;
+
+                                               if (nAdd > 0 && jj + 2 >= words.length) {
+                                                       words[jj] = " ".repeat(nAdd) + words[jj];
+                                               }
+                                       }
+
+                                       line = words[0];
+
+                                       for (jj = 1; jj < words.length; jj++) {
+                                               line = line + " " + words[jj];
+                                       }
+                               }
+                       }
+               }
+
+               if (!ii && liText) {
+                       if (ltr)
+                               line = liText + line;
+                       else
+                               line = line + liText;
+               } else if (extraIndentStr && ii > 0) {
+                       if (ltr)
+                               line = extraIndentStr + line;
+                       else
+                               line = line + extraIndentStr;
+
+               }
+
+               if (indent != "") {
+                       if (ltr && align != EvoConvert.ALIGN_RIGHT)
+                               line = indent + line;
+                       else
+                               line = line + indent;
+               }
+
+               if (quoteLevel > 0) {
+                       if (ltr)
+                               line = EvoConvert.getQuotePrefix(quoteLevel, ltr) + line;
+                       else
+                               line = line + EvoConvert.getQuotePrefix(quoteLevel, ltr);
+               }
+
+               str += line + "\n";
+       }
+
+       return str;
+}
+
+EvoConvert.ImgToText = function(img)
+{
+       if (!img)
+               return "";
+
+       var txt;
+
+       txt = img.alt;
+
+       return txt ? txt : "";
+}
+
+EvoConvert.extractElemText = function(elem, normalDivWidth, quoteLevel)
+{
+       if (!elem)
+               return "";
+
+       if (!elem.childNodes.length)
+               return elem.innerText;
+
+       var str = "", ii;
+
+       for (ii = 0; ii < elem.childNodes.length; ii++) {
+               var node = elem.childNodes.item(ii);
+
+               if (!node)
+                       continue;
+
+               str += EvoConvert.processNode(node, normalDivWidth, quoteLevel);
+       }
+
+       return str;
+}
+
+EvoConvert.mergeConsecutiveSpaces = function(str)
+{
+       if (str.indexOf("  ") >= 0) {
+               var words = str.split(" "), ii, word;
+
+               str = "";
+
+               for (ii = 0; ii < words.length; ii++) {
+                       word = words[ii];
+
+                       if (word) {
+                               if (ii)
+                                       str += " ";
+
+                               str += word;
+                       }
+               }
+
+               if (!words[words.length - 1])
+                       str += " ";
+       }
+
+       return str;
+}
+
+EvoConvert.processNode = function(node, normalDivWidth, quoteLevel)
+{
+       var str = "";
+
+       if (node.nodeType == node.TEXT_NODE) {
+               str = node.nodeValue;
+
+               if (str.indexOf("\r") >= 0 ||
+                   str.indexOf("\n") >= 0 ||
+                   str.indexOf("\t") >= 0 ||
+                   str.indexOf(" ") >= 0) {
+                       var whiteSpace = "normal";
+
+                       if (node.parentElement)
+                               whiteSpace = window.getComputedStyle(node.parentElement).whiteSpace;
+
+                       if (whiteSpace == "pre-line") {
+                               str = EvoConvert.mergeConsecutiveSpaces(str.replace(/\t/g, " "));
+                       } else if (!whiteSpace || whiteSpace == "normal" || whiteSpace == "nowrap") {
+                               if (str == "\n" || str == "\r" || str == "\r\n") {
+                                       str = "";
+                               } else {
+                                       if ((!node.previousSibling || node.previousSibling.tagName) && 
(str[0] == '\r' || str[0] == '\n')) {
+                                               var ii;
+
+                                               for (ii = 0; str[ii] == '\n' || str[ii] == '\r'; ii++) {
+                                                       // do nothing, just skip all leading insignificant 
new lines
+                                               }
+
+                                               str = str.substr(ii);
+                                       }
+
+                                       if (str.length > 0 && (!node.nextSibling || node.nextSibling.tagName) 
&& (str[str.length - 1] == '\r' || str[str.length - 1] == '\n')) {
+                                               var ii;
+
+                                               for (ii = str.length - 1; ii >= 0 && (str[ii] == '\n' || 
str[ii] == '\r'); ii--) {
+                                                       // do nothing, just skip all trailing insignificant 
new lines
+                                               }
+
+                                               str = str.substr(0, ii + 1);
+                                       }
+
+                                       str = EvoConvert.mergeConsecutiveSpaces(str.replace(/\t/g, " 
").replace(/\r/g, " ").replace(/\n/g, " "));
+                               }
+                       }
+               }
+       } else if (node.nodeType == node.ELEMENT_NODE) {
+               if (node.hidden ||
+                   node.tagName == "STYLE" ||
+                   node.tagName == "META" ||
+                   (node.tagName == "SPAN" && node.classList.contains("-x-evo-quoted")))
+                       return str;
+
+               var style = EvoConvert.getComputedOrNodeStyle(node), ltr, align, indent, whiteSpace;
+
+               if (!style)
+                       style = node.style;
+
+               ltr = !style || style.direction != "rtl";
+
+               align = style ? style.textAlign : "";
+               if (!align || align == "")
+                       align = node.style.textAlign;
+               if (align)
+                       align = align.toLowerCase();
+               if (!align)
+                       align = "";
+               if (align == "" || align == "start")
+                       align = ltr ? "left" : "right";
+
+               if (align == "center")
+                       align = EvoConvert.ALIGN_CENTER;
+               else if (align == "right")
+                       align = EvoConvert.ALIGN_RIGHT;
+               else if (align == "justify")
+                       align = EvoConvert.ALIGN_JUSTIFY;
+               else
+                       align = EvoConvert.ALIGN_LEFT;
+
+               // mixed indent and opposite text align does nothing
+               if ((ltr && align == EvoConvert.ALIGN_RIGHT) ||
+                   (!ltr && align == EvoConvert.ALIGN_LEFT)) {
+                       indent = "";
+               } else {
+                       // computed style's 'indent' uses pixels, not characters
+                       indent = ltr ? node.style.marginLeft : node.style.marginRight;
+               }
+
+               if (indent && indent.endsWith("ch")) {
+                       indent = parseInt(indent.slice(0, -2));
+                       if (!Number.isInteger(indent) || indent < 0)
+                               indent = 0;
+               } else {
+                       indent = 0;
+               }
+
+               if (indent > 0)
+                       indent = " ".repeat(indent);
+               else
+                       indent = "";
+
+               whiteSpace = style ? style.whiteSpace.toLowerCase() : "";
+
+               if (node.tagName == "DIV" || node.tagName == "P") {
+                       var liText, extraIndent, width, useDefaultWidth = false;
+
+                       liText = node.getAttribute("x-evo-li-text");
+                       if (!liText)
+                               liText = "";
+
+                       extraIndent = node.getAttribute("x-evo-extra-indent");
+                       extraIndent = extraIndent ? parseInt(extraIndent, 10) : 0;
+                       if (!Number.isInteger(extraIndent)) {
+                               extraIndent = 0;
+                       }
+
+                       width = node.style.width;
+                       if (width && width.endsWith("ch")) {
+                               width = parseInt(width.slice(0, -2));
+
+                               if (!Number.isInteger(width) || width < 0)
+                                       useDefaultWidth = true;
+                       } else {
+                               useDefaultWidth = true;
+                       }
+
+                       if (useDefaultWidth && normalDivWidth > 0) {
+                               width = normalDivWidth - (quoteLevel * 2);
+
+                               if (width < EvoConvert.MIN_PARAGRAPH_WIDTH)
+                                       width = EvoConvert.MIN_PARAGRAPH_WIDTH;
+                       }
+
+                       str = EvoConvert.formatParagraph(EvoConvert.extractElemText(node, normalDivWidth, 
quoteLevel), ltr, align, indent, whiteSpace, width, extraIndent, liText, quoteLevel);
+               } else if (node.tagName == "PRE") {
+                       str = EvoConvert.formatParagraph(EvoConvert.extractElemText(node, normalDivWidth, 
quoteLevel), ltr, align, indent, "pre", -1, 0, "", quoteLevel);
+               } else if (node.tagName == "BR") {
+                       // ignore new-lines added by wrapping, treat them as spaces
+                       if (node.classList.contains("-x-evo-wrap-br"))
+                               str += " ";
+                       else
+                               str = "\n";
+               } else if (node.tagName == "IMG") {
+                       str = EvoConvert.ImgToText(node);
+               } else if (node.tagName == "A" && !node.innerText.includes(" ") && 
!node.innerText.includes("\n")) {
+                       str = EvoConvert.NOWRAP_CHAR_START + node.innerText + EvoConvert.NOWRAP_CHAR_END;
+               } else {
+                       var isBlockquote = node.tagName == "BLOCKQUOTE";
+
+                       str = EvoConvert.extractElemText(node, normalDivWidth, quoteLevel + (isBlockquote ? 1 
: 0));
+
+                       if ((!isBlockquote || !str.endsWith("\n")) &&
+                           str != "\n" && ((style && style.display == "block") || node.tagName == 
"ADDRESS")) {
+                               str += "\n";
+                       }
+               }
+       }
+
+       return str;
+}
+
+/*
+ * Converts element and its children to plain text. Any <div>,<ul>,<ol>, as an immediate child
+ * of the element, is wrapped to upto normalDivWidth characters, if it's defined and greater
+ * than EvoConvert.MIN_PARAGRAPH_WIDTH.
+ */
+EvoConvert.ToPlainText = function(element, normalDivWidth)
 {
        if (!element)
                return null;
 
-       return element.innerText;
+       if (element.tagName == "HTML") {
+               var bodys;
+
+               bodys = element.getElementsByTagName("BODY");
+
+               if (bodys.length == 1)
+                       element = bodys.item(0);
+       }
+
+       if (!element)
+               return null;
+
+       if (!normalDivWidth)
+               normalDivWidth = -1;
+
+       var disconnectFromHead = false;
+
+       if (!element.isConnected) {
+               // this is needed to be able to use window.getComputedStyle()
+               document.head.appendChild(element);
+               disconnectFromHead = true;
+       }
+
+       try {
+               var uls, ols, str = "", ii;
+
+               uls = element.getElementsByTagName("UL");
+               ols = element.getElementsByTagName("OL");
+
+               if (uls.length > 0 || ols.length > 0) {
+                       element = element.cloneNode(true);
+
+                       if (uls.length)
+                               EvoConvert.replaceList(element, "UL");
+
+                       if (ols.length)
+                               EvoConvert.replaceList(element, "OL");
+               }
+
+               for (ii = 0; ii < element.childNodes.length; ii++) {
+                       var node = element.childNodes.item(ii);
+
+                       if (!node)
+                               continue;
+
+                       str += EvoConvert.processNode(node, normalDivWidth, 0);
+               }
+       } finally {
+               if (disconnectFromHead)
+                       document.head.removeChild(element);
+       }
+
+       // remove EvoConvert.NOWRAP_CHAR_START and EvoConvert.NOWRAP_CHAR_END from the result
+       return str.replace(/\x01/g, "").replace(/\x02/g, "");
 }
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
new file mode 100644
index 0000000000..9054f8f1c5
--- /dev/null
+++ b/data/webkit/e-editor.js
@@ -0,0 +1,5840 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2019 Red Hat (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/>.
+ */
+
+'use strict';
+
+/* semi-convention: private functions start with lower-case letter,
+   public functions start with upper-case letter. */
+
+var EvoEditor = {
+       CURRENT_ELEMENT_ATTR : "x-evo-dialog-current-element",
+       BLOCKQUOTE_STYLE : "margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex",
+
+       E_CONTENT_EDITOR_ALIGNMENT_NONE         : -1,
+       E_CONTENT_EDITOR_ALIGNMENT_LEFT         : 0,
+       E_CONTENT_EDITOR_ALIGNMENT_CENTER       : 1,
+       E_CONTENT_EDITOR_ALIGNMENT_RIGHT        : 2,
+       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY      : 3,
+
+       E_CONTENT_EDITOR_BLOCK_FORMAT_NONE      : 0,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH : 1,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PRE       : 2,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS   : 3,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H1        : 4,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H2        : 5,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H3        : 6,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H4        : 7,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H5        : 8,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H6        : 9,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST : 10,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST : 11,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN : 12,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA : 13,
+
+       E_CONTENT_EDITOR_GET_INLINE_IMAGES      : 1 << 0,
+       E_CONTENT_EDITOR_GET_RAW_BODY_HTML      : 1 << 1,
+       E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN     : 1 << 2,
+       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED  : 1 << 3,
+       E_CONTENT_EDITOR_GET_RAW_DRAFT          : 1 << 4,
+       E_CONTENT_EDITOR_GET_TO_SEND_HTML       : 1 << 5,
+       E_CONTENT_EDITOR_GET_TO_SEND_PLAIN      : 1 << 6,
+
+       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,
+
+       E_CONTENT_EDITOR_SCOPE_CELL             : 0,
+       E_CONTENT_EDITOR_SCOPE_ROW              : 1,
+       E_CONTENT_EDITOR_SCOPE_COLUMN           : 2,
+       E_CONTENT_EDITOR_SCOPE_TABLE            : 3,
+
+       /* Flags for ClaimAffectedContent() */
+       CLAIM_CONTENT_FLAG_NONE : 0,
+       CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE : 1 << 0,
+       CLAIM_CONTENT_FLAG_SAVE_HTML : 1 << 1,
+
+       TEXT_INDENT_SIZE : 3, // in characters
+       NORMAL_PARAGRAPH_WIDTH : 71,
+       MAGIC_LINKS : true,
+       MAGIC_SMILEYS : false,
+       UNICODE_SMILEYS : false,
+       WRAP_QUOTED_TEXT_IN_REPLIES : true,
+       START_BOTTOM : false,
+       TOP_SIGNATURE : false,
+
+       FORCE_NO : 0,
+       FORCE_YES : 1,
+       FORCE_MAYBE : 2,
+
+       MODE_PLAIN_TEXT : 0,
+       MODE_HTML : 1,
+
+       mode : 1, // one of the MODE constants
+       storedSelection : null,
+       propertiesSelection : null, // dedicated to Properties dialogs
+       contextMenuNode : null, // the last target node for context menu
+       inheritThemeColors : false,
+       checkInheritFontsOnChange : false,
+       forceFormatStateUpdate : false,
+       formattingState : {
+               mode : -1,
+               anchorElement : null, // to avoid often notifications when just moving within the same node
+               bold : false,
+               italic : false,
+               underline : false,
+               strikethrough : false,
+               script : 0, // -1..subscript, 0..normal, +1..superscript
+               blockFormat : -1,
+               alignment : -1,
+               fgColor : null,
+               bgColor : null,
+               fontSize : null,
+               fontFamily : null,
+               indentLevel : 0,
+               bodyFgColor : null,
+               bodyBgColor : null,
+               bodyLinkColor : null,
+               bodyVlinkColor : null,
+               bodyFontFamily : null
+       }
+};
+
+EvoEditor.maybeUpdateFormattingState = function(force)
+{
+       var anchorElem = null;
+
+       if (!document.getSelection().isCollapsed) {
+               var commonParent;
+
+               commonParent = EvoEditor.GetCommonParent(document.getSelection().anchorNode, 
document.getSelection().focusNode, true);
+               if (commonParent) {
+                       var child1, child2;
+
+                       child1 = EvoEditor.GetDirectChild(commonParent, document.getSelection().anchorNode);
+                       child2 = EvoEditor.GetDirectChild(commonParent, document.getSelection().focusNode);
+
+                       if (child1 && (!child2 || (child2 && EvoEditor.GetChildIndex(commonParent, child1) <= 
EvoEditor.GetChildIndex(commonParent, child2)))) {
+                               anchorElem = document.getSelection().focusNode;
+                       }
+               }
+       }
+
+       if (!anchorElem)
+               anchorElem = document.getSelection().anchorNode;
+       if (!anchorElem)
+               anchorElem = document.body ? document.body.firstElementChild : null;
+
+       if (anchorElem && anchorElem.nodeType == anchorElem.TEXT_NODE)
+               anchorElem = anchorElem.parentElement;
+
+       if (force == EvoEditor.FORCE_NO && EvoEditor.formattingState.anchorElement === anchorElem && 
EvoEditor.mode == EvoEditor.formattingState.mode) {
+               return;
+       }
+
+       force = force == EvoEditor.FORCE_YES;
+
+       EvoEditor.formattingState.anchorElement = anchorElem;
+
+       var changes = {}, nchanges = 0, value, tmp;
+
+       value = EvoEditor.mode;
+       if (value != EvoEditor.formattingState.mode) {
+               EvoEditor.formattingState.mode = value;
+               changes["mode"] = value;
+               nchanges++;
+       }
+
+       value = document.body ? document.body.style.fontFamily : "";
+       if (force || value != EvoEditor.formattingState.bodyFontFamily) {
+               EvoEditor.formattingState.bodyFontFamily = value;
+               changes["bodyFontFamily"] = value;
+               nchanges++;
+       }
+
+       value = document.body ? document.body.text : "";
+       if (force || value != EvoEditor.formattingState.bodyFgColor) {
+               EvoEditor.formattingState.bodyFgColor = value;
+               changes["bodyFgColor"] = value;
+               nchanges++;
+       }
+
+       value = document.body.bgColor;
+       if (force || value != EvoEditor.formattingState.bodyBgColor) {
+               EvoEditor.formattingState.bodyBgColor = value;
+               changes["bodyBgColor"] = value;
+               nchanges++;
+       }
+
+       value = document.body.link;
+       if (force || value != EvoEditor.formattingState.bodyLinkColor) {
+               EvoEditor.formattingState.bodyLinkColor = value;
+               changes["bodyLinkColor"] = value;
+               nchanges++;
+       }
+
+       value = document.body.vLink;
+       if (force || value != EvoEditor.formattingState.bodyVlinkColor) {
+               EvoEditor.formattingState.bodyVlinkColor = value;
+               changes["bodyVlinkColor"] = value;
+               nchanges++;
+       }
+
+       var parent, obj = {
+               script : 0,
+               blockFormat : null,
+               indentLevel : 0,
+               fontFamily : null,
+               fontSize : null,
+               fgColor : null,
+               bgColor : null,
+               bold : null,
+               italic : null,
+               underline : null,
+               strikethrough : null,
+               textAlign : null
+       };
+
+       for (parent = anchorElem; parent && !(parent === document.body); parent = parent.parentElement) {
+               if (obj.script == 0) {
+                       if (parent.tagName == "SUB")
+                               obj.script = -1;
+                       else if (parent.tagName == "SUP")
+                               obj.script = +1;
+               }
+
+               if (obj.blockFormat === null) {
+                       if (parent.tagName == "DIV")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH;
+                       else if (parent.tagName == "PRE")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_PRE;
+                       else if (parent.tagName == "ADDRESS")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS;
+                       else if (parent.tagName == "H1")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H1;
+                       else if (parent.tagName == "H2")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H2;
+                       else if (parent.tagName == "H3")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H3;
+                       else if (parent.tagName == "H4")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H4;
+                       else if (parent.tagName == "H5")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H5;
+                       else if (parent.tagName == "H6")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H6;
+                       else if (parent.tagName == "UL")
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST;
+                       else if (parent.tagName == "OL") {
+                               obj.blockFormat = EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST;
+
+                               var typeAttr = parent.getAttribute("type");
+
+                               if (typeAttr && typeAttr.toUpperCase() == "I")
+                                       obj.blockFormat = 
EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN;
+                               else if (typeAttr && typeAttr.toUpperCase() == "A")
+                                       obj.blockFormat = 
EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA;
+                       }
+               }
+
+               if ((obj.fontSize === null || obj.fontFamily === null || obj.fgColor === null) &&
+                   parent.tagName == "FONT") {
+                       if (obj.fontSize === null && parent.hasAttribute("size")) {
+                               value = parent.getAttribute("size");
+                               value = value ? parseInt(value, 10) : 0;
+                               if (Number.isInteger(value) && value >= 1 && value <= 7) {
+                                       obj.fontSize = value;
+                               }
+                       }
+
+                       if (obj.fontFamily === null && parent.hasAttribute("face"))
+                               obj.fontFamily = parent.getAttribute("face");
+
+                       if (obj.fgColor === null && parent.hasAttribute("color"))
+                               obj.fgColor = parent.getAttribute("color");
+               }
+
+               var dir = window.getComputedStyle(parent).direction;
+
+               if (dir == "rtl") {
+                       tmp = parent.style.marginRight;
+                       if (tmp && tmp.endsWith("ch")) {
+                               tmp = parseInt(tmp.slice(0, -2));
+                       } else {
+                               tmp = "";
+                       }
+               } else { // "ltr" or other
+                       tmp = parent.style.marginLeft;
+                       if (tmp && tmp.endsWith("ch")) {
+                               tmp = parseInt(tmp.slice(0, -2));
+                       } else {
+                               tmp = "";
+                       }
+               }
+
+               if (Number.isInteger(tmp) && tmp > 0) {
+                       obj.indentLevel += tmp / EvoEditor.TEXT_INDENT_SIZE;
+               }
+
+               if (parent.tagName == "UL" || parent.tagName == "OL")
+                       obj.indentLevel++;
+
+               if (obj.bgColor === null && parent.style.backgroundColor)
+                       obj.bgColor = parent.style.backgroundColor;
+
+               if (obj.bold === null && parent.tagName == "B")
+                       obj.bold = true;
+
+               if (obj.italic === null && parent.tagName == "I")
+                       obj.italic = true;
+
+               if (obj.underline === null && parent.tagName == "U")
+                       obj.underline = true;
+
+               if (obj.strikethrough === null && (parent.tagName == "S" || parent.tagName == "STRIKE"))
+                       obj.strikethrough = true;
+
+               if (obj.textAlign === null && parent.style.textAlign)
+                       obj.textAlign = parent.style.textAlign;
+       }
+
+       value = obj.script;
+       if (force || value != EvoEditor.formattingState.script) {
+               EvoEditor.formattingState.script = value;
+               changes["script"] = value;
+               nchanges++;
+       }
+
+       value = obj.blockFormat === null ? EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH : 
obj.blockFormat;
+       if (force || value != EvoEditor.formattingState.blockFormat) {
+               EvoEditor.formattingState.blockFormat = value;
+               changes["blockFormat"] = value;
+               nchanges++;
+       }
+
+       value = obj.fontSize === null ? 3 /* E_CONTENT_EDITOR_FONT_SIZE_NORMAL */ : obj.fontSize;
+       if (force || value != EvoEditor.formattingState.fontSize) {
+               EvoEditor.formattingState.fontSize = value;
+               changes["fontSize"] = value;
+               nchanges++;
+       }
+
+       value = obj.indentLevel;
+       if (force || value != EvoEditor.formattingState.indentLevel) {
+               EvoEditor.formattingState.indentLevel = value;
+               changes["indentLevel"] = value;
+               nchanges++;
+       }
+
+       value = obj.fgColor ? obj.fgColor : "";
+       if (force || value != EvoEditor.formattingState.fgColor) {
+               EvoEditor.formattingState.fgColor = value;
+               changes["fgColor"] = value;
+               nchanges++;
+       }
+
+       value = obj.bgColor ? obj.bgColor : "";
+       if (force || value != EvoEditor.formattingState.bgColor) {
+               EvoEditor.formattingState.bgColor = value;
+               changes["bgColor"] = value;
+               nchanges++;
+       }
+
+       value = obj.bold ? true : false;
+       if (value != EvoEditor.formattingState.bold) {
+               EvoEditor.formattingState.bold = value;
+               changes["bold"] = value;
+               nchanges++;
+       }
+
+       value = obj.italic ? true : false;
+       if (force || value != EvoEditor.formattingState.italic) {
+               EvoEditor.formattingState.italic = value;
+               changes["italic"] = value;
+               nchanges++;
+       }
+
+       value = obj.underline ? true : false;
+       if (force || value != EvoEditor.formattingState.underline) {
+               EvoEditor.formattingState.underline = value;
+               changes["underline"] = value;
+               nchanges++;
+       }
+
+       value = obj.strikethrough ? true : false;
+       if (force || value != EvoEditor.formattingState.strikethrough) {
+               EvoEditor.formattingState.strikethrough = value;
+               changes["strikethrough"] = value;
+               nchanges++;
+       }
+
+       value = obj.fontFamily ? obj.fontFamily : "";
+       // dequote the font name, if needed
+       if (value.length > 1 && value.charAt(0) == '\"' && value.charAt(value.length - 1) == '\"')
+               value = value.substr(1, value.length - 2);
+       if (force || value != EvoEditor.formattingState.fontFamily) {
+               EvoEditor.formattingState.fontFamily = value;
+               changes["fontFamily"] = (!document.body || window.getComputedStyle(document.body).fontFamily 
== value) ? "" : value;
+               nchanges++;
+       }
+
+       tmp = (obj.textAlign ? obj.textAlign : "").toLowerCase();
+       if (tmp == "left" || tmp == "start")
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+       else if (tmp == "right")
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       else if (tmp == "center")
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_CENTER;
+       else if (tmp == "justify")
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY;
+       else if ((anchorElem ? window.getComputedStyle(anchorElem).direction : "").toLowerCase() == "rtl")
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_RIGHT;
+       else
+               value = EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_LEFT;
+
+       if (force || value != EvoEditor.formattingState.alignment) {
+               EvoEditor.formattingState.alignment = value;
+               changes["alignment"] = value;
+               nchanges++;
+       }
+
+       if (force) {
+               changes["forced"] = true;
+               nchanges++;
+       }
+
+       if (nchanges > 0)
+               window.webkit.messageHandlers.formattingChanged.postMessage(changes);
+}
+
+EvoEditor.IsBlockNode = function(node)
+{
+       if (!node || !node.tagName) {
+               return false;
+       }
+
+       return node.tagName == "BLOCKQUOTE" ||
+               node.tagName == "DIV" ||
+               node.tagName == "P" ||
+               node.tagName == "PRE" ||
+               node.tagName == "ADDRESS" ||
+               node.tagName == "H1" ||
+               node.tagName == "H2" ||
+               node.tagName == "H3" ||
+               node.tagName == "H4" ||
+               node.tagName == "H5" ||
+               node.tagName == "H6" ||
+               node.tagName == "TD" ||
+               node.tagName == "TH" ||
+               node.tagName == "UL" ||
+               node.tagName == "OL";
+}
+
+EvoEditor.foreachChildRecur = function(topParent, parent, firstChildIndex, lastChildIndex, traversar)
+{
+       if (!parent) {
+               return false;
+       }
+
+       if (firstChildIndex >= parent.children.length) {
+               return true;
+       }
+
+       var ii, child, next;
+
+       ii = lastChildIndex - firstChildIndex;
+       child = parent.children.item(firstChildIndex);
+
+       while (child && ii >= 0) {
+               next = child.nextElementSibling;
+
+               if (child.children.length > 0 &&
+                   !traversar.flat &&
+                   !EvoEditor.foreachChildRecur(topParent, child, 0, child.children.length - 1, traversar)) {
+                       return false;
+               }
+
+               if (!traversar.onlyBlockElements || EvoEditor.IsBlockNode(child)) {
+                       if (!traversar.exec(topParent, child)) {
+                               return false;
+                       }
+               }
+
+               child = next;
+               ii--;
+       }
+
+       return true;
+}
+
+/*
+   Traverses children of the 'parent', between the 'firstChildIndex' and
+   the 'lastChildIndex', where both indexes are meant inclusive.
+
+   The 'traversar' is an object, which should contain at least function:
+
+      bool exec(parent, element);
+
+   which does its work in the 'element' and returns true, when the traversar
+   should continue. The 'parent' is the one with which the funcion had been
+   called with. The 'traversar' can also contain properties:
+
+      bool flat;
+      bool onlyBlockElements;
+
+   the 'flat', if set to true, traverses only direct children of the parent,
+   otherwise it dives into the hierarchy;
+
+   the 'onlyBlockElements', if set to true, calls exec() only on elements,
+   which are block elements (as of EvoEditor.IsBlockNode()), otherwise it
+   is called for each element on the way.
+*/
+EvoEditor.ForeachChild = function(parent, firstChildIndex, lastChildIndex, traversar)
+{
+       return EvoEditor.foreachChildRecur(parent, parent, firstChildIndex, lastChildIndex, traversar);
+}
+
+EvoEditor.GetParentBlockNode = function(node)
+{
+       while (node && !EvoEditor.IsBlockNode(node) && node.tagName != "BODY") {
+               node = node.parentElement;
+       }
+
+       return node;
+}
+
+EvoEditor.GetCommonParent = function(firstNode, secondNode, longPath)
+{
+       if (!firstNode || !secondNode) {
+               return null;
+       }
+
+       if (firstNode.nodeType == firstNode.TEXT_NODE) {
+               firstNode = firstNode.parentElement;
+       }
+
+       if (secondNode.nodeType == secondNode.TEXT_NODE) {
+               secondNode = secondNode.parentElement;
+       }
+
+       if (!firstNode || !secondNode) {
+               return null;
+       }
+
+       if (firstNode === document.body || secondNode === document.body) {
+               return document.body;
+       }
+
+       var commonParent, secondParent;
+
+       for (commonParent = (longPath ? firstNode : firstNode.parentElement); commonParent; commonParent = 
commonParent.parentElement) {
+               if (commonParent === document.body) {
+                       break;
+               }
+
+               for (secondParent = (longPath ? secondNode : secondNode.parentElement); secondParent; 
secondParent = secondParent.parentElement) {
+                       if (secondParent === document.body) {
+                               break;
+                       }
+
+                       if (secondParent === commonParent) {
+                               return commonParent;
+                       }
+               }
+       }
+
+       return document.body;
+}
+
+EvoEditor.GetDirectChild = function(parent, child)
+{
+       if (!parent || !child || parent === child) {
+               return null;
+       }
+
+       while (child && !(child.parentElement === parent)) {
+               child = child.parentElement;
+       }
+
+       return child;
+}
+
+EvoEditor.GetChildIndex = function(parent, child)
+{
+       if (!parent || !child)
+               return -1;
+
+       var ii;
+
+       for (ii = 0; ii < parent.children.length; ii++) {
+               if (child === parent.children.item(ii))
+                       return ii;
+       }
+
+       return -1;
+}
+
+EvoEditor.ClaimAffectedContent = function(startNode, endNode, flags)
+{
+       var commonParent, startChild, endChild;
+       var firstChildIndex = -1, html = "", ii;
+       var withHtml = (flags & EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML) != 0;
+       var currentElemsArray = null;
+
+       if (!startNode) {
+               startNode = document.getSelection().anchorNode;
+               endNode = document.getSelection().focusNode;
+
+               if (!startNode) {
+                       startNode = document.body;
+               }
+       }
+
+       if (!endNode) {
+               endNode = document.getSelection().focusNode;
+
+               if (!endNode)
+                       endNode = startNode;
+       }
+
+       if ((flags & EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE) != 0) {
+               while (startNode && !(startNode === document.body)) {
+                       if (EvoEditor.IsBlockNode(startNode)) {
+                               break;
+                       }
+
+                       startNode = startNode.parentElement;
+               }
+       }
+
+       if (withHtml) {
+               var node = startNode;
+
+               // cannot store only part of the HTML in a TABLE, only whole, because restoring
+               // for example only "<td>text</td>" into a floating element drops the <td/>
+               if (!node.tagName)
+                       node = node.parentElement;
+
+               if (node.tagName == "TH" || node.tagName == "TD" || node.tagName == "TR") {
+                       node = EvoEditor.getParentElement("TABLE", node, true);
+                       if (node)
+                               startNode = node;
+               }
+
+               currentElemsArray = EvoEditor.RemoveCurrentElementAttr();
+       }
+
+       commonParent = EvoEditor.GetCommonParent(startNode, endNode, false);
+       startChild = EvoEditor.GetDirectChild(commonParent, startNode);
+       endChild = EvoEditor.GetDirectChild(commonParent, endNode);
+
+       for (ii = 0 ; ii < commonParent.children.length; ii++) {
+               var child = commonParent.children.item(ii);
+
+               if (firstChildIndex == -1) {
+                       /* The selection can be made both from the top to the bottom and
+                          from the bottom to the top, thus cover both cases. */
+                       if (child === startChild) {
+                               firstChildIndex = ii;
+                       } else if (child === endChild) {
+                               endChild = startChild;
+                               startChild = child;
+                               firstChildIndex = ii;
+                       }
+               }
+
+               if (firstChildIndex != -1) {
+                       if (withHtml) {
+                               html += child.outerHTML;
+                       }
+
+                       if (child === endChild) {
+                               ii++;
+                               break;
+                       }
+               }
+       }
+
+       var affected = {};
+
+       affected.path = EvoSelection.GetChildPath(document.body, commonParent);
+       affected.firstChildIndex = firstChildIndex;
+       affected.restChildrenCount = commonParent.children.length - ii;
+
+       if (withHtml) {
+               if (firstChildIndex == -1)
+                       affected.html = commonParent.innerHTML;
+               else
+                       affected.html = html;
+
+               EvoEditor.RestoreCurrentElementAttr(currentElemsArray);
+       }
+
+       return affected;
+}
+
+/* Calls EvoEditor.ForeachChild() on a content described by 'affected',
+   which is result of EvoEditor.ClaimAffectedContent(). */
+EvoEditor.ForeachChildInAffectedContent = function(affected, traversar)
+{
+       if (!affected || !traversar) {
+               throw "EvoEditor.ForeachChildInAffectedContent: No 'affected' or 'traversar'";
+       }
+
+       var parent, firstChildIndex, lastChildIndex;
+
+       parent = EvoSelection.FindElementByPath(document.body, affected.path);
+       if (!parent) {
+               throw "EvoEditor.ForeachChildInAffectedContent: Cannot find parent";
+       }
+
+       firstChildIndex = affected.firstChildIndex;
+       /* Cannot subtract one, when none left, because the child index is inclusive */
+       lastChildIndex = parent.children.length - affected.restChildrenCount + (affected.restChildrenCount ? 
-1 : 0);
+
+       return EvoEditor.ForeachChild(parent, firstChildIndex, lastChildIndex, traversar);
+}
+
+EvoEditor.EmitContentChanged = function()
+{
+       if (window.webkit.messageHandlers.contentChanged)
+               window.webkit.messageHandlers.contentChanged.postMessage(null);
+}
+
+EvoEditor.StoreSelection = function()
+{
+       EvoEditor.storedSelection = EvoSelection.Store(document);
+}
+
+EvoEditor.RestoreSelection = function()
+{
+       if (EvoEditor.storedSelection) {
+               EvoSelection.Restore(document, EvoEditor.storedSelection);
+               EvoEditor.storedSelection = null;
+       }
+}
+
+EvoEditor.removeEmptyStyleAttribute = function(element)
+{
+       if (element && !element.style.length)
+               element.removeAttribute("style");
+}
+
+EvoEditor.applySetAlignment = function(record, isUndo)
+{
+       if (record.changes) {
+               var ii, parent, child;
+
+               parent = EvoSelection.FindElementByPath(document.body, record.path);
+               if (!parent) {
+                       throw "EvoEditor.applySetAlignment: Cannot find parent at path " + record.path;
+               }
+
+               for (ii = 0; ii < record.changes.length; ii++) {
+                       var change = record.changes[isUndo ? (record.changes.length - ii - 1) : ii];
+
+                       child = EvoSelection.FindElementByPath(parent, change.path);
+                       if (!child) {
+                               throw "EvoEditor.applySetAlignment: Cannot find child";
+                       }
+
+                       if (isUndo) {
+                               child.style.textAlign = change.before;
+                       } else if ((record.applyValueAfter == "left" && child.style.direction != "rtl" && 
window.getComputedStyle(child).direction != "rtl") ||
+                                  (record.applyValueAfter == "right" && (child.style.direction == "rtl" || 
window.getComputedStyle(child).direction == "rtl"))) {
+                               child.style.textAlign = "";
+                       } else {
+                               child.style.textAlign = record.applyValueAfter;
+                       }
+
+                       EvoEditor.removeEmptyStyleAttribute(child);
+               }
+       }
+}
+
+EvoEditor.SetAlignment = function(alignment)
+{
+       var traversar = {
+               record : null,
+               toSet : null,
+               anyChanged : false,
+
+               flat : false,
+               onlyBlockElements : true,
+
+               exec : function(parent, element) {
+                       if (window.getComputedStyle(element, null).textAlign != traversar.toSet) {
+                               if (traversar.record) {
+                                       if (!traversar.record.changes)
+                                               traversar.record.changes = [];
+
+                                       var change = {};
+
+                                       change.path = EvoSelection.GetChildPath(parent, element);
+                                       change.before = element.style.textAlign;
+
+                                       traversar.record.changes[traversar.record.changes.length] = change;
+                               }
+
+                               traversar.anyChanged = true;
+
+                               if ((traversar.toSet == "left" && element.style.direction != "rtl" && 
window.getComputedStyle(element).direction != "rtl") ||
+                                   (traversar.toSet == "right" && (element.style.direction == "rtl" || 
window.getComputedStyle(element).direction == "rtl"))) {
+                                       element.style.textAlign = "";
+                               } else {
+                                       element.style.textAlign = traversar.toSet;
+                               }
+
+                               EvoEditor.removeEmptyStyleAttribute(element);
+                       }
+
+                       return true;
+               }
+       };
+
+       var affected = EvoEditor.ClaimAffectedContent(null, null, 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       switch (alignment) {
+       case EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_NONE:
+               traversar.toSet = "";
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_LEFT:
+               traversar.toSet = "left";
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_CENTER:
+               traversar.toSet = "center";
+               break;
+       case  EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_RIGHT:
+               traversar.toSet = "right";
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY:
+               traversar.toSet = "justify";
+               break;
+       default:
+               throw "EvoEditor.SetAlignment: Unknown alignment value: '" + alignment + "'";
+       }
+
+       traversar.record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setAlignment", null, 
null, EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       try {
+               EvoEditor.ForeachChildInAffectedContent(affected, traversar);
+
+               if (traversar.record) {
+                       traversar.record.applyValueAfter = traversar.toSet;
+                       traversar.record.apply = EvoEditor.applySetAlignment;
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setAlignment");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+
+               if (traversar.anyChanged)
+                       EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.storeAttributes = function(element)
+{
+       if (!element || !element.attributes.length)
+               return null;
+
+       var attributes = [], ii;
+
+       for (ii = 0; ii < element.attributes.length; ii++) {
+               var attr = {
+                       name : element.attributes.item(ii).name,
+                       value : element.attributes.item(ii).value
+               };
+
+               attributes[attributes.length] = attr;
+       }
+
+       return attributes;
+}
+
+EvoEditor.restoreAttributes = function(element, attributes)
+{
+       if (!element)
+               return;
+
+       while (element.attributes.length) {
+               element.removeAttribute(element.attributes.item(element.attributes.length - 1).name);
+       }
+
+       if (!attributes)
+               return;
+
+       var ii;
+
+       for (ii = 0; ii < attributes.length; ii++) {
+               element.setAttribute(attributes[ii].name, attributes[ii].value);
+       }
+}
+
+EvoEditor.storeElement = function(element)
+{
+       if (!element)
+               return null;
+
+       var elementRecord = {
+               tagName : element.tagName,
+               attributes : EvoEditor.storeAttributes(element)
+       };
+
+       return elementRecord;
+}
+
+EvoEditor.restoreElement = function(parentElement, beforeElement, tagName, attributes)
+{
+       if (!parentElement)
+               throw "EvoEditor.restoreElement: parentElement cannot be null";
+
+       if (!tagName)
+               throw "EvoEditor.restoreElement: tagName cannot be null";
+
+       var node;
+
+       node = parentElement.ownerDocument.createElement(tagName);
+
+       EvoEditor.restoreAttributes(node, attributes);
+
+       parentElement.insertBefore(node, beforeElement);
+
+       return node;
+}
+
+EvoEditor.moveChildren = function(fromElement, toElement, beforeElement, prepareParent, selectionUpdater)
+{
+       if (!fromElement)
+               throw "EvoEditor.moveChildren: fromElement cannot be null";
+
+       if (beforeElement && toElement && !(beforeElement.parentElement === toElement))
+               throw "EvoEditor.moveChildren: beforeElement is not a direct child of toElement";
+
+       var node;
+
+       for (node = toElement; node; node = node.parentElement) {
+               if (node === fromElement)
+                       throw "EvoEditor.moveChildren: toElement cannot be child of fromElement";
+       }
+
+       var firstElement = toElement;
+
+       while (fromElement.firstChild) {
+               if (prepareParent && fromElement.firstChild.tagName && fromElement.firstChild.tagName == 
"LI") {
+                       var toParent = prepareParent.exec();
+
+                       if (toElement) {
+                               toElement.parentElement.insertBefore(toParent, toElement.nextElementSibling);
+                       }
+
+                       if (!firstElement) {
+                               firstElement = toParent;
+                       }
+
+                       var li = fromElement.firstChild, replacedBy = li.firstChild;
+
+                       while (li.firstChild) {
+                               toParent.append(li.firstChild);
+                       }
+
+                       if (selectionUpdater)
+                               selectionUpdater.beforeRemove(fromElement.firstChild);
+
+                       fromElement.removeChild(fromElement.firstChild);
+
+                       if (selectionUpdater)
+                               selectionUpdater.afterRemove(replacedBy);
+               } else {
+                       if (!toElement && prepareParent) {
+                               toElement = prepareParent.exec();
+
+                               // trying to move other than LI into UL/OL, thus do not enclose it into LI
+                               if (prepareParent.tagName == "LI" && (fromElement.tagName == "UL" || 
fromElement.tagName == "OL")) {
+                                       var toParent = toElement.parentElement;
+                                       toParent.removeChild(toElement);
+                                       toElement = toParent;
+                               }
+                       }
+
+                       if (!firstElement) {
+                               firstElement = toElement;
+                       }
+
+                       toElement.insertBefore(fromElement.firstChild, beforeElement);
+               }
+       }
+
+       return firstElement;
+}
+
+EvoEditor.renameElement = function(element, tagName, attributes, targetElement, selectionUpdater)
+{
+       var prepareParent = {
+               element : element,
+               tagName : tagName,
+               attributes : attributes,
+               targetElement : targetElement,
+
+               exec : function() {
+                       if (this.targetElement)
+                               return EvoEditor.restoreElement(this.targetElement, null, this.tagName, 
this.attributes);
+                       else
+                               return EvoEditor.restoreElement(this.element.parentElement, this.element, 
this.tagName, this.attributes);
+               }
+       };
+       var newElement;
+
+       newElement = EvoEditor.moveChildren(element, null, null, prepareParent, selectionUpdater);
+
+       element.remove();
+
+       return newElement;
+}
+
+EvoEditor.getBlockquoteLevel = function(node)
+{
+       if (!node || node.tagName == "BODY")
+               return 0;
+
+       var blockquoteLevel = 0, parent = node;
+
+       while (parent && parent.tagName != "BODY") {
+               if (parent.tagName == "BLOCKQUOTE")
+                       blockquoteLevel++;
+
+               parent = parent.parentElement;
+       }
+
+       return blockquoteLevel;
+}
+
+EvoEditor.SetBlockFormat = function(format)
+{
+       var traversar = {
+               toSet : null,
+               createParent : null,
+               firstLI : true,
+               targetElement : null,
+               selectionUpdater : null,
+
+               flat : true,
+               onlyBlockElements : true,
+
+               exec : function(parent, element) {
+                       var newElement;
+
+                       if (this.toSet.tagName != "LI" && (element.tagName == "UL" || element.tagName == 
"OL")) {
+                               var affected = [];
+
+                               if (!EvoEditor.allChildrenInSelection(element, true, affected)) {
+                                       var elemParent = element.parentElement, insBefore, jj;
+
+                                       if (affected.length > 0 && !(affected[0] === 
element.firstElementChild)) {
+                                               insBefore = EvoEditor.splitList(element, 1, affected);
+                                       } else {
+                                               insBefore = element;
+                                       }
+
+                                       for (jj = 0; jj < affected.length; jj++) {
+                                               EvoEditor.insertListChildBefore(affected[jj], 
this.toSet.tagName, insBefore ? insBefore.parentElement : elemParent, insBefore, this.selectionUpdater);
+                                       }
+
+                                       if (!element.childElementCount) {
+                                               this.selectionUpdater.beforeRemove(element);
+
+                                               element.remove();
+
+                                               this.selectionUpdater.afterRemove(insBefore ? 
insBefore.previousElementSibling : elemParent.lastElementChild);
+                                       }
+
+                                       return true;
+                               }
+                       }
+
+                       if (this.firstLI) {
+                               if (this.createParent) {
+                                       this.targetElement = EvoEditor.restoreElement(parent, element, 
this.createParent.tagName, this.createParent.attributes);
+                               }
+
+                               this.firstLI = false;
+                       }
+
+                       newElement = EvoEditor.renameElement(element, this.toSet.tagName, 
this.toSet.attributes, this.targetElement, this.selectionUpdater);
+
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && (this.toSet.tagName == "DIV" || 
this.toSet.tagName == "PRE")) {
+                               var blockquoteLevel = EvoEditor.getBlockquoteLevel(newElement);
+
+                               if (blockquoteLevel > 0) {
+                                       var width = -1;
+
+                                       if (this.toSet.tagName == "DIV" && blockquoteLevel * 2 < 
EvoEditor.NORMAL_PARAGRAPH_WIDTH)
+                                               width = EvoEditor.NORMAL_PARAGRAPH_WIDTH - blockquoteLevel * 
2;
+
+                                       EvoEditor.quoteParagraph(newElement, blockquoteLevel, width);
+                               }
+                       }
+
+                       if (this.selectionUpdater) {
+                               this.selectionUpdater.beforeRemove(element);
+                               this.selectionUpdater.afterRemove(newElement);
+                       }
+
+                       return true;
+               }
+       };
+
+       traversar.selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       var affected = EvoEditor.ClaimAffectedContent(null, null, 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       switch (format) {
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH:
+               traversar.toSet = { tagName : "DIV" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_PRE:
+               traversar.toSet = { tagName : "PRE" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS:
+               traversar.toSet = { tagName : "ADDRESS" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H1:
+               traversar.toSet = { tagName : "H1" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H2:
+               traversar.toSet = { tagName : "H2" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H3:
+               traversar.toSet = { tagName : "H3" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H4:
+               traversar.toSet = { tagName : "H4" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H5:
+               traversar.toSet = { tagName : "H5" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_H6:
+               traversar.toSet = { tagName : "H6" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST:
+               traversar.toSet = { tagName : "LI" };
+               traversar.createParent = { tagName : "UL" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST:
+               traversar.toSet = { tagName : "LI" };
+               traversar.createParent = { tagName : "OL" };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN:
+               traversar.toSet = { tagName : "LI" };
+               traversar.createParent = { tagName : "OL", attributes : [ { name : "type", value : "I" } ] };
+               break;
+       case EvoEditor.E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA:
+               traversar.toSet = { tagName : "LI" };
+               traversar.createParent = { tagName : "OL", attributes : [ { name : "type", value : "A" } ] };
+               break;
+       default:
+               throw "EvoEditor.SetBlockFormat: Unknown block format value: '" + format + "'";
+       }
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBlockFormat", null, null,
+               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+       try {
+               EvoEditor.ForeachChildInAffectedContent(affected, traversar);
+
+               traversar.selectionUpdater.restore();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBlockFormat");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.allChildrenInSelection = function(element, allowPartial, affected)
+{
+       if (!element || !element.firstChild)
+               return false;
+
+       var selection = document.getSelection(), all;
+
+       all = selection.containsNode(element.firstElementChild, allowPartial) &&
+             selection.containsNode(element.lastElementChild, allowPartial);
+
+       var node;
+
+       affected.length = 0;
+
+       for (node = element.firstElementChild; node; node = node.nextElementSibling) {
+               if (all || selection.containsNode(node, allowPartial))
+                       affected[affected.length] = node;
+       }
+
+       return all;
+}
+
+EvoEditor.splitList = function(element, nParents, onlyAffected)
+{
+       var parent, from = null;
+
+       if (onlyAffected && onlyAffected.length)
+               from = onlyAffected[onlyAffected.length - 1].nextElementSibling;
+
+       if (!from)
+               from = element.nextElementSibling;
+
+       if (nParents == -1) {
+               nParents = 0;
+
+               for (parent = from; parent && parent.tagName != "BODY"; parent = parent.parentElement) {
+                       nParents++;
+               }
+       }
+
+       var nextFrom, clone;
+
+       parent = from ? from.parentElement : element.parentElement;
+
+       if (!from && parent) {
+               from = parent.nextElementSibling;
+               nextFrom = from;
+               nParents--;
+               parent = parent.parentElement;
+       }
+
+       while (nParents > 0 && parent && parent.tagName != "HTML") {
+               nParents--;
+               nextFrom = null;
+
+               if (from) {
+                       clone = from.parentElement.cloneNode(false);
+                       from.parentElement.parentElement.insertBefore(clone, 
from.parentElement.nextElementSibling);
+
+                       nextFrom = clone;
+
+                       while (from.nextElementSibling) {
+                               clone.appendChild(from.nextElementSibling);
+                       }
+
+                       clone.insertBefore(from, clone.firstElementChild);
+               }
+
+               from = nextFrom;
+               parent = parent.parentElement;
+       }
+
+       if (nextFrom)
+               return nextFrom;
+
+       return parent.nextElementSibling;
+}
+
+EvoEditor.insertListChildBefore = function(child, tagName, parent, insBefore, selectionUpdater)
+{
+       if (child.tagName == "LI") {
+               var node = document.createElement(tagName);
+
+               while(child.firstChild)
+                       node.appendChild(child.firstChild);
+
+               parent.insertBefore(node, insBefore);
+
+               if (selectionUpdater)
+                       selectionUpdater.beforeRemove(child);
+
+               child.remove();
+
+               if (selectionUpdater)
+                       selectionUpdater.afterRemove(node);
+       } else {
+               parent.insertBefore(child, insBefore);
+       }
+}
+
+EvoEditor.applyIndent = function(record, isUndo)
+{
+       if (record.changes) {
+               var ii, parent, child;
+
+               parent = EvoSelection.FindElementByPath(document.body, record.path);
+               if (!parent) {
+                       throw "EvoEditor.applyIndent: Cannot find parent at path " + record.path;
+               }
+
+               for (ii = 0; ii < record.changes.length; ii++) {
+                       var change = record.changes[isUndo ? (record.changes.length - ii - 1) : ii];
+
+                       child = EvoSelection.FindElementByPath(change.pathIsFromBody ? document.body : 
parent, change.path);
+                       if (!child) {
+                               throw "EvoEditor.applyIndent: Cannot find child";
+                       }
+
+                       if (change.isList) {
+                               EvoUndoRedo.RestoreChildren(change, child, isUndo);
+                               continue;
+                       }
+
+                       if (isUndo) {
+                               child.style.marginLeft = change.beforeMarginLeft;
+                               child.style.marginRight = change.beforeMarginRight;
+                               child.style.width = change.beforeWidth;
+                       } else {
+                               child.style.marginLeft = change.afterMarginLeft;
+                               child.style.marginRight = change.afterMarginRight;
+                               child.style.width = change.afterWidth;
+                       }
+
+                       EvoEditor.removeEmptyStyleAttribute(child);
+               }
+       }
+}
+
+EvoEditor.Indent = function(increment)
+{
+       var traversar = {
+               record : null,
+               selectionUpdater : null,
+               increment : increment,
+
+               flat : true,
+               onlyBlockElements : true,
+
+               exec : function(parent, element) {
+                       var change = null, isList = element.tagName == "UL" || element.tagName == "OL";
+                       var isNested = isList && (element.parentElement.tagName == "UL" || 
element.parentElement.tagName == "OL");
+
+                       if (traversar.record) {
+                               if (!traversar.record.changes)
+                                       traversar.record.changes = [];
+
+                               change = {};
+
+                               change.pathIsFromBody = false;
+
+                               if (isList) {
+                                       change.isList = isList;
+                                       change.path = EvoSelection.GetChildPath(parent, element);
+                               } else {
+                                       change.path = EvoSelection.GetChildPath(parent, element);
+                                       change.beforeMarginLeft = element.style.marginLeft;
+                                       change.beforeMarginRight = element.style.marginRight;
+                                       change.beforeWidth = element.style.width;
+                               }
+
+                               traversar.record.changes[traversar.record.changes.length] = change;
+                       }
+
+                       if (isList) {
+                               var elemParent = null, all, affected = [], jj;
+
+                               all = EvoEditor.allChildrenInSelection(element, true, affected);
+
+                               if (this.increment) {
+                                       var clone;
+
+                                       clone = element.cloneNode(false);
+
+                                       if (all) {
+                                               if (change) {
+                                                       var childIndex = 
EvoEditor.GetChildIndex(element.parentElement, element);
+                                                       EvoUndoRedo.BackupChildrenBefore(change, 
element.parentElement, childIndex, childIndex);
+                                                       change.path = EvoSelection.GetChildPath(parent, 
element.parentElement);
+                                               }
+
+                                               element.parentElement.insertBefore(clone, element);
+                                               clone.appendChild(element);
+
+                                               if (change)
+                                                       EvoUndoRedo.BackupChildrenAfter(change, 
clone.parentElement);
+                                       } else if (affected.length > 0) {
+                                               if (change) {
+                                                       EvoUndoRedo.BackupChildrenBefore(change, element,
+                                                               EvoEditor.GetChildIndex(element, affected[0]),
+                                                               EvoEditor.GetChildIndex(element, 
affected[affected.length - 1]));
+                                               }
+
+                                               element.insertBefore(clone, affected[0]);
+
+                                               for (jj = 0; jj < affected.length; jj++) {
+                                                       clone.appendChild(affected[jj]);
+                                               }
+
+                                               if (change)
+                                                       EvoUndoRedo.BackupChildrenAfter(change, element);
+                                       }
+                               } else {
+                                       var insBefore = null;
+
+                                       elemParent = element.parentElement;
+
+                                       // decrease indent in nested lists of the same type will merge items 
into one list
+                                       if (isNested && elemParent.tagName == element.tagName &&
+                                           elemParent.getAttribute("type") == element.getAttribute("type")) {
+                                               if (change) {
+                                                       var childIndex = EvoEditor.GetChildIndex(elemParent, 
element);
+                                                       EvoUndoRedo.BackupChildrenBefore(change, elemParent, 
childIndex, childIndex);
+                                                       change.path = EvoSelection.GetChildPath(parent, 
elemParent);
+                                               }
+
+                                               if (!all && affected.length > 0 && !(affected[0] === 
element.firstElementChild)) {
+                                                       insBefore = EvoEditor.splitList(element, 1, affected);
+                                               } else {
+                                                       insBefore = element;
+                                               }
+
+                                               for (jj = 0; jj < affected.length; jj++) {
+                                                       elemParent.insertBefore(affected[jj], insBefore);
+                                               }
+
+                                               if (!element.childElementCount) {
+                                                       this.selectionUpdater.beforeRemove(element);
+
+                                                       element.remove();
+
+                                                       this.selectionUpdater.afterRemove(affected[0]);
+                                               }
+
+                                               if (change)
+                                                       EvoUndoRedo.BackupChildrenAfter(change, elemParent);
+                                       } else {
+                                               var tmpElement = element;
+
+                                               if (isNested) {
+                                                       tmpElement = elemParent;
+                                                       elemParent = elemParent.parentElement;
+                                               }
+
+                                               if (change) {
+                                                       var childIndex = EvoEditor.GetChildIndex(elemParent, 
tmpElement);
+                                                       EvoUndoRedo.BackupChildrenBefore(change, elemParent, 
childIndex, childIndex);
+                                                       if (isNested) {
+                                                               change.pathIsFromBody = true;
+                                                               change.path = 
EvoSelection.GetChildPath(document.body, elemParent);
+                                                       } else {
+                                                               change.path = 
EvoSelection.GetChildPath(parent, elemParent);
+                                                       }
+                                               }
+
+                                               if (isNested) {
+                                                       var clone;
+
+                                                       insBefore = EvoEditor.splitList(element, 1, affected);
+
+                                                       clone = element.cloneNode(false);
+                                                       if (insBefore)
+                                                               insBefore.parentElement.insertBefore(clone, 
insBefore);
+                                                       else
+                                                               elemParent.insertBefore(clone, insBefore);
+
+                                                       for (jj = 0; jj < affected.length; jj++) {
+                                                               clone.appendChild(affected[jj]);
+                                                       }
+                                               } else {
+                                                       if (!all && affected.length > 0 && 
affected[affected.length - 1] === element.lastElementChild) {
+                                                               insBefore = element.nextElementSibling;
+                                                       } else if (!all && affected.length > 0 && 
!(affected[0] === element.firstElementChild)) {
+                                                               insBefore = EvoEditor.splitList(element, 1, 
affected);
+                                                       } else {
+                                                               insBefore = element;
+                                                       }
+
+                                                       for (jj = 0; jj < affected.length; jj++) {
+                                                               EvoEditor.insertListChildBefore(affected[jj], 
"DIV", insBefore ? insBefore.parentElement : elemParent, insBefore, this.selectionUpdater);
+                                                       }
+                                               }
+
+                                               while (element && !(element === elemParent) && 
!element.childElementCount) {
+                                                       tmpElement = element.parentElement;
+
+                                                       this.selectionUpdater.beforeRemove(element);
+
+                                                       element.remove();
+
+                                                       this.selectionUpdater.afterRemove(insBefore ? 
insBefore.previousElementSibling : elemParent.lastElementChild);
+
+                                                       element = tmpElement;
+                                               }
+
+                                               if (change)
+                                                       EvoUndoRedo.BackupChildrenAfter(change, elemParent);
+                                       }
+                               }
+                       } else {
+                               var currValue = null, dir, width;
+
+                               dir = window.getComputedStyle(element).direction;
+
+                               if (dir == "rtl") {
+                                       if (element.style.marginRight.endsWith("ch"))
+                                               currValue = element.style.marginRight;
+                               } else { // "ltr" or other
+                                       if (element.style.marginLeft.endsWith("ch"))
+                                               currValue = element.style.marginLeft;
+                               }
+
+                               if (!currValue) {
+                                       currValue = 0;
+                               } else {
+                                       currValue = parseInt(currValue.slice(0, -2));
+                                       if (!Number.isInteger(currValue))
+                                               currValue = 0;
+                               }
+
+                               width = 0;
+                               if (element.style.width.endsWith("ch")) {
+                                       width = parseInt(element.style.width.slice(0, -2));
+                                       if (!Number.isInteger(width))
+                                               width = 0;
+                               }
+
+                               if (traversar.increment) {
+                                       if (width && width - EvoEditor.TEXT_INDENT_SIZE > 0)
+                                               width = width - EvoEditor.TEXT_INDENT_SIZE;
+                                       currValue = (currValue + EvoEditor.TEXT_INDENT_SIZE) + "ch";
+                               } else if (currValue > EvoEditor.TEXT_INDENT_SIZE) {
+                                       if (width)
+                                               width = width + EvoEditor.TEXT_INDENT_SIZE;
+                                       currValue = (currValue - EvoEditor.TEXT_INDENT_SIZE) + "ch";
+                               } else {
+                                       if (width)
+                                               width = width + currValue;
+                                       currValue = "";
+                               }
+
+                               if (dir == "rtl") {
+                                       element.style.marginRight = currValue;
+                               } else { // "ltr" or other
+                                       element.style.marginLeft = currValue;
+                               }
+
+                               if (width)
+                                       element.style.width = width + "ch";
+
+                               if (change) {
+                                       change.afterMarginLeft = element.style.marginLeft;
+                                       change.afterMarginRight = element.style.marginRight;
+                                       change.afterWidth = element.style.width;
+                               }
+
+                               EvoEditor.removeEmptyStyleAttribute(element);
+                       }
+
+                       return true;
+               }
+       };
+
+       var affected = EvoEditor.ClaimAffectedContent(null, null, 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       traversar.record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, increment ? "Indent" : 
"Outdent", null, null, EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+       traversar.selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       try {
+               EvoEditor.ForeachChildInAffectedContent(affected, traversar);
+
+               if (traversar.record) {
+                       traversar.record.apply = EvoEditor.applyIndent;
+               }
+
+               traversar.selectionUpdater.restore();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, increment ? "Indent" : "Outdent");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.applyDivNormalize = function(record, isUndo)
+{
+       var element = EvoSelection.FindElementByPath(document.body, record.path);
+
+       if (!element)
+               throw "EvoEditor.applyDivNormalize: Path not found";
+
+       var value;
+
+       if (isUndo)
+               value = record.beforeValue;
+       else
+               value = record.afterValue;
+
+       if (record.isWidthStyle) {
+               element.style.width = value;
+               EvoEditor.removeEmptyStyleAttribute(element);
+       } else {
+               element.innerHTML = value;
+       }
+}
+
+EvoEditor.correctParagraphsAfterInsertContent = function(opType)
+{
+       var node, list, ii;
+
+       list = document.body.getElementsByTagName("DIV");
+
+       for (ii = 0; ii < list.length; ii++) {
+               node = list[ii];
+
+               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                       var beforeValue = node.style.width;
+                       EvoEditor.maybeUpdateParagraphWidth(node);
+
+                       if (node.style.width != beforeValue) {
+                               var record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType + 
"::divWidths", node, node, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+                               try {
+                                       if (record) {
+                                               record.path = EvoSelection.GetChildPath(document.body, node);
+                                               record.beforeValue = beforeValue;
+                                               record.afterValue = node.style.width;
+                                               record.isWidthStyle = true;
+                                               record.apply = EvoEditor.applyDivNormalize;
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType + 
"::divWidths");
+                               }
+                       }
+               }
+
+               if (!node.firstChild) {
+                       var record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType + 
"::divBR", node, node, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+                       try {
+                               beforeValue = node.innerHTML;
+                               node.appendChild(document.createElement("BR"));
+
+                               if (record) {
+                                       record.path = EvoSelection.GetChildPath(document.body, node);
+                                       record.beforeValue = beforeValue;
+                                       record.afterValue = node.innerHTML;
+                                       record.apply = EvoEditor.applyDivNormalize;
+                               }
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType + "::divBR");
+                       }
+               }
+       }
+}
+
+EvoEditor.InsertHTML = function(opType, html)
+{
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+       try {
+               document.execCommand("insertHTML", false, html);
+
+               var node, list, ii;
+
+               list = document.body.getElementsByTagName("BLOCKQUOTE");
+
+               for (ii = 0; ii < list.length; ii++) {
+                       node = list[ii];
+
+                       EvoEditor.setAttributeWithUndoRedo("InsertHTML::fixBlockquote", node, "class", null);
+                       EvoEditor.setAttributeWithUndoRedo("InsertHTML::fixBlockquote", node, "style", null);
+               }
+
+               EvoEditor.correctParagraphsAfterInsertContent(opType);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.InsertText = function(opType, text)
+{
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+       try {
+               document.execCommand("insertText", false, text);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.SetBodyAttribute = function(name, value)
+{
+       EvoEditor.setAttributeWithUndoRedo("SetBodyAttribute", document.body, name, value);
+}
+
+EvoEditor.applySetBodyFontName = function(record, isUndo)
+{
+       EvoEditor.UpdateStyleSheet("x-evo-body-fontname", isUndo ? record.beforeCSS : record.afterCSS);
+
+       if (record.beforeStyle != record.afterStyle) {
+               document.body.style.fontFamily = isUndo ? record.beforeStyle : record.afterStyle;
+               EvoEditor.removeEmptyStyleAttribute(body.document);
+       }
+}
+
+EvoEditor.SetBodyFontName = function(name)
+{
+       var record;
+
+       record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBodyFontName", document.body, 
document.body, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+
+       try {
+               var beforeCSS, css, beforeStyle;
+
+               if (name)
+                       css = "body { font-family: " + name + "; }";
+               else
+                       css = null;
+
+               beforeStyle = document.body.style.fontFamily;
+               beforeCSS = EvoEditor.UpdateStyleSheet("x-evo-body-fontname", css);
+
+               if (name != document.body.style.fontFamily)
+                       document.body.style.fontFamily = name ? name : "";
+
+               if (record) {
+                       record.apply = EvoEditor.applySetBodyFontName;
+                       record.beforeCSS = beforeCSS;
+                       record.afterCSS = css;
+                       record.beforeStyle = beforeStyle;
+                       record.afterStyle = document.body.style.fontFamily;
+
+                       if (record.beforeCSS == record.afterCSS && record.beforeStyle == record.afterStyle)
+                               record.ignore = true;
+               }
+
+               EvoEditor.removeEmptyStyleAttribute(document.body);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBodyFontName");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_YES);
+
+               if (!record || !record.ignore)
+                       EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.emptyParagraphAsHtml = function()
+{
+       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+               return "<div style=\"width:" + EvoEditor.NORMAL_PARAGRAPH_WIDTH + "ch;\"><br></div>";
+       } else {
+               return "<div><br></div>";
+       }
+}
+
+EvoEditor.initializeContent = function()
+{
+       // for backward compatibility
+       document.execCommand("StyleWithCSS", false, "false");
+
+       if (document.body) {
+               // attach on body, thus it runs before EvoUndoRedo.beforeInputCb()
+               document.body.onbeforeinput = EvoEditor.beforeInputCb;
+
+               if (!document.body.firstChild) {
+                       EvoUndoRedo.Disable();
+                       try {
+                               document.body.innerHTML = EvoEditor.emptyParagraphAsHtml();
+                       } finally {
+                               EvoUndoRedo.Enable();
+                       }
+               }
+
+               // make sure there is a selection
+               if (!document.getSelection().anchorNode || document.getSelection().anchorNode.tagName == 
"HTML") {
+                       document.getSelection().setPosition(document.body.firstChild ? 
document.body.firstChild : document.body, 0);
+               }
+       }
+}
+
+EvoEditor.getNextNodeInHierarchy = function(node, upToNode)
+{
+       if (!node)
+               return null;
+
+       var next;
+
+       next = node.firstChild;
+
+       if (!next)
+               next = node.nextSibling;
+
+       if (!next) {
+               next = node.parentElement;
+
+               if (next === upToNode || next === document.body)
+                       next = null;
+
+               while (next) {
+                       if (next.nextSibling) {
+                               next = next.nextSibling;
+                               break;
+                       } else {
+                               next = next.parentElement;
+
+                               if (next === upToNode || next === document.body)
+                                       next = null;
+                       }
+               }
+       }
+
+       return next;
+}
+
+// it already knows the line is too long; the node is where the text length exceeded
+EvoEditor.quoteParagraphWrap = function(node, lineLength, wrapWidth, prefixHtml)
+{
+       if (node.nodeType == node.ELEMENT_NODE) {
+               var br = document.createElement("BR");
+               br.className = "-x-evo-wrap-br";
+
+               node.insertAdjacentElement("beforebegin", br);
+               node.insertAdjacentHTML("beforebegin", prefixHtml);
+
+               return node.innerText.length;
+       }
+
+       var words = node.nodeValue.split(" "), ii, offset = 0, inc;
+
+       for (ii = 0; ii < words.length; ii++) {
+               var word = words[ii], wordLen = word.length;
+
+               if (lineLength + wordLen > wrapWidth) {
+                       if (offset > 0) {
+                               node.splitText(offset - 1);
+                               node = node.nextSibling;
+
+                               // erase the space at the end of the line
+                               node.splitText(1);
+                               var next = node.nextSibling;
+                               node.remove();
+                               node = next;
+
+                               var br = document.createElement("BR");
+                               br.className = "-x-evo-wrap-br";
+
+                               node.parentElement.insertBefore(br, node);
+                               br.insertAdjacentHTML("afterend", prefixHtml);
+                       }
+
+                       offset = 0;
+                       lineLength = 0;
+               }
+
+               inc = wordLen + (ii + 1 < words.length ? 1 : 0);
+
+               lineLength += inc;
+               offset += inc;
+       }
+
+       return lineLength;
+}
+
+EvoEditor.getBlockquotePrefixHtml = function(blockquoteLevel)
+{
+       var prefixHtml;
+
+       prefixHtml = "<span class='-x-evo-quote-character'>&gt; </span>".repeat(blockquoteLevel);
+       prefixHtml = "<span class='-x-evo-quoted'>" + prefixHtml + "</span>";
+
+       return prefixHtml;
+}
+
+EvoEditor.quoteParagraph = function(paragraph, blockquoteLevel, wrapWidth)
+{
+       if (!paragraph || !(blockquoteLevel > 0))
+               return;
+
+       EvoEditor.removeQuoteMarks(paragraph);
+
+       if (paragraph.tagName == "PRE")
+               wrapWidth = -1;
+
+       var node = paragraph.firstChild, next, lineLength = 0;
+       var prefixHtml = EvoEditor.getBlockquotePrefixHtml(blockquoteLevel);
+
+       while (node) {
+               next = EvoEditor.getNextNodeInHierarchy(node, paragraph);
+
+               if (node.nodeType == node.TEXT_NODE) {
+                       if (wrapWidth > 0 && lineLength + node.nodeValue.length > wrapWidth) {
+                               lineLength = EvoEditor.quoteParagraphWrap(node, lineLength, wrapWidth, 
prefixHtml);
+                       } else {
+                               lineLength += node.nodeValue.length;
+                       }
+               } else if (node.nodeType == node.ELEMENT_NODE) {
+                       if (node.tagName == "BR") {
+                               if (node.classList.contains("-x-evo-wrap-br")) {
+                                       node.remove();
+                               } else {
+                                       if (node.parentElement.childNodes.length != 1)
+                                               node.insertAdjacentHTML("afterend", prefixHtml);
+
+                                       lineLength = 0;
+                               }
+                       } else if (node.tagName == "A") {
+                               var len = node.innerText.length;
+
+                               if (wrapWidth > 0 && lineLength + len > wrapWidth && (!next || next.tagName 
!= "BR")) {
+                                       lineLength = EvoEditor.quoteParagraphWrap(node, lineLength, 
wrapWidth, prefixHtml);
+                               } else {
+                                       lineLength += len;
+                               }
+
+                               // do not traverse into the anchor element
+                               next = node.nextSibling;
+                       }
+               }
+
+               node = next;
+       }
+
+       paragraph.insertAdjacentHTML("afterbegin", prefixHtml);
+}
+
+EvoEditor.reBlockquotePlainText = function(plainText, usePreTag)
+{
+       var lines = plainText.replace(/\&/g, "&amp;").split("\n"), ii, html = "", level = 0;
+
+       for (ii = 0; ii < lines.length; ii++) {
+               var line = lines[ii], newLevel = 0, skip = 0;
+
+               // Conversion to Plain Text adds empty line at the end
+               if (ii + 1 >= lines.length && !line[0])
+                       break;
+
+               while (line[skip] == '>') {
+                       newLevel++;
+                       skip++;
+                       if (line[skip] == ' ')
+                               skip++;
+               }
+
+               while (newLevel > level) {
+                       html += "<blockquote type='cite'>";
+                       level++;
+               }
+
+               while (newLevel < level) {
+                       html += "</blockquote>";
+                       level--;
+               }
+
+               html += usePreTag ? "<pre>" : "<div>";
+
+               while (line[skip] == ' ') {
+                       skip++;
+                       html += "&nbsp;";
+               }
+
+               if (skip)
+                       line = line.substr(skip);
+
+               html += (line[0] ? line.replace(/</g, "&lt;").replace(/>/g, "&gt;") : "<br>");
+               html += usePreTag ? "</pre>" : "</div>";
+       }
+
+       while (0 < level) {
+               html += "</blockquote>";
+               level--;
+       }
+
+       return html;
+}
+
+EvoEditor.convertParagraphs = function(parent, blockquoteLevel, wrapWidth, canChangeQuoteParagraphs)
+{
+       if (!parent)
+               return;
+
+       var ii;
+
+       for (ii = 0; ii < parent.children.length; ii++) {
+               var child = parent.children.item(ii);
+
+               if (child.tagName == "DIV") {
+                       if (wrapWidth == -1 || (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && 
blockquoteLevel > 0)) {
+                               child.style.width = "";
+                               EvoEditor.removeEmptyStyleAttribute(child);
+                       } else {
+                               child.style.width = wrapWidth + "ch";
+                               child.removeAttribute("x-evo-width");
+                       }
+
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && blockquoteLevel > 0)
+                               EvoEditor.quoteParagraph(child, blockquoteLevel, wrapWidth);
+               } else if (child.tagName == "PRE") {
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && blockquoteLevel > 0) {
+                               var prefixHtml = EvoEditor.getBlockquotePrefixHtml(blockquoteLevel);
+                               var lines, jj, text;
+
+                               text = child.innerText;
+
+                               if (text == "\n" || text == "\r\n")
+                                       lines = [ "" ];
+                               else
+                                       lines = text.split("\n");
+
+                               text = "";
+
+                               for (jj = 0; jj < lines.length; jj++) {
+                                       text += prefixHtml + lines[jj].replace(/\&/g, "&amp;").replace(/</g, 
"&lt;").replace(/>/g, "&gt;");
+
+                                       if (!lines[jj])
+                                               text += "<BR>";
+
+                                       if (jj + 1 < lines.length)
+                                               text += "\n";
+                               }
+
+                               if (!lines.length)
+                                       text += prefixHtml;
+
+                               child.innerHTML = text;
+                       } else {
+                               EvoEditor.convertParagraphs(child, blockquoteLevel, wrapWidth, 
canChangeQuoteParagraphs);
+                       }
+               } else if (child.tagName == "BLOCKQUOTE") {
+                       var innerWrapWidth = wrapWidth;
+
+                       if (innerWrapWidth > 0) {
+                               innerWrapWidth -= 2; // length of "> "
+
+                               if (innerWrapWidth < EvoConvert.MIN_PARAGRAPH_WIDTH)
+                                       innerWrapWidth = EvoConvert.MIN_PARAGRAPH_WIDTH;
+                       }
+
+                       // replace blockquote content with pure plain text and then re-blockquote it
+                       // and do it only on the top level, not recursively (nested citations)
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && !blockquoteLevel) {
+                               child.innerHTML = 
EvoEditor.reBlockquotePlainText(EvoConvert.ToPlainText(child, -1),
+                                       (child.firstElementChild && child.firstElementChild.tagName == "PRE" 
&& (
+                                       !canChangeQuoteParagraphs || 
!EvoEditor.WRAP_QUOTED_TEXT_IN_REPLIES)));
+                       }
+
+                       EvoEditor.convertParagraphs(child, blockquoteLevel + 1, innerWrapWidth, 
canChangeQuoteParagraphs);
+               } else if (child.tagName == "UL") {
+                       if (wrapWidth == -1) {
+                               child.style.width = "";
+                               EvoEditor.removeEmptyStyleAttribute(child);
+                       } else {
+                               var innerWrapWidth = wrapWidth;
+
+                               innerWrapWidth -= 3; // length of " * " prefix
+
+                               if (innerWrapWidth < EvoConvert.MIN_PARAGRAPH_WIDTH)
+                                       innerWrapWidth = EvoConvert.MIN_PARAGRAPH_WIDTH;
+
+                               child.style.width = innerWrapWidth + "ch";
+                       }
+               } else if (child.tagName == "OL") {
+                       if (wrapWidth == -1) {
+                               child.style.width = "";
+                               child.style.paddingInlineStart = "";
+                               EvoEditor.removeEmptyStyleAttribute(child);
+                       } else {
+                               var innerWrapWidth = wrapWidth, olNeedWidth;
+
+                               olNeedWidth = EvoConvert.GetOLMaxLetters(child.getAttribute("type"), 
child.children.length) + 2; // length of ". " suffix
+
+                               if (olNeedWidth < EvoConvert.MIN_OL_WIDTH)
+                                       olNeedWidth = EvoConvert.MIN_OL_WIDTH;
+
+                               innerWrapWidth -= olNeedWidth;
+
+                               if (innerWrapWidth < EvoConvert.MIN_PARAGRAPH_WIDTH)
+                                       innerWrapWidth = EvoConvert.MIN_PARAGRAPH_WIDTH;
+
+                               child.style.width = innerWrapWidth + "ch";
+                               child.style.paddingInlineStart = olNeedWidth + "ch";
+                       }
+               }
+       }
+}
+
+EvoEditor.SetNormalParagraphWidth = function(value)
+{
+       if (EvoEditor.NORMAL_PARAGRAPH_WIDTH != value) {
+               EvoEditor.NORMAL_PARAGRAPH_WIDTH = value;
+
+               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT)
+                       EvoEditor.convertParagraphs(document.body, 0, EvoEditor.NORMAL_PARAGRAPH_WIDTH, 
false);
+       }
+}
+
+EvoEditor.moveNodeContent = function(node, intoNode)
+{
+       if (!node || !node.parentElement)
+               return;
+
+       var parent = node.parentElement;
+
+       while (node.firstChild) {
+               if (intoNode) {
+                       intoNode.append(node.firstChild);
+               } else {
+                       parent.insertBefore(node.firstChild, node);
+               }
+       }
+}
+
+EvoEditor.convertTags = function()
+{
+       var ii, list;
+
+       for (ii = document.images.length - 1; ii >= 0; ii--) {
+               var img = document.images[ii];
+
+               img.outerText = EvoConvert.ImgToText(img);
+       }
+
+       list = document.getElementsByTagName("A");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var anchor = list[ii];
+
+               EvoEditor.moveNodeContent(anchor);
+
+               anchor.remove();
+       }
+
+       list = document.getElementsByTagName("TABLE");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var table = list[ii], textNode;
+
+               textNode = document.createTextNode(table.innerText);
+               table.parentElement.insertBefore(textNode, table);
+               table.remove();
+       }
+
+       list = document.getElementsByTagName("BLOCKQUOTE");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var blockquoteNode = list[ii];
+
+               blockquoteNode.removeAttribute("class");
+               blockquoteNode.removeAttribute("style");
+       }
+
+       var node = document.body.firstChild, next;
+
+       while (node) {
+               var removeNode = false;
+
+               if (node.nodeType == node.ELEMENT_NODE) {
+                       if (node.tagName != "DIV" &&
+                           node.tagName != "PRE" &&
+                           node.tagName != "BLOCKQUOTE" &&
+                           node.tagName != "UL" &&
+                           node.tagName != "OL" &&
+                           node.tagName != "LI" &&
+                           node.tagName != "BR") {
+                               removeNode = true;
+
+                               // convert P into DIV
+                               if (node.tagName == "P") {
+                                       var div = document.createElement("DIV");
+                                       EvoEditor.moveNodeContent(node, div);
+                                       node.parentElement.insertBefore(div, node.nextSibling);
+                               } else {
+                                       EvoEditor.moveNodeContent(node);
+                               }
+                       }
+               }
+
+               // skip the signature wrapper
+               if (!removeNode && node.tagName == "DIV" && node.className == "-x-evo-signature-wrapper")
+                       next = node.nextSibling;
+               else
+                       next = EvoEditor.getNextNodeInHierarchy(node, document.body);
+
+               if (removeNode)
+                       node.remove();
+
+               node = next;
+       }
+
+       document.body.normalize();
+}
+
+EvoEditor.removeQuoteMarks = function(element)
+{
+       var ii, list;
+
+       if (!element)
+               element = document;
+
+       list = element.querySelectorAll("SPAN.-x-evo-quoted");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var node = list[ii];
+
+               node.remove();
+       }
+
+       list = element.querySelectorAll("BR.-x-evo-wrap-br");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var node = list[ii];
+
+               node.insertAdjacentText("beforebegin", " ");
+               node.remove();
+       }
+
+       if (element === document)
+               document.body.normalize();
+       else
+               element.normalize();
+}
+
+EvoEditor.SetMode = function(mode)
+{
+       if (EvoEditor.mode != mode) {
+               var opType = "setMode::" + (mode == EvoEditor.MODE_PLAIN_TEXT ? "PlainText" : "HTML"), record;
+
+               record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_DOCUMENT, opType, null, null);
+
+               if (record) {
+                       record.modeBefore = EvoEditor.mode;
+                       record.modeAfter = mode;
+                       record.apply = function(record, isUndo) {
+                               var useMode = isUndo ? record.modeBefore : record.modeAfter;
+
+                               if (EvoEditor.mode != useMode) {
+                                       EvoEditor.mode = useMode;
+                               }
+                       }
+               }
+
+               EvoUndoRedo.Disable();
+               try {
+                       EvoEditor.mode = mode;
+
+                       EvoEditor.removeQuoteMarks(null);
+
+                       if (mode == EvoEditor.MODE_PLAIN_TEXT) {
+                               EvoEditor.convertTags();
+                               EvoEditor.convertParagraphs(document.body, 0, 
EvoEditor.NORMAL_PARAGRAPH_WIDTH, false);
+                       } else {
+                               EvoEditor.convertParagraphs(document.body, 0, -1, false);
+                       }
+               } finally {
+                       EvoUndoRedo.Enable();
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_DOCUMENT, opType);
+
+                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_YES);
+               }
+       }
+}
+
+EvoEditor.applyFontReset = function(record, isUndo)
+{
+       if (record.changes) {
+               var ii;
+
+               for (ii = 0; ii < record.changes.length; ii++) {
+                       var change = record.changes[isUndo ? (record.changes.length - ii - 1) : ii];
+                       var parent = EvoSelection.FindElementByPath(document.body, change.parentPath);
+
+                       if (!parent) {
+                               throw "EvoEditor.applyFontReset: Cannot find node at path " + change.path;
+                       }
+
+                       parent.innerHTML = isUndo ? change.htmlBefore : change.htmlAfter;
+               }
+       }
+}
+
+EvoEditor.replaceInheritFonts = function(undoRedoRecord, selectionUpdater, nodes)
+{
+       var ii;
+
+       if (!nodes)
+               nodes = document.querySelectorAll("FONT[face=inherit]");
+
+       for (ii = nodes.length - 1; ii >= 0; ii--) {
+               var node = nodes.item(ii);
+
+               if (!node || (!undoRedoRecord && !document.getSelection().containsNode(node, true)))
+                       continue;
+
+               var parent, change = null;
+
+               parent = node.parentElement;
+
+               if (undoRedoRecord) {
+                       if (!undoRedoRecord.changes)
+                               undoRedoRecord.changes = [];
+
+                       change = {
+                               parentPath : EvoSelection.GetChildPath(document.body, parent),
+                               htmlBefore : parent.innerHTML,
+                               htmlAfter : ""
+                       };
+
+                       undoRedoRecord.changes[undoRedoRecord.changes.length] = change;
+               }
+
+               if (node.attributes.length == 1) {
+                       var child;
+
+                       while (node.firstChild) {
+                               var child = node.firstChild;
+
+                               selectionUpdater.beforeRemove(child);
+
+                               parent.insertBefore(child, node);
+
+                               selectionUpdater.afterRemove(child);
+                       }
+
+                       node.remove();
+               } else {
+                       node.removeAttribute("face");
+               }
+
+               if (change)
+                       change.htmlAfter = parent.innerHTML;
+       }
+
+       if (undoRedoRecord && undoRedoRecord.changes)
+               undoRedoRecord.apply = EvoEditor.applyFontReset;
+}
+
+EvoEditor.maybeReplaceInheritFonts = function()
+{
+       var nodes = document.querySelectorAll("FONT[face=inherit]");
+
+       if (nodes.length <= 0)
+               return;
+
+       var record, selectionUpdater;
+
+       selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "UnsetFontName", null, null, 
EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+       try {
+               EvoEditor.replaceInheritFonts(record, selectionUpdater, nodes);
+
+               selectionUpdater.restore();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "UnsetFontName");
+
+               if (record)
+                       EvoUndoRedo.GroupTopRecords(2);
+       }
+}
+
+EvoEditor.SetFontName = function(name)
+{
+       if (!name || name == "")
+               name = "inherit";
+
+       var record, selectionUpdater = EvoSelection.CreateUpdaterObject(), bodyFontFamily;
+
+       // to workaround https://bugs.webkit.org/show_bug.cgi?id=204622
+       bodyFontFamily = document.body.style.fontFamily;
+
+       record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName");
+       try {
+               if (!document.getSelection().isCollapsed && bodyFontFamily)
+                       document.body.style.fontFamily = "";
+
+               document.execCommand("FontName", false, name);
+
+               if (document.getSelection().isCollapsed) {
+                       if (name == "inherit")
+                               EvoEditor.checkInheritFontsOnChange = true;
+
+                       /* Format change on collapsed selection is not applied immediately */
+                       if (record)
+                               record.ignore = true;
+               } else if (name == "inherit") {
+                       var subrecord;
+
+                       subrecord = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName", 
null, null, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+                       try {
+                               EvoEditor.replaceInheritFonts(subrecord, selectionUpdater);
+                               selectionUpdater.restore();
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName");
+                       }
+               }
+       } finally {
+               if (bodyFontFamily && document.body.style.fontFamily != bodyFontFamily)
+                       document.body.style.fontFamily = bodyFontFamily;
+
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+
+               EvoEditor.removeEmptyStyleAttribute(document.body);
+       }
+}
+
+EvoEditor.convertHtmlToSend = function()
+{
+       var html, bgcolor, text, link, vlink;
+       var unsetBgcolor = false, unsetText = false, unsetLink = false, unsetVlink = false;
+       var themeCss, inheritThemeColors = EvoEditor.inheritThemeColors;
+       var ii, styles, styleNode = null, topSignatureSpacers, elems;
+
+       themeCss = EvoEditor.UpdateThemeStyleSheet(null);
+       bgcolor = document.documentElement.getAttribute("x-evo-bgcolor");
+       text = document.documentElement.getAttribute("x-evo-text");
+       link = document.documentElement.getAttribute("x-evo-link");
+       vlink = document.documentElement.getAttribute("x-evo-vlink");
+
+       document.documentElement.removeAttribute("x-evo-bgcolor");
+       document.documentElement.removeAttribute("x-evo-text");
+       document.documentElement.removeAttribute("x-evo-link");
+       document.documentElement.removeAttribute("x-evo-vlink");
+
+       topSignatureSpacers = document.querySelectorAll(".-x-evo-top-signature-spacer");
+
+       for (ii = topSignatureSpacers.length - 1; ii >= 0; ii--) {
+               topSignatureSpacers[ii].removeAttribute("class");
+       }
+
+       if (inheritThemeColors) {
+               if (bgcolor && !document.body.getAttribute("bgcolor")) {
+                       document.body.setAttribute("bgcolor", bgcolor);
+                       unsetBgcolor = true;
+               }
+
+               if (text && !document.body.getAttribute("text")) {
+                       document.body.setAttribute("text", text);
+                       unsetText = true;
+               }
+
+               if (link && !document.body.getAttribute("link")) {
+                       document.body.setAttribute("link", link);
+                       unsetLink = true;
+               }
+
+               if (vlink && !document.body.getAttribute("vlink")) {
+                       document.body.setAttribute("vlink", vlink);
+                       unsetVlink = true;
+               }
+       }
+
+       styles = document.head.getElementsByTagName("style");
+
+       for (ii = 0; ii < styles.length; ii++) {
+               if (styles[ii].id == "x-evo-body-fontname") {
+                       styleNode = styles[ii];
+                       styleNode.id = "";
+                       break;
+               }
+       }
+
+       if (EvoEditor.mode == EvoEditor.MODE_HTML) {
+               elems = document.getElementsByTagName("BLOCKQUOTE");
+
+               for (ii = 0; ii < elems.length; ii++) {
+                       elems[ii].setAttribute("style", EvoEditor.BLOCKQUOTE_STYLE);
+               }
+       }
+
+       html = document.documentElement.outerHTML;
+
+       if (EvoEditor.mode == EvoEditor.MODE_HTML) {
+               elems = document.getElementsByTagName("BLOCKQUOTE");
+
+               for (ii = 0; ii < elems.length; ii++) {
+                       elems[ii].removeAttribute("style");
+               }
+       }
+
+       if (styleNode)
+               styleNode.id = "x-evo-body-fontname";
+
+       if (bgcolor)
+               document.documentElement.setAttribute("x-evo-bgcolor", bgcolor);
+       if (text)
+               document.documentElement.setAttribute("x-evo-text", text);
+       if (link)
+               document.documentElement.setAttribute("x-evo-link", link);
+       if (vlink)
+               document.documentElement.setAttribute("x-evo-vlink", vlink);
+
+       if (inheritThemeColors) {
+               if (unsetBgcolor)
+                       document.body.removeAttribute("bgcolor");
+
+               if (unsetText)
+                       document.body.removeAttribute("text");
+
+               if (unsetLink)
+                       document.body.removeAttribute("link");
+
+               if (unsetVlink)
+                       document.body.removeAttribute("vlink");
+       }
+
+       for (ii = topSignatureSpacers.length - 1; ii >= 0; ii--) {
+               var elem = topSignatureSpacers[ii];
+
+               if (elem.previousSibling && elem.previousSibling.tagName == "DIV" && 
elem.previousSibling.className == "-x-evo-signature-wrapper") {
+                       elem.className = "-x-evo-top-signature-spacer";
+                       break;
+               }
+       }
+
+       if (themeCss)
+               EvoEditor.UpdateThemeStyleSheet(themeCss);
+
+       return html;
+}
+
+EvoEditor.GetContent = function(flags, cid_uid_prefix)
+{
+       var content_data = {};
+
+       if (!document.body)
+               return content_data;
+
+       var img_elems = [], data_names = [], bkg_elems = [], elems, ii, jj, currentElemsArray = null;
+       var scrollX = window.scrollX, scrollY = window.scrollY;
+
+       EvoUndoRedo.Disable();
+
+       try {
+               currentElemsArray = EvoEditor.RemoveCurrentElementAttr();
+
+               // For safety, to not export with empty 'style' attributes; these do not need Undo
+               elems = document.querySelectorAll("[style='']");
+               for (ii = 0; ii < elems.length; ii++) {
+                       EvoEditor.removeEmptyStyleAttribute(elems[ii]);
+               }
+
+               if ((flags & EvoEditor.E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED) != 0) {
+                       var hidden_elems = [];
+
+                       try {
+                               elems = document.getElementsByClassName("-x-evo-signature-wrapper");
+                               if (elems && elems.length) {
+                                       for (ii = 0; ii < elems.length; ii++) {
+                                               var elem = elems.item(ii);
+
+                                               if (elem && !elem.hidden) {
+                                                       hidden_elems[hidden_elems.length] = elem;
+                                                       elem.hidden = true;
+                                               }
+                                       }
+                               }
+
+                               elems = document.getElementsByTagName("BLOCKQUOTE");
+                               if (elems && elems.length) {
+                                       for (ii = 0; ii < elems.length; ii++) {
+                                               var elem = elems.item(ii);
+
+                                               if (elem && !elem.hidden) {
+                                                       hidden_elems[hidden_elems.length] = elem;
+                                                       elem.hidden = true;
+                                               }
+                                       }
+                               }
+
+                               content_data["raw-body-stripped"] = document.body.innerText;
+                       } finally {
+                               for (ii = 0; ii < hidden_elems.length; ii++) {
+                                       hidden_elems[ii].hidden = false;
+                               }
+                       }
+               }
+
+               // Do these before changing image sources
+               if ((flags & EvoEditor.E_CONTENT_EDITOR_GET_RAW_BODY_HTML) != 0)
+                       content_data["raw-body-html"] = document.body.innerHTML;
+
+               if ((flags & EvoEditor.E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN) != 0)
+                       content_data["raw-body-plain"] = document.body.innerText;
+
+               if (EvoEditor.mode == EvoEditor.MODE_HTML &&
+                   (flags & EvoEditor.E_CONTENT_EDITOR_GET_INLINE_IMAGES) != 0) {
+                       var images = [];
+
+                       for (ii = 0; ii < document.images.length; ii++) {
+                               var elem = document.images.item(ii);
+                               var src = (elem && elem.src) ? elem.src.toLowerCase() : "";
+
+                               if (elem && (
+                                   src.startsWith("data:") ||
+                                   src.startsWith("file://") ||
+                                   src.startsWith("evo-file://"))) {
+                                       for (jj = 0; jj < img_elems.length; jj++) {
+                                               if (elem.src == img_elems[jj].orig_src) {
+                                                       img_elems[jj].subelems[img_elems[jj].subelems.length] 
= elem;
+                                                       elem.src = img_elems[jj].cid;
+                                                       break;
+                                               }
+                                       }
+
+                                       if (jj >= img_elems.length) {
+                                               var img_obj = {
+                                                       subelems : [ elem ],
+                                                       cid : "cid:" + cid_uid_prefix + "-" + 
img_elems.length,
+                                                       orig_src : elem.src
+                                               };
+
+                                               if (elem.src.toLowerCase().startsWith("cid:"))
+                                                       img_obj.cid = elem.src;
+
+                                               img_elems[img_elems.length] = img_obj;
+                                               images[images.length] = {
+                                                       cid : img_obj.cid,
+                                                       src : elem.src
+                                               };
+                                               elem.src = img_obj.cid;
+
+                                               if (elem.hasAttribute("data-name"))
+                                                       images[images.length - 1].name = 
elem.getAttribute("data-name");
+                                       }
+                               } else if (elem && src.startsWith("cid:")) {
+                                       images[images.length] = {
+                                               cid : elem.src,
+                                               src : elem.src
+                                       };
+                               }
+
+                               if (elem) {
+                                       // just remove the attribute used by the old editor
+                                       elem.removeAttribute("data-inline");
+
+                                       if (elem.hasAttribute("data-name")) {
+                                               data_names[data_names.length] = {
+                                                       elem : elem,
+                                                       name : elem.getAttribute("data-name")
+                                               };
+
+                                               elem.removeAttribute("data-name");
+                                       }
+                               }
+                       }
+
+                       var backgrounds = document.querySelectorAll("[background]");
+                       for (ii = 0; ii < backgrounds.length; ii++) {
+                               var elem = backgrounds[ii];
+                               var src = elem ? elem.getAttribute("background").toLowerCase() : "";
+
+                               if (elem && (
+                                   src.startsWith("data:") ||
+                                   src.startsWith("file://") ||
+                                   src.startsWith("evo-file://"))) {
+                                       var bkg = elem.getAttribute("background");
+
+                                       for (jj = 0; jj < bkg_elems.length; jj++) {
+                                               if (bkg == bkg_elems[jj].orig_src) {
+                                                       bkg_elems[jj].subelems[bkg_elems[jj].subelems.length] 
= elem;
+                                                       elem.setAttribute("background", bkg_elems[jj].cid);
+                                                       break;
+                                               }
+                                       }
+
+                                       if (jj >= bkg_elems.length) {
+                                               var bkg_obj = {
+                                                       subelems : [ elem ],
+                                                       cid : "cid:" + cid_uid_prefix + "-" + 
bkg_elems.length,
+                                                       orig_src : bkg
+                                               };
+
+                                               // re-read, because it could change
+                                               if 
(elem.getAttribute("background").toLowerCase().startsWith("cid:"))
+                                                       bkg_obj.cid = elem.getAttribute("background");
+
+                                               bkg_elems[bkg_elems.length] = bkg_obj;
+                                               images[images.length] = {
+                                                       cid : bkg_obj.cid,
+                                                       src : elem.getAttribute("background")
+                                               };
+                                               elem.setAttribute("background", bkg_obj.cid);
+
+                                               if (elem.hasAttribute("data-name"))
+                                                       images[images.length - 1].name = 
elem.getAttribute("data-name");
+                                       }
+                               } else if (elem && src.startsWith("cid:")) {
+                                       images[images.length] = {
+                                               cid : elem.getAttribute("background"),
+                                               src : elem.getAttribute("background")
+                                       };
+                               }
+
+                               if (elem) {
+                                       if (elem.hasAttribute("data-name")) {
+                                               data_names[data_names.length] = {
+                                                       elem : elem,
+                                                       name : elem.getAttribute("data-name")
+                                               };
+
+                                               elem.removeAttribute("data-name");
+                                       }
+                               }
+                       }
+
+                       if (images.length)
+                               content_data["images"] = images;
+               }
+
+               // Draft should have replaced images as well
+               if ((flags & EvoEditor.E_CONTENT_EDITOR_GET_RAW_DRAFT) != 0) {
+                       document.head.setAttribute("x-evo-selection", 
EvoSelection.ToString(EvoSelection.Store(document)));
+                       try {
+                               content_data["raw-draft"] = document.documentElement.innerHTML;
+                       } finally {
+                               document.head.removeAttribute("x-evo-selection");
+                       }
+               }
+
+               if ((flags & EvoEditor.E_CONTENT_EDITOR_GET_TO_SEND_HTML) != 0)
+                       content_data["to-send-html"] = EvoEditor.convertHtmlToSend();
+
+               if ((flags & EvoEditor. E_CONTENT_EDITOR_GET_TO_SEND_PLAIN) != 0) {
+                       content_data["to-send-plain"] = EvoConvert.ToPlainText(document.body, 
EvoEditor.NORMAL_PARAGRAPH_WIDTH);
+               }
+       } finally {
+               try {
+                       for (ii = 0; ii < img_elems.length; ii++) {
+                               var img_obj = img_elems[ii];
+
+                               for (jj = 0; jj < img_obj.subelems.length; jj++) {
+                                       img_obj.subelems[jj].src = img_obj.orig_src;
+                               }
+                       }
+
+                       for (ii = 0; ii < data_names.length; ii++) {
+                               data_names[ii].elem.setAttribute("data-name", data_names[ii].name);
+                       }
+
+                       for (ii = 0; ii < bkg_elems.length; ii++) {
+                               var bkg_obj = bkg_elems[ii];
+
+                               for (jj = 0; jj < bkg_obj.subelems.length; jj++) {
+                                       bkg_obj.subelems[jj].setAttribute("background", bkg_obj.orig_src);
+                               }
+                       }
+
+                       EvoEditor.RestoreCurrentElementAttr(currentElemsArray);
+               } finally {
+                       EvoUndoRedo.Enable();
+               }
+       }
+
+       // the above changes can cause change of the scroll offset, thus restore it
+       window.scrollTo(scrollX, scrollY);
+
+       return content_data;
+}
+
+EvoEditor.UpdateStyleSheet = function(id, css)
+{
+       var styles, ii, res = null;
+
+       styles = document.head.getElementsByTagName("style");
+
+       for (ii = 0; ii < styles.length; ii++) {
+               if (styles[ii].id == id) {
+                       res = styles[ii].innerHTML;
+
+                       if (css)
+                               styles[ii].innerHTML = css;
+                       else
+                               document.head.removeChild(styles[ii]);
+
+                       return res;
+               }
+       }
+
+       if (css) {
+               var style;
+
+               style = document.createElement("STYLE");
+               style.id = id;
+               style.innerHTML = css;
+               document.head.append(style);
+       }
+
+       return res;
+}
+
+EvoEditor.UpdateThemeStyleSheet = function(css)
+{
+       return EvoEditor.UpdateStyleSheet("x-evo-theme-sheet", css);
+}
+
+EvoEditor.findSmileys = function(text, unicodeSmileys)
+{
+       /* Based on original use_pictograms() from GtkHTML */
+       var emoticons_chars = [
+               /*  0 */  "D",  "O",  ")",  "(",  "|",  "/",  "P",  "Q",  "*",  "!",
+               /* 10 */  "S", null,  ":",  "-", null,  ":", null,  ":",  "-",  null,
+               /* 20 */  ":", null,  ":",  ";",  "=",  "-", "\"", null,  ":",  ";",
+               /* 30 */  "B", "\"",  "|", null,  ":",  "-",  "'", null,  ":",  "X",
+               /* 40 */ null,  ":", null,  ":",  "-", null,  ":", null,  ":",  "-",
+               /* 50 */ null,  ":", null,  ":",  "-", null,  ":", null,  ":",  "-",
+               /* 60 */ null,  ":", null,  ":", null,  ":",  "-", null,  ":", null,
+               /* 70 */  ":",  "-", null,  ":", null,  ":",  "-", null,  ":", null ];
+       var 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 ];
+       var 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"
+       ];
+       var res = null, pos, state, start, uc;
+
+       start = text.length - 1;
+
+       if (start < 1)
+               return res;
+
+       pos = start;
+       while (pos >= 0) {
+               state = 0;
+               while (pos >= 0) {
+                       uc = text[pos];
+                       var relative = 0;
+                       while (emoticons_chars[state + relative] != null) {
+                               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 = text[pos - 1];
+                       if (uc == 'O') {
+                               state = -1;
+                               pos--;
+                       } else if (uc == '>') {
+                               state = -5;
+                               pos--;
+                       }
+               }
+
+               if (state < 0) {
+                       if (pos > 0) {
+                               uc = text[pos - 1];
+
+                               if (uc != ' ') {
+                                       return res;
+                               }
+                       }
+
+                       var obj = EvoEditor.lookupEmoticon(emoticons_icon_names[- state - 1], unicodeSmileys);
+
+                       if (obj) {
+                               obj.start = pos;
+                               obj.end = start + 1;
+
+                               if (!res)
+                                       res = [];
+
+                               res[res.length] = obj;
+                       }
+
+                       pos--;
+                       start = pos;
+               } else {
+                       break;
+               }
+       }
+
+       return res;
+}
+
+EvoEditor.maybeUpdateParagraphWidth = function(topNode)
+{
+       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+               var node = topNode, citeLevel = 0;
+
+               while (node && node.tagName != "BODY") {
+                       if (node.tagName == "BLOCKQUOTE")
+                               citeLevel++;
+
+                       node = node.parentElement;
+               }
+
+               if (citeLevel * 2 < EvoEditor.NORMAL_PARAGRAPH_WIDTH) {
+                       topNode.style.width = (EvoEditor.NORMAL_PARAGRAPH_WIDTH - citeLevel * 2) + "ch";
+               }
+       }
+}
+
+EvoEditor.splitAtChild = function(parent, childNode)
+{
+       var newNode, node, next, ii;
+
+       newNode = parent.ownerDocument.createElement(parent.tagName);
+
+       for (ii = 0; ii < parent.attributes.length; ii++) {
+               newNode.setAttribute(parent.attributes[ii].nodeName, parent.attributes[ii].nodeValue);
+       }
+
+       node = childNode;
+       while (node) {
+               next = node.nextSibling;
+               newNode.appendChild(node);
+               node = next;
+       }
+
+       parent.parentElement.insertBefore(newNode, parent.nextSibling);
+
+       return newNode;
+}
+
+EvoEditor.hasElementWithTagNameAsParent = function(node, tagName)
+{
+       if (!node)
+               return false;
+
+       for (node = node.parentElement; node; node = node.parentElement) {
+               if (node.tagName == tagName)
+                       return true;
+       }
+
+       return false;
+}
+
+EvoEditor.requoteNodeParagraph = function(node)
+{
+       while (node && node.tagName != "BODY" && !EvoEditor.IsBlockNode(node)) {
+               node = node.parentElement;
+       }
+
+       if (!node || node.tagName == "BODY")
+               return null;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "requote", node, node,
+               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+       try {
+               var blockquoteLevel = EvoEditor.getBlockquoteLevel(node);
+
+               EvoEditor.quoteParagraph(node, blockquoteLevel, EvoEditor.NORMAL_PARAGRAPH_WIDTH - (2 * 
blockquoteLevel));
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "requote");
+       }
+
+       return node;
+}
+
+EvoEditor.replaceMatchWithNode = function(opType, node, match, newNode, canEmit, withUndo)
+{
+       if (withUndo) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType, node.parentElement, 
node.parentElement,
+                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       }
+
+       try {
+               var selection = document.getSelection();
+               var offset = selection.anchorOffset, updateSelection = selection.anchorNode === node, 
newAnchorNode;
+
+               node.splitText(match.end);
+               newAnchorNode = node.nextSibling;
+               node.splitText(match.start);
+
+               node = node.nextSibling;
+
+               node.parentElement.insertBefore(newNode, node);
+               if (newNode.tagName == "A")
+                       newNode.appendChild(node);
+               else
+                       node.remove();
+
+               if (updateSelection && newAnchorNode && offset - match.end >= 0)
+                       selection.setPosition(newAnchorNode, offset - match.end);
+       } finally {
+               if (withUndo) {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType);
+
+                       if (canEmit) {
+                               EvoUndoRedo.GroupTopRecords(2);
+                               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                               EvoEditor.EmitContentChanged();
+                       }
+               }
+       }
+
+       return node;
+}
+
+EvoEditor.linkifyText = function(anchorNode, withUndo)
+{
+       if (!anchorNode)
+               return false;
+
+       var text = anchorNode.nodeValue, tmpNode;
+
+       if (!text)
+               return false;
+
+       for (tmpNode = anchorNode; tmpNode && tmpNode.tagName != "BODY"; tmpNode = tmpNode.parentElement) {
+               if (tmpNode.tagName == "A") {
+                       return false;
+               }
+       }
+
+       var parts, ii;
+
+       parts = EvoEditor.splitTextWithLinks(text);
+
+       if (!parts)
+               return false;
+
+       if (withUndo) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink", 
anchorNode.parentElement, anchorNode.parentElement,
+                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       }
+
+       try {
+               var selection = document.getSelection(), matchEnd = 0, insBefore, parent;
+               var offset = selection.anchorOffset, updateSelection = selection.anchorNode === anchorNode, 
newAnchorNode = null;
+
+               insBefore = anchorNode;
+               parent = anchorNode.parentElement;
+
+               for (ii = 0; ii < parts.length; ii++) {
+                       var part = parts[ii], node, isLast = ii + 1 >= parts.length;
+
+                       if (part.href) {
+                               node = document.createElement("A");
+                               node.href = part.href;
+                               node.innerText = part.text;
+                       } else if (isLast) {
+                               node = null;
+                               // it can be a space, which cannot be added after the element, thus 
workaround it this way
+                               newAnchorNode = anchorNode.splitText(matchEnd);
+                       } else {
+                               node = document.createTextNode(part.text);
+                       }
+
+                       if (node)
+                               parent.insertBefore(node, insBefore);
+
+                       if (!isLast) {
+                               matchEnd += part.text.length;
+                       } else if (node) {
+                               newAnchorNode = node;
+                       }
+               }
+
+               if (anchorNode)
+                       anchorNode.remove();
+
+               if (updateSelection && newAnchorNode && offset - matchEnd >= 0)
+                       selection.setPosition(newAnchorNode, offset - matchEnd);
+       } finally {
+               if (withUndo) {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "magicLink");
+
+                       EvoUndoRedo.GroupTopRecords(2);
+                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                       EvoEditor.EmitContentChanged();
+               }
+       }
+
+       return true;
+}
+
+EvoEditor.maybeRemoveQuotationMark = function(node)
+{
+       if (!node || node.nodeType != node.ELEMENT_NODE || node.tagName != "SPAN" ||
+           node.className != "-x-evo-quoted")
+               return false;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "removeQuotationMark", node, node,
+               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+       try {
+               node.remove();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "removeQuotationMark");
+       }
+
+       return true;
+}
+
+EvoEditor.removeEmptyElements = function(tagName)
+{
+       var nodes, node, ii, didRemove = 0;
+
+       nodes = document.getElementsByTagName(tagName);
+
+       for (ii = nodes.length - 1; ii >= 0; ii--) {
+               node = nodes[ii];
+
+               // more than one child element means it's not empty
+               if (node.childElementCount > 1)
+                       continue;
+
+               // the first element is not quotation mark
+               if (node.firstElementChild && (node.firstElementChild.tagName != "SPAN" ||
+                   node.firstElementChild.className != "-x-evo-quoted"))
+                       continue;
+
+               // the text inside is not empty, possibly on either of the two sides of the quotation mark
+               if ((node.firstChild && (node.firstChild.nodeType == node.TEXT_NODE && 
node.firstChild.nodeValue)) ||
+                   (node.lastChild && !(node.lastChild === node.firstChild) && (node.lastChild.nodeType == 
node.TEXT_NODE && node.lastChild.nodeValue)))
+                       continue;
+
+               // it's either completely empty or it contains only the quotation mark and nothing else
+               didRemove++;
+
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "removeEmptyElem::" + tagName, 
node.parentElement, node.parentElement,
+                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+               try {
+                       node.remove();
+               } finally {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "removeEmptyElem::" + tagName);
+               }
+       }
+
+       return didRemove;
+}
+
+EvoEditor.beforeInputCb = function(inputEvent)
+{
+       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && inputEvent && (
+           inputEvent.inputType == "deleteContentForward" || inputEvent.inputType == 
"deleteContentBackward")) {
+               var selection = document.getSelection();
+
+               // workaround WebKit bug https://bugs.webkit.org/show_bug.cgi?id=209605
+               if (selection.anchorNode && selection.anchorNode.nodeType == 
selection.anchorNode.ELEMENT_NODE &&
+                   selection.isCollapsed && EvoEditor.IsBlockNode(selection.anchorNode) && 
selection.anchorNode.firstChild.tagName == "BR" &&
+                   !selection.anchorNode.firstChild.nextSibling) {
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, inputEvent.inputType, 
selection.anchorNode, selection.anchorNode,
+                               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+                       try {
+                               var next = selection.anchorNode.nextSibling;
+
+                               if (!next)
+                                       next = selection.anchorNode.previousSibling;
+                               if (!next)
+                                       next = selection.anchorNode.parentElement;
+
+                               selection.anchorNode.remove();
+
+                               selection.setPosition(next, 0);
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, inputEvent.inputType);
+                       }
+
+                       inputEvent.stopImmediatePropagation();
+                       inputEvent.stopPropagation();
+                       inputEvent.preventDefault();
+
+                       return;
+               }
+       }
+
+       if (EvoUndoRedo.disabled ||
+           !inputEvent ||
+           inputEvent.inputType != "insertText" ||
+           !inputEvent.data ||
+           inputEvent.data.length != 1 ||
+           inputEvent.data == " " ||
+           inputEvent.data == "\t")
+               return;
+
+       var selection = document.getSelection();
+
+       // when writing at the end of the anchor, then write into the anchor, not out (WebKit writes out)
+       if (!selection ||
+           !selection.isCollapsed ||
+           !selection.anchorNode ||
+           selection.anchorNode.nodeType != selection.anchorNode.TEXT_NODE ||
+           selection.anchorOffset != selection.anchorNode.nodeValue.length ||
+           !selection.anchorNode.parentElement ||
+           selection.anchorNode.parentElement.tagName != "A")
+               return;
+
+       var node = selection.anchorNode;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, "insertText", selection.anchorNode, 
selection.anchorNode,
+               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       try {
+               node.nodeValue += inputEvent.data;
+               selection.setPosition(node, node.nodeValue.length);
+
+               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT)
+                       node.parentElement.href = node.nodeValue;
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, "insertText");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+
+       // it will add the text, if anything breaks before it gets here
+       inputEvent.stopImmediatePropagation();
+       inputEvent.stopPropagation();
+       inputEvent.preventDefault();
+}
+
+EvoEditor.AfterInputEvent = function(inputEvent, isWordDelim)
+{
+       var isInsertParagraph = inputEvent.inputType == "insertParagraph";
+       var selection = document.getSelection();
+
+       if (isInsertParagraph && selection.isCollapsed && selection.anchorNode && 
selection.anchorNode.tagName == "BODY") {
+               document.execCommand("insertHTML", false, EvoEditor.emptyParagraphAsHtml());
+               EvoUndoRedo.GroupTopRecords(2, "insertParagraph::withFormat");
+               return;
+       }
+
+       // make sure there's always a DIV in the body (like after 'select all' followed by 'delete')
+       if (!document.body.childNodes.length || (document.body.childNodes.length == 1 && 
document.body.childNodes[0].tagName == "BR")) {
+               document.execCommand("insertHTML", false, EvoEditor.emptyParagraphAsHtml());
+               EvoUndoRedo.GroupTopRecords(2, inputEvent.inputType + "::fillEmptyBody");
+               return;
+       }
+
+       if (isInsertParagraph && selection.isCollapsed && selection.anchorNode && 
selection.anchorNode.tagName == "SPAN" &&
+           selection.anchorNode.children.length == 1 && selection.anchorNode.firstElementChild.tagName == 
"BR" &&
+           selection.anchorNode.parentElement.tagName == "DIV") {
+               // new paragraph in UL/OL creates: <div><span style='white-space: normal;'><br></span><div>
+               // thus avoid the <span />, which is not expected in the EvoEditor
+               var node = selection.anchorNode;
+
+               while (node.firstChild) {
+                       node.parentElement.insertBefore(node.firstChild, node);
+               }
+
+               selection.setPosition(node.parentElement, 0);
+               node.remove();
+       }
+
+       if (isInsertParagraph && selection.isCollapsed && selection.anchorNode && 
selection.anchorNode.tagName == "DIV") {
+               // for example when moving away from ul/ol, the newly created
+               // paragraph can inherit styles from it, which is also negative text-indent
+               selection.anchorNode.style.textIndent = "";
+               EvoEditor.removeEmptyStyleAttribute(selection.anchorNode);
+               EvoEditor.maybeUpdateParagraphWidth(selection.anchorNode);
+
+               // it can be inherited, which is not desired when the user edits the content of it
+               if (selection.anchorNode.className == "-x-evo-top-signature-spacer")
+                       selection.anchorNode.removeAttribute("class");
+       }
+
+       // inserting paragraph in BLOCKQUOTE creates a new BLOCKQUOTE without <DIV> inside it
+       if (isInsertParagraph && selection.isCollapsed && selection.anchorNode && 
(selection.anchorNode.tagName == "BLOCKQUOTE" ||
+           (selection.anchorNode.nodeType == selection.anchorNode.TEXT_NODE && 
selection.anchorNode.parentElement &&
+            selection.anchorNode.parentElement.tagName == "BLOCKQUOTE"))) {
+               var blockquoteNode = selection.anchorNode;
+
+               if (blockquoteNode.nodeType == blockquoteNode.TEXT_NODE)
+                       blockquoteNode = blockquoteNode.parentElement;
+
+               if (!blockquoteNode.firstChild || !EvoEditor.IsBlockNode(blockquoteNode.firstChild)) {
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "blockquoteFix", 
blockquoteNode, blockquoteNode,
+                               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+                       try {
+                               var divNode = document.createElement("DIV");
+
+                               while (blockquoteNode.firstChild) {
+                                       divNode.appendChild(blockquoteNode.firstChild);
+                               }
+
+                               blockquoteNode.appendChild(divNode);
+                               EvoEditor.maybeUpdateParagraphWidth(divNode);
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "blockquoteFix");
+                               EvoUndoRedo.GroupTopRecords(2, "insertParagraph::blockquoteFix");
+                               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                               EvoEditor.EmitContentChanged();
+                       }
+               }
+       }
+
+       // special editing of blockquotes
+       if (selection.isCollapsed && (inputEvent.inputType.startsWith("insert") || 
inputEvent.inputType.startsWith("delete"))) {
+               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT && inputEvent.inputType.startsWith("delete")) 
{
+                       var didRemove = 0;
+
+                       didRemove += EvoEditor.removeEmptyElements("DIV");
+                       didRemove += EvoEditor.removeEmptyElements("PRE");
+
+                       if (didRemove)
+                               EvoUndoRedo.GroupTopRecords(didRemove + 1, inputEvent.inputType + 
"::removeEmptyElems");
+               }
+
+               if (EvoEditor.hasElementWithTagNameAsParent(selection.anchorNode, "BLOCKQUOTE")) {
+                       // insertParagraph should split the blockquote into two
+                       if (isInsertParagraph) {
+                               var node = selection.anchorNode, childNode = node, parent, removeNode = null;
+
+                               for (parent = node.parentElement; parent && parent.tagName != "BODY"; parent 
= parent.parentElement) {
+                                       if (parent.tagName == "BLOCKQUOTE") {
+                                               childNode = parent;
+                                       }
+                               }
+
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "blockquoteSplit", 
childNode, childNode,
+                                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+                               try {
+                                       if (node.nodeType == node.ELEMENT_NODE && node.childNodes.length == 1 
&& node.firstChild.tagName == "BR")
+                                               removeNode = node;
+                                       else if (node.nodeType == node.ELEMENT_NODE && node.childNodes.length 
1 && node.firstChild.tagName == "BR")
+                                               removeNode = node.firstChild;
+
+                                       childNode = node;
+
+                                       for (parent = node.parentElement; parent && parent.tagName != "BODY"; 
parent = parent.parentElement) {
+                                               if (parent.tagName == "BLOCKQUOTE") {
+                                                       childNode = EvoEditor.splitAtChild(parent, childNode);
+                                                       parent = childNode;
+                                               } else {
+                                                       childNode = parent;
+                                               }
+                                       }
+
+                                       if (parent) {
+                                               var divNode = document.createElement("DIV");
+                                               divNode.appendChild(document.createElement("BR"));
+                                               parent.insertBefore(divNode, childNode);
+                                               document.getSelection().setPosition(divNode, 0);
+                                               EvoEditor.maybeUpdateParagraphWidth(divNode);
+                                       }
+
+                                       while (removeNode && removeNode.tagName != "BODY") {
+                                               node = removeNode.parentElement;
+                                               node.removeChild(removeNode);
+
+                                               if (node.childNodes.length)
+                                                       break;
+
+                                               removeNode = node;
+                                       }
+
+                                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                                               node = document.getSelection().anchorNode;
+
+                                               if (node && node.nextElementSibling) {
+                                                       var blockquoteLevel = 
(node.nextElementSibling.tagName == "BLOCKQUOTE" ? 1 : 0);
+
+                                                       EvoEditor.removeQuoteMarks(node.nextElementSibling);
+                                                       EvoEditor.convertParagraphs(node.nextElementSibling, 
blockquoteLevel,
+                                                               EvoEditor.NORMAL_PARAGRAPH_WIDTH - 
(blockquoteLevel * 2), false);
+                                               }
+
+                                               if (node && node.previousElementSibling) {
+                                                       var blockquoteLevel = 
(node.previousElementSibling.tagName == "BLOCKQUOTE" ? 1 : 0);
+
+                                                       
EvoEditor.removeQuoteMarks(node.previousElementSibling);
+                                                       
EvoEditor.convertParagraphs(node.previousElementSibling, blockquoteLevel,
+                                                               EvoEditor.NORMAL_PARAGRAPH_WIDTH - 
(blockquoteLevel * 2), false);
+                                               }
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"blockquoteSplit");
+                                       EvoUndoRedo.GroupTopRecords(2, "insertParagraph::blockquoteSplit");
+                                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                                       EvoEditor.EmitContentChanged();
+                               }
+                       // insertLineBreak should re-quote text in the Plain Text mode
+                       } else if (inputEvent.inputType == "insertLineBreak") {
+                               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                                       var selNode = document.getSelection().anchorNode, node = selNode, 
parent;
+
+                                       while (node && node.tagName != "BODY" && 
!EvoEditor.IsBlockNode(node)) {
+                                               node = node.parentElement;
+                                       }
+
+                                       if (node && node.tagName != "BODY" && selNode.previousSibling && 
selNode.previousSibling.nodeValue == "\n") {
+                                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"requote", node, node,
+                                                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+                                               try {
+                                                       var blockquoteLevel;
+
+                                                       // the "\n" is replaced with full paragraph
+                                                       
selNode.parentElement.removeChild(selNode.previousSibling);
+
+                                                       parent = selNode.parentElement;
+
+                                                       var childNode = selNode;
+
+                                                       while (parent && parent.tagName != "BODY") {
+                                                               childNode = EvoEditor.splitAtChild(parent, 
childNode);
+
+                                                               if (childNode === node || 
EvoEditor.IsBlockNode(parent))
+                                                                       break;
+
+                                                               parent = childNode.parentElement;
+                                                       }
+
+                                                       blockquoteLevel = 
EvoEditor.getBlockquoteLevel(parent);
+
+                                                       EvoEditor.quoteParagraph(childNode, blockquoteLevel, 
EvoEditor.NORMAL_PARAGRAPH_WIDTH - (2 * blockquoteLevel));
+
+                                                       document.getSelection().setPosition(childNode, 0);
+                                               } finally {
+                                                       
EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "requote");
+                                                       EvoUndoRedo.GroupTopRecords(2, 
"insertLineBreak::requote");
+                                                       
EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                                                       EvoEditor.EmitContentChanged();
+                                               }
+                                       }
+                               }
+                       // it's an insert or delete in the blockquote, which means to recalculate where 
quotation marks should be
+                       } else if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                               var node = document.getSelection().anchorNode;
+
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "requote::group");
+                               try {
+                                       var selection = EvoSelection.Store(document);
+
+                                       EvoEditor.removeEmptyElements("DIV");
+                                       EvoEditor.removeEmptyElements("PRE");
+
+                                       node = EvoEditor.requoteNodeParagraph(node);
+
+                                       if (node && inputEvent.inputType.startsWith("delete")) {
+                                               if (node.nextSiblingElement)
+                                                       
EvoEditor.requoteNodeParagraph(node.nextSiblingElement);
+                                               if (node.previousSiblingElement)
+                                                       
EvoEditor.requoteNodeParagraph(node.previousSiblingElement);
+                                       }
+
+                                       EvoSelection.Restore(document, selection);
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, 
"requote::group");
+                                       EvoUndoRedo.GroupTopRecords(2, inputEvent.inputType + "::requote");
+                                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                                       EvoEditor.EmitContentChanged();
+                               }
+                       }
+               }
+       }
+
+       if ((!isInsertParagraph && inputEvent.inputType != "insertText") ||
+           (!(EvoEditor.MAGIC_LINKS && (isWordDelim || isInsertParagraph)) &&
+           !EvoEditor.MAGIC_SMILEYS)) {
+               return;
+       }
+
+       if (!selection.isCollapsed || !selection.anchorNode)
+               return;
+
+       var anchorNode = selection.anchorNode, parentElem;
+
+       if (anchorNode.nodeType != anchorNode.ELEMENT_NODE) {
+               parentElem = anchorNode.parentElement;
+
+               if (!parentElem)
+                       return;
+       } else {
+               parentElem = anchorNode;
+       }
+
+       if (isInsertParagraph) {
+               parentElem = parentElem.previousElementSibling;
+
+               if (!parentElem)
+                       return;
+
+               anchorNode = parentElem.lastChild;
+
+               if (!anchorNode || anchorNode.nodeType != anchorNode.TEXT_NODE)
+                       return;
+       }
+
+       if (!anchorNode.nodeValue)
+               return;
+
+       var canLinks;
+
+       canLinks = EvoEditor.MAGIC_LINKS && (isWordDelim || isInsertParagraph);
+
+       var text = anchorNode.nodeValue, covered = false;
+
+       if (canLinks)
+               covered = EvoEditor.linkifyText(anchorNode, true);
+
+       if (!covered && EvoEditor.MAGIC_SMILEYS) {
+               var matches;
+
+               // the replace call below replaces &nbsp; (0xA0) with regular space
+               matches = EvoEditor.findSmileys(text.replace(/ /g, " "), EvoEditor.UNICODE_SMILEYS);
+               if (matches) {
+                       var ii, sz = matches.length, node, tmpElement = null;
+
+                       if (sz > 1)
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "magicSmiley");
+
+                       try {
+                               // they are ordered from the end already
+                               for (ii = 0; ii < sz; ii++) {
+                                       var match = matches[ii];
+
+                                       if (!match.imageUri || EvoEditor.UNICODE_SMILEYS || EvoEditor.mode != 
EvoEditor.MODE_HTML) {
+                                               node = document.createTextNode(match.text);
+                                       } else {
+                                               if (!tmpElement)
+                                                       tmpElement = document.createElement("SPAN");
+
+                                               tmpElement.innerHTML = 
EvoEditor.createEmoticonHTML(match.text, match.imageUri, match.width, match.height);
+                                               node = tmpElement.firstChild;
+                                       }
+
+                                       anchorNode = EvoEditor.replaceMatchWithNode("magicSmiley", 
anchorNode, match, node, sz == 1, true);
+                               }
+                       } finally {
+                               if (sz > 1) {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "magicSmiley");
+                                       EvoUndoRedo.GroupTopRecords(2);
+                                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                                       EvoEditor.EmitContentChanged();
+                               }
+                       }
+               }
+       }
+}
+
+EvoEditor.getParentElement = function(tagName, fromNode, canClimbUp)
+{
+       var node = fromNode;
+
+       if (!node)
+               node = document.getSelection().focusNode;
+
+       if (!node)
+               node = document.getSelection().anchorNode;
+
+       while (node && node.nodeType != node.ELEMENT_NODE) {
+               node = node.parentElement;
+       }
+
+       if (canClimbUp) {
+               while (node && node.tagName != tagName) {
+                       node = node.parentElement;
+               }
+       }
+
+       if (node && node.tagName == tagName)
+               return node;
+
+       return null;
+}
+
+EvoEditor.storePropertiesSelection = function()
+{
+       EvoEditor.propertiesSelection = EvoSelection.Store(document);
+}
+
+EvoEditor.restorePropertiesSelection = function()
+{
+       if (EvoEditor.propertiesSelection) {
+               var selection = EvoEditor.propertiesSelection;
+
+               EvoEditor.propertiesSelection = null;
+
+               try {
+                       // Ignore any errors here
+                       EvoSelection.Restore(document, selection);
+               } catch (exception) {
+               }
+       }
+}
+
+// returns an array with affected elements, which can be passed to EvoEditor.RestoreCurrentElementAttr()
+EvoEditor.RemoveCurrentElementAttr = function()
+{
+       var nodes, ii, len, elems = [];
+
+       nodes = document.querySelectorAll("[" + EvoEditor.CURRENT_ELEMENT_ATTR + "]");
+       len = nodes ? nodes.length : 0;
+
+       for (ii = 0; ii < len; ii++) {
+               var elem = nodes[len - ii - 1];
+
+               elems[elems.length] = elem;
+               elem.removeAttribute(EvoEditor.CURRENT_ELEMENT_ATTR);
+       }
+
+       return elems;
+}
+
+EvoEditor.RestoreCurrentElementAttr = function(elemsArray)
+{
+       if (elemsArray) {
+               var ii;
+
+               for (ii = 0; ii < elemsArray.length; ii++) {
+                       elemsArray[ii].setAttribute(EvoEditor.CURRENT_ELEMENT_ATTR, "1");
+               }
+       }
+}
+
+EvoEditor.getCurrentElement = function()
+{
+       return document.querySelector("[" + EvoEditor.CURRENT_ELEMENT_ATTR + "]");
+}
+
+EvoEditor.setCurrentElement = function(element)
+{
+       EvoEditor.RemoveCurrentElementAttr();
+
+       if (element)
+               element.setAttribute(EvoEditor.CURRENT_ELEMENT_ATTR, "1");
+}
+
+EvoEditor.OnDialogOpen = function(name)
+{
+       EvoEditor.propertiesSelection = null;
+
+       EvoEditor.RemoveCurrentElementAttr();
+
+       var node = null;
+
+       if (name == "link" || name == "cell" || name == "page") {
+               EvoEditor.storePropertiesSelection();
+
+               if (name == "cell") {
+                       var tdnode, thnode;
+
+                       tdnode = (EvoEditor.contextMenuNode && EvoEditor.contextMenuNode.tagName == "TD") ? 
EvoEditor.contextMenuNode : EvoEditor.getParentElement("TD", null, false);
+                       thnode = (EvoEditor.contextMenuNode && EvoEditor.contextMenuNode.tagName == "TH") ? 
EvoEditor.contextMenuNode : EvoEditor.getParentElement("TH", null, false);
+
+                       if (tdnode === EvoEditor.contextMenuNode) {
+                               node = tdnode;
+                       } else if (thnode === EvoEditor.contextMenuNode) {
+                               node = thnode;
+                       } else if (tdnode && thnode) {
+                               for (node = thnode; node; node = node.parentElement) {
+                                       if (node === tdnode) {
+                                               // TH is a child of TD
+                                               node = thnode;
+                                               break;
+                                       }
+                               }
+
+                               if (!node)
+                                       node = tdnode;
+                       } else {
+                               node = tdnode ? tdnode : thnode;
+                       }
+
+                       if (node)
+                               EvoEditor.setCurrentElement(node);
+               }
+
+               if (name == "cell" || name == "page")
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "Dialog::" + name);
+       } else if (name == "hrule" || name == "image" || name == "table") {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "Dialog::" + name);
+
+               if (name == "hrule") {
+                       node = (EvoEditor.contextMenuNode && EvoEditor.contextMenuNode.tagName == "HR") ? 
EvoEditor.contextMenuNode : EvoEditor.getParentElement("HR", null, false);
+               } else if (name == "image") {
+                       node = (EvoEditor.contextMenuNode && EvoEditor.contextMenuNode.tagName == "IMG") ? 
EvoEditor.contextMenuNode : EvoEditor.getParentElement("IMG", null, false);
+               } else if (name == "table") {
+                       node = (EvoEditor.contextMenuNode && EvoEditor.contextMenuNode.tagName == "TABLE") ? 
EvoEditor.contextMenuNode : EvoEditor.getParentElement("TABLE", null, true);
+               }
+
+               if (node) {
+                       EvoEditor.setCurrentElement(node);
+               } else {
+                       if (name == "hrule")
+                               EvoEditor.InsertHTML("CreateHRule", "<HR " + EvoEditor.CURRENT_ELEMENT_ATTR + 
"=\"1\">");
+                       else if (name == "image")
+                               EvoEditor.InsertHTML("CreateImage", "<IMG " + EvoEditor.CURRENT_ELEMENT_ATTR 
+ "=\"1\">");
+                       else if (name == "table")
+                               EvoEditor.InsertHTML("CreateTable", "<TABLE " + 
EvoEditor.CURRENT_ELEMENT_ATTR + "=\"1\"></TABLE>");
+               }
+       }
+
+       node = EvoEditor.getCurrentElement();
+
+       if (node && name != "table" && name != "cell" && name != "image") {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, "Dialog::" + name + "::event", node, 
node,
+                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+               EvoUndoRedo.Disable();
+       }
+}
+
+EvoEditor.OnDialogClose = function(name)
+{
+       if (name == "link" || name == "cell")
+               EvoEditor.restorePropertiesSelection();
+       else
+               EvoEditor.propertiesSelection = null;
+
+       EvoEditor.contextMenuNode = null;
+
+       var node = EvoEditor.getCurrentElement();
+
+       EvoEditor.RemoveCurrentElementAttr();
+
+       if (node && name != "table" && name != "cell" && name != "image") {
+               EvoUndoRedo.Enable();
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, "Dialog::" + name + "::event");
+       }
+
+       if (name == "hrule" || name == "image" || name == "table" || name == "cell" || name == "page")
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "Dialog::" + name);
+}
+
+EvoEditor.applySetAttribute = function(record, isUndo)
+{
+       var element = EvoSelection.FindElementByPath(document.body, record.path);
+
+       if (!element)
+               throw "EvoEditor.applySetAttribute: Path not found";
+
+       var value;
+
+       if (isUndo)
+               value = record.beforeValue;
+       else
+               value = record.afterValue;
+
+       if (value == null)
+               element.removeAttribute(record.attrName);
+       else
+               element.setAttribute(record.attrName, value);
+}
+
+EvoEditor.setAttributeWithUndoRedo = function(opTypePrefix, element, name, value)
+{
+       if (!element)
+               return false;
+
+       if ((value == null && !element.hasAttribute(name)) ||
+           (value != null && value == element.getAttribute(name)))
+               return false;
+
+       var record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opTypePrefix + "::" + name, 
element, element, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+
+       try {
+               if (record) {
+                       record.path = EvoSelection.GetChildPath(document.body, element);
+                       record.attrName = name;
+                       record.beforeValue = element.hasAttribute(name) ? element.getAttribute(name) : null;
+                       record.afterValue = value;
+                       record.apply = EvoEditor.applySetAttribute;
+               }
+
+               if (value == null) {
+                       element.removeAttribute(name);
+               } else {
+                       element.setAttribute(name, value);
+               }
+
+               if (record && record.beforeValue == record.afterValue) {
+                       record.ignore = true;
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opTypePrefix + "::" + name);
+
+               if (!EvoUndoRedo.IsRecording()) {
+                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                       EvoEditor.EmitContentChanged();
+               }
+       }
+
+       return true;
+}
+
+EvoEditor.addElementWithUndoRedo = function(opType, tagName, fillNodeFunc, parent, insertBefore, 
contentArray)
+{
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType, parent, parent, 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       try {
+               var selectionUpdater = EvoSelection.CreateUpdaterObject(), node;
+
+               node = document.createElement(tagName);
+
+               if (fillNodeFunc)
+                       fillNodeFunc(node);
+
+               parent.insertBefore(node, insertBefore);
+
+               if (contentArray) {
+                       var ii;
+
+                       for (ii = 0; ii < contentArray.length; ii++) {
+                               node.append(contentArray[ii]);
+                       }
+               }
+
+               selectionUpdater.restore();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.removeElementWithUndoRedo = function(opType, element)
+{
+       if (element) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType, element.parentElement, 
element.parentElement, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+               try {
+                       var selectionUpdater = EvoSelection.CreateUpdaterObject(), firstChild;
+
+                       firstChild = element.firstChild;
+
+                       while (element.firstChild) {
+                               element.parentElement.insertBefore(element.firstChild, element);
+                       }
+
+                       selectionUpdater.beforeRemove(element);
+                       element.remove();
+                       selectionUpdater.afterRemove(firstChild);
+
+                       selectionUpdater.restore();
+               } finally {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType);
+                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                       EvoEditor.EmitContentChanged();
+               }
+       }
+}
+
+// 'value' can be 'null', to remove the attribute
+EvoEditor.DialogUtilsSetAttribute = function(selector, name, value)
+{
+       var element;
+
+       if (selector)
+               element = document.querySelector(selector);
+       else
+               element = EvoEditor.getCurrentElement();
+
+       if (element) {
+               EvoEditor.setAttributeWithUndoRedo("DlgUtilsSetAttribute", element, name, value);
+       }
+}
+
+EvoEditor.DialogUtilsGetAttribute = function(selector, name)
+{
+       var element;
+
+       if (selector)
+               element = document.querySelector(selector);
+       else
+               element = EvoEditor.getCurrentElement();
+
+       if (element && element.hasAttribute(name))
+               return element.getAttribute(name);
+
+       return null;
+}
+
+EvoEditor.DialogUtilsHasAttribute = function(name)
+{
+       var element = EvoEditor.getCurrentElement();
+
+       return element && element.hasAttribute(name);
+}
+
+EvoEditor.LinkGetProperties = function()
+{
+       var res = null, anchor = EvoEditor.getParentElement("A", null, false);
+
+       if (anchor) {
+               res = [];
+               res["href"] = anchor.href;
+               res["text"] = anchor.innerText;
+       } else if (!document.getSelection().isCollapsed && document.getSelection().rangeCount > 0) {
+               var range;
+
+               range = document.getSelection().getRangeAt(0);
+
+               if (range) {
+                       res = [];
+                       res["text"] = range.toString();
+               }
+       }
+
+       return res;
+}
+
+EvoEditor.LinkSetProperties = function(href, text)
+{
+       // The properties dialog can discard selection, thus restore it before doing changes
+       EvoEditor.restorePropertiesSelection();
+
+       var anchor = EvoEditor.getParentElement("A", null, false);
+
+       if (anchor && (anchor.href != href || anchor.innerText != text)) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetLinkValues", anchor, anchor, 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+               try {
+                       if (anchor.href != href)
+                               anchor.href = href;
+                       if (anchor.innerText != text) {
+                               var selection = EvoSelection.Store(document);
+                               anchor.innerText = text;
+                               EvoSelection.Restore(document, selection);
+                       }
+               } finally {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetLinkValues");
+                       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+                       EvoEditor.EmitContentChanged();
+               }
+       } else if (!anchor && href != "" && text != "") {
+               text = text.replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+               href = href.replace(/\&/g, "&amp;").replace(/\"/g, "&quot;");
+
+               EvoEditor.InsertHTML("CreateLink", "<A href=\"" + href + "\">" + text + "</A>");
+       }
+}
+
+EvoEditor.Unlink = function()
+{
+       // The properties dialog can discard selection, thus restore it before doing changes
+       EvoEditor.restorePropertiesSelection();
+
+       var anchor = EvoEditor.getParentElement("A", null, false);
+
+       EvoEditor.removeElementWithUndoRedo("Unlink", anchor);
+}
+
+EvoEditor.ReplaceImageSrc = function(selector, uri)
+{
+       if (!selector)
+               selector = "#x-evo-dialog-current-element";
+
+       var element = document.querySelector(selector);
+
+       if (element) {
+               if (uri) {
+                       var attrName;
+
+                       if (element.tagName == "IMG")
+                               attrName = "src";
+                       else
+                               attrName = "background";
+
+                       EvoEditor.setAttributeWithUndoRedo("ReplaceImageSrc", element, attrName, uri);
+               } else {
+                       if (element.tagName == "IMG") {
+                               EvoEditor.removeElementWithUndoRedo("ReplaceImageSrc", element);
+                       } else {
+                               EvoEditor.setAttributeWithUndoRedo("ReplaceImageSrc", element, "background", 
null);
+                       }
+               }
+       }
+}
+
+EvoEditor.DialogUtilsSetImageUrl = function(href)
+{
+       var element = EvoEditor.getCurrentElement();
+
+       if (element && element.tagName == "IMG") {
+               var anchor = EvoEditor.getParentElement("A", element, true);
+
+               if (anchor) {
+                       if (href && anchor.href != href) {
+                               EvoEditor.setAttributeWithUndoRedo("DialogUtilsSetImageUrl", anchor, "href", 
href);
+                       } else if (!href) {
+                               EvoEditor.removeElementWithUndoRedo("DialogUtilsSetImageUrl::unset", element);
+                       }
+               } else if (href) {
+                       var fillHref = function(node) {
+                               node.href = href;
+                       };
+
+                       EvoEditor.addElementWithUndoRedo("DialogUtilsSetImageUrl", "A", fillHref, 
element.parentElement, element, [ element ]);
+               }
+       }
+}
+
+EvoEditor.DialogUtilsGetImageUrl = function()
+{
+       var element = EvoEditor.getCurrentElement(), res = null;
+
+       if (element && element.tagName == "IMG") {
+               var anchor = EvoEditor.getParentElement("A", element, true);
+
+               if (anchor)
+                       res = anchor.href;
+       }
+
+       return res;
+}
+
+EvoEditor.DialogUtilsGetImageWidth = function(natural)
+{
+       var element = EvoEditor.getCurrentElement(), res = -1;
+
+       if (element && element.tagName == "IMG") {
+               if (natural)
+                       res = element.naturalWidth;
+               else
+                       res = element.width;
+       }
+
+       return res;
+}
+
+EvoEditor.DialogUtilsGetImageHeight = function(natural)
+{
+       var element = EvoEditor.getCurrentElement(), res = -1;
+
+       if (element && element.tagName == "IMG") {
+               if (natural)
+                       res = element.naturalHeight;
+               else
+                       res = element.height;
+       }
+
+       return res;
+}
+
+EvoEditor.dialogUtilsForeachTableScope = function(scope, traversar, opType)
+{
+       var cell = EvoEditor.getCurrentElement();
+
+       if (!cell)
+               throw "EvoEditor.dialogUtilsForeachTableScope: Current cell not found";
+
+       traversar.selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+
+       try {
+               var table = EvoEditor.getParentElement("TABLE", cell, true);
+
+               var rowFunc = function(row, traversar) {
+                       var jj, length = row.cells.length;
+
+                       for (jj = 0; jj < length; jj++) {
+                               var cell = row.cells[length - jj - 1];
+
+                               if (cell && !traversar.exec(cell))
+                                       return false;
+                       }
+
+                       return true;
+               };
+
+               if (scope == EvoEditor.E_CONTENT_EDITOR_SCOPE_CELL) {
+                       traversar.exec(cell);
+               } else if (scope == EvoEditor.E_CONTENT_EDITOR_SCOPE_COLUMN) {
+                       if (table) {
+                               var length = table.rows.length, ii, cellIndex = cell.cellIndex;
+
+                               for (ii = 0; ii < length; ii++) {
+                                       var row = table.rows[length - ii - 1];
+
+                                       if (row && cellIndex < row.cells.length &&
+                                           !traversar.exec(row.cells[cellIndex]))
+                                               break;
+                               }
+                       }
+               } else if (scope == EvoEditor.E_CONTENT_EDITOR_SCOPE_ROW) {
+                       var row = EvoEditor.getParentElement("TR", cell, true);
+
+                       if (row)
+                               rowFunc(row, traversar);
+               } else if (scope == EvoEditor.E_CONTENT_EDITOR_SCOPE_TABLE) {
+                       if (table) {
+                               var length = table.rows.length, ii;
+
+                               for (ii = 0; ii < length; ii++) {
+                                       if (!rowFunc(table.rows[length - ii - 1], traversar))
+                                               break;
+                               }
+                       }
+               }
+
+               try {
+                       traversar.selectionUpdater.restore();
+               } catch (ex) {
+               }
+
+               EvoEditor.dialogUtilsTableEnsureCurrentElement(table);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, opType);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+
+               if (traversar.anyChanged)
+                       EvoEditor.EmitContentChanged();
+       }
+
+       traversar.selectionUpdater = null;
+}
+
+EvoEditor.DialogUtilsTableSetAttribute = function(scope, attrName, attrValue)
+{
+       var traversar = {
+               attrName : attrName,
+               attrValue : attrValue,
+               anyChanged : false,
+
+               exec : function(cell) {
+                       if (EvoEditor.setAttributeWithUndoRedo("", cell, this.attrName, this.attrValue))
+                               this.anyChanged = true;
+
+                       return true;
+               }
+       };
+
+       EvoEditor.dialogUtilsForeachTableScope(scope, traversar, "TableSetAttribute::" + attrName);
+}
+
+EvoEditor.DialogUtilsTableGetCellIsHeader = function()
+{
+       var element = EvoEditor.getCurrentElement();
+
+       return element && element.tagName == "TH";
+}
+
+EvoEditor.DialogUtilsTableSetHeader = function(scope, isHeader)
+{
+       var traversar = {
+               isHeader : isHeader,
+               selectionUpdater : null,
+               anyChanged : false,
+
+               exec : function(cell) {
+                       if ((!this.isHeader && cell.tagName == "TD") ||
+                           (this.isHeader && cell.tagName == "TH"))
+                               return;
+
+                       this.anyChanged = true;
+
+                       var opType = this.isHeader ? "unsetheader" : "setheader";
+
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType, cell, cell,
+                               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                       try {
+                               var node = document.createElement(this.isHeader ? "TH" : "TD");
+
+                               while(cell.firstChild) {
+                                       node.append(cell.firstChild);
+                               }
+
+                               var ii;
+
+                               for (ii = 0; ii < cell.attributes.length; ii++) {
+                                       node.setAttribute(cell.attributes[ii].name, 
cell.attributes[ii].value);
+                               }
+
+                               cell.parentElement.insertBefore(node, cell);
+
+                               if (this.selectionUpdater)
+                                       this.selectionUpdater.beforeRemove(cell);
+
+                               cell.remove();
+
+                               if (this.selectionUpdater)
+                                       this.selectionUpdater.afterRemove(node);
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, opType);
+                       }
+
+                       return true;
+               }
+       };
+
+       EvoEditor.dialogUtilsForeachTableScope(scope, traversar, "DialogUtilsTableSetHeader");
+}
+
+EvoEditor.DialogUtilsTableGetRowCount = function()
+{
+       var element = EvoEditor.getCurrentElement();
+
+       if (!element)
+               return 0;
+
+       element = EvoEditor.getParentElement("TABLE", element, true);
+
+       if (!element)
+               return 0;
+
+       return element.rows.length;
+}
+
+EvoEditor.dialogUtilsTableEnsureCurrentElement = function(table)
+{
+       if (table && !EvoEditor.getCurrentElement() && table.rows.length > 0) {
+               EvoEditor.setCurrentElement(table.rows[0].cells.length > 0 ? table.rows[0].cells[0] : table);
+       }
+}
+
+EvoEditor.DialogUtilsTableSetRowCount = function(rowCount)
+{
+       var currentElem = EvoEditor.getCurrentElement();
+
+       if (!currentElem)
+               return;
+
+       var table = EvoEditor.getParentElement("TABLE", currentElem, true);
+
+       if (!table || table.rows.length == rowCount)
+               return;
+
+       var selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "DialogUtilsTableSetRowCount", table, table, 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+       try {
+               var ii;
+
+               if (table.rows.length < rowCount) {
+                       var jj, nCells = table.rows.length ? table.rows[0].cells.length : 1;
+
+                       for (ii = table.rows.length; ii < rowCount; ii++) {
+                               var row;
+
+                               row = table.insertRow(-1);
+
+                               for (jj = 0; jj < nCells; jj++) {
+                                       row.insertCell(-1);
+                               }
+                       }
+               } else if (table.rows.length > rowCount) {
+                       for (ii = table.rows.length; ii > rowCount; ii--) {
+                               table.deleteRow(ii - 1);
+                       }
+               }
+
+               try {
+                       // it can fail, due to removed rows
+                       selectionUpdater.restore();
+               } catch (ex) {
+               }
+
+               EvoEditor.dialogUtilsTableEnsureCurrentElement(table);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "DialogUtilsTableSetRowCount");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.DialogUtilsTableGetColumnCount = function()
+{
+       var element = EvoEditor.getCurrentElement();
+
+       if (!element)
+               return 0;
+
+       element = EvoEditor.getParentElement("TABLE", element, true);
+
+       if (!element || !element.rows.length)
+               return 0;
+
+       return element.rows[0].cells.length;
+}
+
+EvoEditor.DialogUtilsTableSetColumnCount = function(columnCount)
+{
+       var currentElem = EvoEditor.getCurrentElement();
+
+       if (!currentElem)
+               return;
+
+       var table = EvoEditor.getParentElement("TABLE", currentElem, true);
+
+       if (!table || !table.rows.length || table.rows[0].cells.length == columnCount)
+               return;
+
+       var selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "DialogUtilsTableSetColumnCount", table, 
table, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+
+       try {
+               var ii, jj;
+
+               for (jj = 0; jj < table.rows.length; jj++) {
+                       var row = table.rows[jj];
+
+                       if (row.cells.length < columnCount) {
+                               for (ii = row.cells.length; ii < columnCount; ii++) {
+                                       row.insertCell(-1);
+                               }
+                       } else if (row.cells.length > columnCount) {
+                               for (ii = row.cells.length; ii > columnCount; ii--) {
+                                       row.deleteCell(ii - 1);
+                               }
+                       }
+               }
+
+               try {
+                       // it can fail, due to removed columns
+                       selectionUpdater.restore();
+               } catch (ex) {
+               }
+
+               EvoEditor.dialogUtilsTableEnsureCurrentElement(table);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "DialogUtilsTableSetColumnCount");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.DialogUtilsTableDeleteCellContent = function()
+{
+       var traversar = {
+               anyChanged : false,
+
+               exec : function(cell) {
+                       if (cell.firstChild) {
+                               this.anyChanged = true;
+
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"subdeletecellcontent", cell, cell, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                               try {
+                                       while (cell.firstChild) {
+                                               if (this.selectionUpdater)
+                                                       this.selectionUpdater.beforeRemove(cell.firstChild);
+
+                                               cell.removeChild(cell.firstChild);
+
+                                               if (this.selectionUpdater)
+                                                       this.selectionUpdater.afterRemove(cell);
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"subdeletecellcontent");
+                               }
+                       }
+
+                       return true;
+               }
+       };
+
+       EvoEditor.dialogUtilsForeachTableScope(EvoEditor.E_CONTENT_EDITOR_SCOPE_CELL, traversar, 
"TableDeleteCellContent");
+}
+
+EvoEditor.DialogUtilsTableDeleteColumn = function()
+{
+       var traversar = {
+               anyChanged : false,
+
+               exec : function(cell) {
+                       this.anyChanged = true;
+
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "subdeletecolumn", cell, cell,
+                               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                       try {
+                               if (this.selectionUpdater) {
+                                       this.selectionUpdater.beforeRemove(cell);
+                                       this.selectionUpdater.afterRemove(cell.nextElementSibling ? 
cell.nextElementSibling : cell.previousElementSibling);
+                               }
+
+                               cell.remove();
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "subdeletecolumn");
+                       }
+
+                       return true;
+               }
+       };
+
+       EvoEditor.dialogUtilsForeachTableScope(EvoEditor.E_CONTENT_EDITOR_SCOPE_COLUMN, traversar, 
"TableDeleteColumn");
+}
+
+EvoEditor.DialogUtilsTableDeleteRow = function()
+{
+       var traversar = {
+               anyChanged : false,
+
+               exec : function(cell) {
+                       this.anyChanged = true;
+
+                       var row = cell.parentElement;
+
+                       if (!row)
+                               return false;
+
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "subdeleterow", row, row,
+                               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                       try {
+                               row.parentElement.deleteRow(row.rowIndex);
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "subdeleterow");
+                       }
+
+                       return false;
+               }
+       };
+
+       EvoEditor.dialogUtilsForeachTableScope(EvoEditor.E_CONTENT_EDITOR_SCOPE_ROW, traversar, 
"TableDeleteColumn");
+}
+
+EvoEditor.DialogUtilsTableDelete = function()
+{
+       var element = EvoEditor.getCurrentElement();
+
+       if (!element)
+               return;
+
+       element = EvoEditor.getParentElement("TABLE", element, true);
+
+       if (!element)
+               return;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "TableDelete", element, element,
+               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       try {
+               element.remove();
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "TableDelete");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+// 'what' can be "column" or "row",
+// 'where' can be lower than 0 for before/above, higher than 0 for after/below
+EvoEditor.DialogUtilsTableInsert = function(what, where)
+{
+       if (what != "column" && what != "row")
+               throw "EvoEditor.DialogUtilsTableInsert: 'what' (" + what + ") can be only 'column' or 'row'";
+       if (!where)
+               throw "EvoEditor.DialogUtilsTableInsert: 'where' cannot be zero";
+
+       var cell, table;
+
+       cell = EvoEditor.getCurrentElement();
+
+       if (!cell)
+               return;
+
+       table = EvoEditor.getParentElement("TABLE", cell, true);
+
+       if (!table)
+               return;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "TableInsert::" + what, table, table,
+               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       try {
+               var index, ii;
+
+               if (what == "column") {
+                       index = cell.cellIndex;
+
+                       if (where > 0)
+                               index++;
+
+                       for (ii = 0; ii < table.rows.length; ii++) {
+                               table.rows[ii].insertCell(index <= table.rows[ii].cells.length ? index : -1);
+                       }
+               } else { // what == "row"
+                       var row = EvoEditor.getParentElement("TR", cell, true);
+
+                       if (row) {
+                               index = row.rowIndex;
+
+                               if (where > 0)
+                                       index++;
+
+                               row = table.insertRow(index <= table.rows.length ? index : -1);
+
+                               for (ii = 0; ii < table.rows[0].cells.length; ii++) {
+                                       row.insertCell(-1);
+                               }
+                       }
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "TableInsert::" + what);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.GetCaretWord = function()
+{
+       if (document.getSelection().rangeCount < 1)
+               return null;
+
+       var range = document.getSelection().getRangeAt(0);
+
+       if (!range)
+               return null;
+
+       range = range.cloneRange();
+       range.expand("word");
+
+       return range.toString();
+}
+
+EvoEditor.replaceSelectionWord = function(opType, expandWord, replacement)
+{
+       if (!expandWord && document.getSelection().isCollapsed)
+               return;
+
+       if (document.getSelection().rangeCount < 1)
+               return;
+
+       var range = document.getSelection().getRangeAt(0);
+
+       if (!range)
+               return;
+
+       if (expandWord)
+               range.expand("word");
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType, null, null, 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       try {
+               var fragment = range.extractContents(), node;
+
+               /* Get the text node to replace and leave other formatting nodes
+                * untouched (font color, boldness, ...). */
+               fragment.normalize();
+
+               for (node = fragment.firstChild; node && node.nodeType != node.TEXT_NODE; node = 
node.firstChild) {
+                       ;
+               }
+
+               if (node && node.nodeType == node.TEXT_NODE && replacement) {
+                       var text;
+
+                       /* Replace the word */
+                       text = document.createTextNode(replacement);
+                       node.parentNode.replaceChild(text, node);
+
+                       /* Insert the word on current location. */
+                       range.insertNode(fragment);
+
+                       document.getSelection().setPosition(text, replacement ? replacement.length : 0);
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType);
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.ReplaceCaretWord = function(replacement)
+{
+       EvoEditor.replaceSelectionWord("ReplaceCaretWord", true, replacement);
+}
+
+EvoEditor.ReplaceSelection = function(replacement)
+{
+       EvoEditor.replaceSelectionWord("ReplaceSelection", false, replacement);
+}
+
+EvoEditor.SpellCheckContinue = function(fromCaret, directionNext)
+{
+       var selection, storedSelection = null;
+
+       selection = document.getSelection();
+
+       if (fromCaret) {
+               storedSelection = EvoSelection.Store(document);
+       } else {
+               if (directionNext) {
+                       selection.modify("move", "left", "documentboundary");
+               } else {
+                       selection.modify ("move", "right", "documentboundary");
+                       selection.modify ("extend", "backward", "word");
+               }
+       }
+
+       var selectWord = function(selection, directionNext) {
+               var anchorNode, anchorOffset;
+
+               anchorNode = selection.anchorNode;
+               anchorOffset = selection.anchorOffset;
+
+               if (directionNext) {
+                       var focusNode, focusOffset;
+
+                       focusNode = selection.focusNode;
+                       focusOffset = selection.focusOffset;
+
+                       /* Jump _behind_ next word */
+                       selection.modify("move", "forward", "word");
+                       /* Jump before the word */
+                       selection.modify("move", "backward", "word");
+                       /* Select it */
+                       selection.modify("extend", "forward", "word");
+
+                       /* If the selection didn't change, then we have most probably reached the end of the 
document. */
+                       return !((anchorNode === selection.anchorNode) &&
+                                (anchorOffset == selection.anchorOffset) &&
+                                (focusNode === selection.focusNode) &&
+                                (focusOffset == selection.focusOffset));
+               } else {
+                       /* Jump on the beginning of current word */
+                       selection.modify("move", "backward", "word");
+                       /* Jump before previous word */
+                       selection.modify("move", "backward", "word");
+                       /* Select it */
+                       selection.modify("extend", "forward", "word");
+
+                       /* If the selection start didn't change, then we have most probably reached the 
beginning of the document. */
+                       return (!(anchorNode === selection.anchorNode)) ||
+                               (anchorOffset != selection.anchorOffset);
+               }
+       };
+
+       while (selectWord(selection, directionNext)) {
+               if (selection.rangeCount < 1)
+                       break;
+
+               var range = selection.getRangeAt(0);
+
+               if (!range)
+                       break;
+
+               var word = range.toString();
+
+               if (!EvoEditor.SpellCheckWord(word)) {
+                       /* Found misspelled word */
+                       return word;
+               }
+       }
+
+       /* Restore the selection to contain the last misspelled word. This is
+        * reached only when we reach the beginning/end of the document */
+       if (storedSelection)
+               EvoSelection.Restore(document, storedSelection);
+
+       return null;
+}
+
+EvoEditor.MoveSelectionToPoint = function(xx, yy, cancel_if_not_collapsed)
+{
+       if (!cancel_if_not_collapsed || document.getSelection().isCollapsed) {
+               var range = document.caretRangeFromPoint(xx, yy);
+
+               document.getSelection().removeAllRanges();
+               document.getSelection().addRange(range);
+       }
+}
+
+EvoEditor.createEmoticonHTML = function(text, imageUri, width, height)
+{
+       if (imageUri.toLowerCase().startsWith("file:"))
+               imageUri = "evo-" + imageUri;
+
+       if (imageUri && EvoEditor.mode == EvoEditor.MODE_HTML && !EvoEditor.UNICODE_SMILEYS)
+               return "<img src=\"" + imageUri + "\" alt=\"" +
+                       text.replace(/\&/g, "&amp;").replace(/\"/g, "&quot;").replace(/\'/g, "&apos;") +
+                       "\" width=\"" + width + "px\" height=\"" + height + "px\">";
+
+       return text;
+}
+
+EvoEditor.InsertEmoticon = function(text, imageUri, width, height)
+{
+       EvoEditor.InsertHTML("InsertEmoticon", EvoEditor.createEmoticonHTML(text, imageUri, width, height));
+}
+
+EvoEditor.InsertImage = function(imageUri, width, height)
+{
+       if (imageUri.toLowerCase().startsWith("file:"))
+               imageUri = "evo-" + imageUri;
+
+       var html = "<img src=\"" + imageUri + "\"";
+
+       if (width > 0 && height > 0) {
+               html += " width=\"" + width + "px\" height=\"" + height + "px\"";
+       }
+
+       html += ">";
+
+       EvoEditor.InsertHTML("InsertImage", html);
+}
+
+EvoEditor.GetCurrentSignatureUid = function()
+{
+       var elem = document.querySelector(".-x-evo-signature[id]");
+
+       if (elem)
+               return elem.id;
+
+       return "";
+}
+
+EvoEditor.removeUnwantedTags = function(parent)
+{
+       if (!parent)
+               return;
+
+       var child, next = null;
+
+       for (child = parent.firstChild; child; child = next) {
+               next = child.nextSibling;
+
+               if (child.tagName == "STYLE" ||
+                   child.tagName == "META")
+                       child.remove();
+       }
+}
+
+EvoEditor.InsertSignature = function(content, isHTML, uid, fromMessage, checkChanged, ignoreNextChange, 
startBottom, topSignature, addDelimiter)
+{
+       var sigSpan, node, scrollX, scrollY;
+
+       scrollX = window.scrollX;
+       scrollY = window.scrollY;
+
+       sigSpan = document.createElement("SPAN");
+       sigSpan.className = "-x-evo-signature";
+       sigSpan.id = uid;
+
+       if (content) {
+               if (isHTML && EvoEditor.mode != EvoEditor.MODE_HTML) {
+                       node = document.createElement("SPAN");
+                       node.innerHTML = content;
+
+                       EvoEditor.removeUnwantedTags(node);
+
+                       content = EvoConvert.ToPlainText(node, EvoEditor.NORMAL_PARAGRAPH_WIDTH);
+                       if (content != "") {
+                               content = "<PRE>" + content.replace(/\&/g, "&amp;").replace(/</g, 
"&lt;").replace(/>/g, "&gt;") + "</PRE>";
+                       }
+
+                       isHTML = 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 (addDelimiter) {
+                       var found;
+
+                       if (isHTML) {
+                               found = content.substr(0, 8).toUpperCase().startsWith("-- <BR>") || 
content.match(/\n-- <BR>/i) != null;
+                       } else {
+                               found = content.startsWith("-- \n") || content.match(/\n-- \n/i) != null;
+                       }
+
+                       /* Skip the delimiter if the signature already has one. */
+                       if (!found) {
+                               /* Always use the HTML delimiter as we are never in anything
+                                * like a strict plain text mode. */
+                               node = document.createElement("PRE");
+                               node.innerHTML = "-- <BR>";
+                               sigSpan.appendChild(node);
+                       }
+               }
+
+               sigSpan.insertAdjacentHTML("beforeend", content);
+
+               node = sigSpan.querySelector("[data-evo-signature-plain-text-mode]");
+               if (node)
+                       node.removeAttribute("[data-evo-signature-plain-text-mode]");
+
+               node = sigSpan.querySelector("#-x-evo-selection-start-marker");
+               if (node)
+                       node.remove();
+
+               node = sigSpan.querySelector("#-x-evo-selection-end-marker");
+               if (node)
+                       node.remove();
+
+               EvoEditor.removeUnwantedTags(sigSpan);
+       }
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "InsertSignature");
+       try {
+               var signatures, ii, done = false, useWrapper = null;
+
+               signatures = document.getElementsByClassName("-x-evo-signature-wrapper");
+               for (ii = signatures.length; ii-- && !done;) {
+                       var wrapper, signature;
+
+                       wrapper = signatures[ii];
+                       signature = wrapper.firstElementChild;
+
+                       /* 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 (fromMessage && content && signature) {
+                               if (checkChanged) {
+                                       /* 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. */
+                                               uid = "none";
+                                               ignoreNextChange = true;
+                                       }
+
+                                       checkChanged = false;
+                                       fromMessage = false;
+                               } else {
+                                       /* Old messages will have the signature id in the name attribute, 
correct it. */
+                                       if (signature.hasAttribute("name")) {
+                                               id = signature.getAttribute("name");
+                                               signature.id = id;
+                                               signature.removeAttribute(name);
+                                       } else {
+                                               id = signature.id;
+                                       }
+
+                                       /* Keep the signature and check if is it the same
+                                        * as the signature in body or the user previously
+                                        * changed it. */
+                                       checkChanged = true;
+                               }
+
+                               done = true;
+                       } else {
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::old-changes", wrapper, wrapper,
+                                       EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+                               try {
+                                       /* If the top signature was set we have to remove the newline
+                                        * that was inserted after it */
+                                       if (topSignature) {
+                                               node = document.querySelector(".-x-evo-top-signature-spacer");
+                                               if (node && (!node.firstChild || !node.textContent ||
+                                                   (node.childNodes.length == 1 && node.firstChild.tagName 
== "BR"))) {
+                                                       node.remove();
+                                               }
+                                       }
+
+                                       /* Leave just one signature wrapper there as it will be reused. */
+                                       if (ii) {
+                                               wrapper.remove();
+                                       } else {
+                                               wrapper.removeChild(signature);
+                                               useWrapper = wrapper;
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::old-changes");
+                               }
+                       }
+               }
+
+               if (!done) {
+                       if (useWrapper) {
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::new-changes", useWrapper, useWrapper, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                               try {
+                                       useWrapper.appendChild(sigSpan);
+
+                                       /* Insert a spacer below the top signature */
+                                       if (topSignature && content) {
+                                               node = document.createElement("DIV");
+                                               node.appendChild(document.createElement("BR"));
+                                               node.className = "-x-evo-top-signature-spacer";
+
+                                               document.body.insertBefore(node, useWrapper.nextSibling);
+
+                                               EvoEditor.maybeUpdateParagraphWidth(node);
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::new-changes");
+                               }
+                       } else {
+                               useWrapper = document.createElement("DIV");
+                               useWrapper.className = "-x-evo-signature-wrapper";
+                               useWrapper.appendChild(sigSpan);
+
+                               EvoEditor.maybeUpdateParagraphWidth(useWrapper);
+
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::new-changes", document.body, document.body, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                               try {
+                                       if (topSignature) {
+                                               document.body.insertBefore(useWrapper, 
document.body.firstChild);
+
+                                               node = document.createElement("DIV");
+                                               node.appendChild(document.createElement("BR"));
+                                               node.className = "-x-evo-top-signature-spacer";
+
+                                               document.body.insertBefore(node, useWrapper.nextSibling);
+                                       } else {
+                                               document.body.appendChild(useWrapper);
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertSignature::new-changes");
+                               }
+                       }
+
+                       fromMessage = false;
+
+                       // Position the caret and scroll to it
+                       if (startBottom) {
+                               if (topSignature) {
+                                       document.getSelection().setPosition(document.body.lastChild, 0);
+                               } else if (useWrapper.previousSibling) {
+                                       document.getSelection().setPosition(useWrapper.previousSibling, 0);
+                               } else {
+                                       document.getSelection().setPosition(useWrapper, 0);
+                               }
+                       } else {
+                               document.getSelection().setPosition(document.body.firstChild, 0);
+                       }
+
+                       node = document.getSelection().anchorNode;
+
+                       if (node) {
+                               if (node.nodeType != node.ELEMENT_NODE)
+                                       node = node.parentElement;
+
+                               if (node && node.scrollIntoViewIfNeeded != undefined)
+                                       node.scrollIntoViewIfNeeded();
+                       }
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "InsertSignature");
+       }
+
+       // the above changes can cause change of the scroll offset, thus restore it
+       window.scrollTo(scrollX, scrollY);
+
+       var res = [];
+
+       res["fromMessage"] = fromMessage;
+       res["checkChanged"] = checkChanged;
+       res["ignoreNextChange"] = ignoreNextChange;
+       res["newUid"] = uid;
+
+       return res;
+}
+
+EvoEditor.isEmptyParagraph = function(node)
+{
+       if (!node || !EvoEditor.IsBlockNode(node))
+               return false;
+
+       return !node.children.length || (node.children.length == 1 && node.children[0].tagName == "BR");
+}
+
+// replaces current selection with the plain text or HTML, quoted or normal DIV
+EvoEditor.InsertContent = function(text, isHTML, quote)
+{
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "InsertContent");
+       try {
+               if (!document.getSelection().isCollapsed) {
+                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "InsertContent::sel-remove", 
null, null,
+                               EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                       try {
+                               document.getSelection().deleteFromDocument();
+                       } finally {
+                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::sel-remove");
+                       }
+               }
+
+               var wasPlain = !isHTML;
+               var content = document.createElement(quote ? "BLOCKQUOTE" : "DIV");
+
+               if (quote) {
+                       content.setAttribute("type", "cite");
+               }
+
+               if (isHTML) {
+                       content.innerHTML = text;
+
+                       // paste can contain <meta> elements, like the one with Content-Type, which can be 
removed
+                       while (content.firstElementChild && content.firstElementChild.tagName == "META") {
+                               content.removeChild(content.firstElementChild);
+                       }
+
+                       // remove comments at the beginning, like the Evolution's "<!-- text/html -->"
+                       while (content.firstChild && content.firstChild.nodeType == 
content.firstChild.COMMENT_NODE) {
+                               content.removeChild(content.firstChild);
+                       }
+
+                       // convert P into DIV
+                       var node = content.firstChild, next;
+
+                       while (node) {
+                               var removeNode = false;
+
+                               if (node.nodeType == node.ELEMENT_NODE && node.tagName == "P") {
+                                       removeNode = true;
+
+                                       if (node.tagName == "P") {
+                                               var div = document.createElement("DIV");
+                                               EvoEditor.moveNodeContent(node, div);
+                                               node.parentElement.insertBefore(div, node.nextSibling);
+                                       } else {
+                                               EvoEditor.moveNodeContent(node);
+                                       }
+                               }
+
+                               next = EvoEditor.getNextNodeInHierarchy(node, content);
+
+                               if (removeNode)
+                                       node.remove();
+
+                               node = next;
+                       }
+
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                               EvoEditor.convertParagraphs(content, quote ? 1 : 0, 
EvoEditor.NORMAL_PARAGRAPH_WIDTH, quote);
+                               content.innerText = EvoConvert.ToPlainText(content, 
EvoEditor.NORMAL_PARAGRAPH_WIDTH);
+                       } else {
+                               EvoEditor.convertParagraphs(content, quote ? 1 : 0, -1, quote);
+                       }
+               } else {
+                       var lines = text.split("\n");
+
+                       if (lines.length == 1 || (lines.length == 2 && !lines[1])) {
+                               content.innerText = lines[0];
+                       } else {
+                               var ii, line, divNode;
+
+                               for (ii = 0; ii < lines.length; ii++) {
+                                       line = lines[ii];
+                                       divNode = document.createElement("DIV");
+
+                                       content.appendChild(divNode);
+
+                                       if (!line.length) {
+                                               divNode.appendChild(document.createElement("BR"));
+                                       } else {
+                                               divNode.innerText = line;
+                                       }
+                               }
+
+                               isHTML = true;
+                       }
+               }
+
+               if (EvoEditor.MAGIC_LINKS) {
+                       var node, next, covered = false;
+
+                       for (node = content.firstChild; node; node = next) {
+                               next = EvoEditor.getNextNodeInHierarchy(node, content);
+
+                               if (node.nodeType == node.TEXT_NODE)
+                                       covered = EvoEditor.linkifyText(node, false) || covered;
+                       }
+
+                       if (covered && !isHTML) {
+                               EvoEditor.convertParagraphs(content, quote ? 1 : 0, EvoEditor.mode == 
EvoEditor.MODE_PLAIN_TEXT ? EvoEditor.NORMAL_PARAGRAPH_WIDTH : -1, quote);
+                               isHTML = true;
+                       }
+               }
+
+               if (quote) {
+                       if (!isHTML)
+                               EvoEditor.convertParagraphs(content, quote ? 1 : 0, EvoEditor.mode == 
EvoEditor.MODE_PLAIN_TEXT ? EvoEditor.NORMAL_PARAGRAPH_WIDTH : -1, quote);
+
+                       var anchorNode = document.getSelection().anchorNode, intoBody = false;
+
+                       if (!content.firstElementChild || (content.firstElementChild.tagName != "DIV" && 
content.firstElementChild.tagName != "P" &&
+                           content.firstElementChild.tagName != "PRE")) {
+                               // enclose quoted text into DIV
+                               var node = document.createElement("DIV");
+
+                               while (content.firstChild) {
+                                       node.appendChild(content.firstChild);
+                               }
+
+                               content.appendChild(node);
+                       }
+
+                       if (anchorNode) {
+                               var node, parentBlock = null;
+
+                               if (anchorNode.nodeType == anchorNode.ELEMENT_NODE) {
+                                       node = anchorNode;
+                               } else {
+                                       node = anchorNode.parentElement;
+                               }
+
+                               while (node && node.tagName != "BODY" && !EvoEditor.IsBlockNode(node)) {
+                                       parentBlock = node;
+
+                                       node = node.parentElement;
+                               }
+
+                               if (node && node.tagName != "BLOCKQUOTE")
+                                       parentBlock = node;
+                               else if (!parentBlock)
+                                       parentBlock = node;
+
+                               if (!parentBlock) {
+                                       intoBody = true;
+                               } else {
+                                       var willSplit = parentBlock.tagName == "DIV" || parentBlock.tagName 
== "P" || parentBlock.tagName == "PRE";
+
+                                       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::text", parentBlock, parentBlock,
+                                               (willSplit ? 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE : 0) | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                                       try {
+                                               if (willSplit) {
+                                                       // need to split the content up to the parent block 
node
+                                                       if (anchorNode.nodeType == anchorNode.TEXT_NODE) {
+                                                               
anchorNode.splitText(document.getSelection().anchorOffset);
+                                                       }
+
+                                                       var from = anchorNode.nextSibling, parent, nextFrom = 
null;
+
+                                                       parent = from ? from.parentElement : 
anchorNode.parentElement;
+
+                                                       if (!from && parent) {
+                                                               from = parent.nextElementSibling;
+                                                               nextFrom = from;
+                                                               parent = parent.parentElement;
+                                                       }
+
+                                                       while (parent && parent.tagName != "BODY") {
+                                                               nextFrom = null;
+
+                                                               if (from) {
+                                                                       var clone;
+
+                                                                       clone = 
from.parentElement.cloneNode(false);
+                                                                       
from.parentElement.parentElement.insertBefore(clone, from.parentElement.nextSibling);
+
+                                                                       nextFrom = clone;
+
+                                                                       while (from.nextSibling) {
+                                                                               
clone.appendChild(from.nextSibling);
+                                                                       }
+
+                                                                       clone.insertBefore(from, 
clone.firstChild);
+                                                               }
+
+                                                               if (parent === parentBlock.parentElement || 
(parent.parentElement && parent.parentElement.tagName == "BLOCKQUOTE")) {
+                                                                       break;
+                                                               }
+
+                                                               from = nextFrom;
+                                                               parent = parent.parentElement;
+                                                       }
+                                               }
+
+                                               parentBlock.insertAdjacentElement("afterend", content);
+
+                                               if (content.nextSibling)
+                                                       
document.getSelection().setPosition(content.nextSibling, 0);
+                                               else if (content.lastChild) {
+                                                       node = content.lastChild;
+
+                                                       while (node.lastChild)
+                                                               node = node.lastChild;
+
+                                                       document.getSelection().setPosition(node, 
node.nodeType == node.TEXT_NODE ? node.nodeValue.length : 0);
+                                               } else
+                                                       document.getSelection().setPosition(content, 0);
+
+                                               if (anchorNode.nodeType == anchorNode.ELEMENT_NODE && 
anchorNode.parentElement &&
+                                                   EvoEditor.isEmptyParagraph(anchorNode)) {
+                                                       anchorNode.remove();
+                                               } else {
+                                                       anchorNode = parentBlock.nextSibling.nextSibling;
+
+                                                       if (anchorNode && anchorNode.nodeType == 
anchorNode.ELEMENT_NODE && anchorNode.parentElement &&
+                                                           EvoEditor.isEmptyParagraph(anchorNode)) {
+                                                               anchorNode.remove();
+                                                       }
+                                               }
+                                       } finally {
+                                               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::text");
+                                       }
+                               }
+                       } else {
+                               intoBody = true;
+                       }
+
+                       if (intoBody) {
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::text", document.body, document.body,
+                                       EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                               try {
+                                       document.body.insertAdjacentElement("afterbegin", content);
+                                       EvoEditor.maybeUpdateParagraphWidth(content);
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::text");
+                               }
+                       }
+
+                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+                               var ii;
+
+                               for (ii = 0; ii < content.children.length; ii++) {
+                                       EvoEditor.requoteNodeParagraph(content.children[ii]);
+                               }
+                       }
+               } else if (isHTML) {
+                       var list, ii;
+
+                       list = content.getElementsByTagName("BLOCKQUOTE");
+
+                       for (ii = 0; ii < list.length; ii++) {
+                               var node = list[ii];
+
+                               node.removeAttribute("class");
+                               node.removeAttribute("style");
+                       }
+
+                       var selection = document.getSelection();
+
+                       var useOuterHTML = !list.length &&
+                               !content.getElementsByTagName("DIV").length &&
+                               !content.getElementsByTagName("PRE").length;
+
+                       if (!useOuterHTML && selection.isCollapsed && selection.focusNode && 
EvoEditor.isEmptyParagraph(selection.focusNode)) {
+                               var node = selection.focusNode, lastNode = null;
+
+                               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::replaceEmptyBlock", node, node,
+                                       EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+                               try {
+                                       if (useOuterHTML) {
+                                               lastNode = content;
+                                               node.parentElement.insertBefore(content, node);
+                                       } else {
+                                               while (content.firstChild) {
+                                                       lastNode = content.firstChild;
+                                                       node.parentElement.insertBefore(content.firstChild, 
node);
+                                               }
+                                       }
+
+                                       node.remove();
+
+                                       if (lastNode) {
+                                               while (lastNode.lastChild) {
+                                                       lastNode = lastNode.lastChild;
+                                               }
+
+                                               selection.setPosition(lastNode, lastNode.nodeType == 
lastNode.TEXT_NODE ? lastNode.nodeValue.length : 0);
+                                       }
+                               } finally {
+                                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, 
"InsertContent::replaceEmptyBlock");
+                               }
+
+                               EvoEditor.correctParagraphsAfterInsertContent("InsertContent::inEmptyBlock");
+                       } else {
+                               useOuterHTML = useOuterHTML && !wasPlain;
+
+                               EvoEditor.InsertHTML("InsertContent::text", useOuterHTML ? content.outerHTML 
: content.innerHTML);
+                       }
+               } else {
+                       EvoEditor.InsertText("InsertContent::text", content.innerText);
+               }
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "InsertContent");
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+               EvoEditor.EmitContentChanged();
+       }
+}
+
+EvoEditor.splitPreTexts = function(node, isInPre, newNodes)
+{
+       if (!node)
+               return;
+
+       isInPre = isInPre || node.tagName == "PRE";
+
+       var currPre = null, child, childIsPre, next;
+
+       for (child = node.firstChild; child; child = next) {
+               childIsPre = child.tagName == "PRE";
+               next = child.nextSibling;
+
+               if (childIsPre || child.tagName == "BLOCKQUOTE") {
+                       currPre = null;
+
+                       var list = [], ii, clone = null;
+
+                       EvoEditor.splitPreTexts(child, isInPre, list);
+
+                       for (ii = 0; ii < list.length; ii++) {
+                               if (childIsPre) {
+                                       newNodes[newNodes.length] = list[ii];
+                               } else {
+                                       if (!clone) {
+                                               clone = child.cloneNode(false);
+                                               newNodes[newNodes.length] = clone;
+                                       }
+
+                                       clone.appendChild(list[ii]);
+                               }
+                       }
+               } else if (isInPre && child.nodeType == node.TEXT_NODE) {
+                       var text = child.nodeValue, pre, ii, lines;
+
+                       lines = text.split("\n");
+
+                       for (ii = 0; ii < lines.length; ii++) {
+                               var line = lines[ii].replace(/\r/g, "");
+
+                               // <pre> is shown as a block, thus adding a new line at the end behaves like 
two <br>-s
+                               if (!line && ii + 1 >= lines.length) {
+                                       if (ii > 0)
+                                               currPre = null;
+                                       break;
+                               }
+
+                               if (ii == 0 && currPre) {
+                                       if (line)
+                                               currPre.appendChild(document.createTextNode(line));
+                                       if (lines.length > 1)
+                                               currPre = null;
+                                       continue;
+                               }
+
+                               pre = document.createElement("PRE");
+
+                               if (line) {
+                                       pre.innerText = line;
+                               } else {
+                                       pre.appendChild(document.createElement("BR"));
+                               }
+
+                               currPre = pre;
+                               newNodes[newNodes.length] = pre;
+                       }
+               } else if (currPre && child.tagName == "BR") {
+                       currPre = null;
+               } else {
+                       child.remove();
+
+                       if (currPre) {
+                               currPre.appendChild(child);
+                       } else if (isInPre) {
+                               currPre = document.createElement("PRE");
+                               currPre.appendChild(child);
+
+                               newNodes[newNodes.length] = currPre;
+                       } else {
+                               newNodes[newNodes.length] = child;
+                       }
+               }
+       }
+}
+
+EvoEditor.processLoadedContent = function()
+{
+       if (!document.body)
+               return;
+
+       var node, didCite, ii, list;
+
+       if (!document.body.hasAttribute("data-evo-draft") && document.querySelector("PRE")) {
+               var next, replacement;
+
+               document.body.normalize();
+
+               for (node = document.body.firstChild; node; node = next) {
+                       next = node.nextSibling;
+
+                       if (node.tagName == "PRE" || node.tagName == "BLOCKQUOTE") {
+                               list = [];
+
+                               EvoEditor.splitPreTexts(node, false, list);
+
+                               for (ii = 0; ii < list.length; ii++) {
+                                       node.parentElement.insertBefore(list[ii], node);
+                               }
+
+                               node.remove();
+                       }
+               }
+       }
+
+       node = document.querySelector("SPAN.-x-evo-cite-body");
+
+       didCite = node;
+
+       if (node)
+               node.remove();
+
+       if (didCite) {
+               didCite = document.createElement("BLOCKQUOTE");
+               didCite.setAttribute("type", "cite");
+
+               while (document.body.firstChild) {
+                       didCite.appendChild(document.body.firstChild);
+               }
+
+               var next;
+
+               // Evolution builds HTML with insignificant "\n", thus remove them first
+               for (node = didCite.firstChild; node; node = next) {
+                       next = EvoEditor.getNextNodeInHierarchy(node, didCite);
+
+                       if (node.nodeType == node.TEXT_NODE && node.nodeValue && node.nodeValue.charAt(0) == 
'\n' && (
+                           (node.previousSibling && EvoEditor.IsBlockNode(node.previousSibling)) ||
+                           (!node.previousSibling && node.parentElement.tagName == "BLOCKQUOTE" && 
!(node.parentElement === didCite)))) {
+                               node.nodeValue = node.nodeValue.substr(1);
+                       }
+               }
+
+               document.body.appendChild(didCite);
+       }
+
+       list = document.querySelectorAll("STYLE[id]");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               node = list[ii];
+
+               if (node.id && node.id.startsWith("-x-evo-"))
+                       node.remove();
+       }
+
+       list = document.querySelectorAll("DIV[data-headers]");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               node = list[ii];
+
+               node.removeAttribute("data-headers");
+
+               document.body.insertAdjacentElement("afterbegin", node);
+       }
+
+       list = document.querySelectorAll("SPAN.-x-evo-to-body[data-credits]");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               node = list[ii];
+
+               var credits = node.getAttribute("data-credits");
+               if (credits) {
+                       var elem;
+
+                       elem = document.createElement("DIV");
+                       elem.innerText = credits;
+
+                       document.body.insertAdjacentElement("afterbegin", elem);
+                       EvoEditor.maybeUpdateParagraphWidth(elem);
+               }
+
+               node.remove();
+       }
+
+       list = document.querySelectorAll(".-x-evo-paragraph");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               node = list[ii];
+               node.removeAttribute("class");
+       }
+
+       list = document.querySelectorAll("[data-evo-paragraph]");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               list[ii].removeAttribute("data-evo-paragraph");
+       }
+
+       // require blocks under BLOCKQUOTE and style them properly
+       list = document.getElementsByTagName("BLOCKQUOTE");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               var blockquoteNode = list[ii], addingTo = null, next;
+
+               for (node = blockquoteNode.firstChild; node; node = next) {
+                       next = node.nextSibling;
+
+                       if (!EvoEditor.IsBlockNode(node) && (node.nodeType == node.ELEMENT_NODE || 
(node.nodeValue && node.nodeValue != "\n" && node.nodeValue != "\r\n"))) {
+                               if (!addingTo) {
+                                       addingTo = 
document.createElement(EvoEditor.hasElementWithTagNameAsParent(node, "PRE") ? "PRE" : "DIV");
+                                       blockquoteNode.insertBefore(addingTo, node);
+                                       EvoEditor.maybeUpdateParagraphWidth(addingTo);
+                               }
+
+                               addingTo.appendChild(node);
+                       } else {
+                               addingTo = null;
+                       }
+               }
+
+               blockquoteNode.removeAttribute("class");
+               blockquoteNode.removeAttribute("style");
+       }
+
+       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT) {
+               EvoEditor.convertParagraphs(document.body, 0, EvoEditor.NORMAL_PARAGRAPH_WIDTH, didCite);
+
+               if (EvoEditor.MAGIC_LINKS) {
+                       var next;
+
+                       for (node = document.body.firstChild; node; node = next) {
+                               next = EvoEditor.getNextNodeInHierarchy(node, null);
+
+                               if (node.nodeType == node.TEXT_NODE)
+                                       EvoEditor.linkifyText(node, false);
+                       }
+               }
+       }
+
+       // remove comments at the beginning, like the Evolution's "<!-- text/html -->"
+       while (document.documentElement.firstChild && document.documentElement.firstChild.nodeType == 
document.documentElement.firstChild.COMMENT_NODE) {
+               document.documentElement.removeChild(document.documentElement.firstChild);
+       }
+
+       document.body.removeAttribute("data-evo-draft");
+       document.body.removeAttribute("data-evo-plain-text");
+       document.body.removeAttribute("spellcheck");
+
+       list = document.querySelectorAll("[id=-x-evo-input-start]");
+
+       for (ii = list.length - 1; ii >= 0; ii--) {
+               node = list[ii];
+               node.removeAttribute("id");
+
+               document.getSelection().setPosition(node, 0);
+               node.scrollIntoView();
+       }
+
+       if (EvoEditor.START_BOTTOM) {
+               var node = document.createElement("DIV");
+
+               node.appendChild(document.createElement("BR"));
+               document.body.appendChild(node);
+               EvoEditor.maybeUpdateParagraphWidth(node);
+
+               document.getSelection().setPosition(node, 0);
+               node.scrollIntoView();
+       }
+}
+
+EvoEditor.LoadHTML = function(html)
+{
+       EvoUndoRedo.Disable();
+       try {
+               document.documentElement.innerHTML = html;
+
+               EvoEditor.processLoadedContent();
+               EvoEditor.initializeContent();
+       } finally {
+               EvoUndoRedo.Enable();
+               EvoUndoRedo.Clear();
+       }
+}
+
+EvoEditor.wrapParagraph = function(paragraphNode, maxLetters, currentPar, usedLetters, wasNestedElem)
+{
+       var child = paragraphNode.firstChild, nextChild, appendBR;
+
+       while (child) {
+               appendBR = false;
+
+               if (child.nodeType == child.TEXT_NODE) {
+                       var text = child.nodeValue;
+
+                       // merge consecutive text nodes into one (similar to paragraphNode.normalize())
+                       while (child.nextSibling && child.nextSibling.nodeType == child.TEXT_NODE) {
+                               nextChild = child.nextSibling;
+                               text += nextChild.nodeValue;
+
+                               child.remove();
+
+                               child = nextChild;
+                       }
+
+                       while (text.length + usedLetters > maxLetters) {
+                               var spacePos = text.lastIndexOf(" ", maxLetters - usedLetters);
+
+                               if (spacePos < 0)
+                                       spacePos = text.indexOf(" ");
+
+                               if (spacePos > 0 && (!usedLetters || usedLetters + spacePos <= maxLetters)) {
+                                       var textNode = document.createTextNode(((usedLetters > 0 && 
!wasNestedElem) ? " " : "") +
+                                               text.substr(0, spacePos));
+
+                                       if (currentPar)
+                                               currentPar.appendChild(textNode);
+                                       else
+                                               child.parentElement.insertBefore(textNode, child);
+
+                                       text = text.substr(spacePos + 1);
+                               }
+
+                               if (currentPar)
+                                       currentPar.appendChild(document.createElement("BR"));
+                               else
+                                       child.parentElement.insertBefore(document.createElement("BR"), child);
+
+                               usedLetters = 0;
+
+                               if (spacePos == 0)
+                                       text = text.substr(1);
+                               else if (spacePos < 0)
+                                       break;
+                       }
+
+                       child.nodeValue = ((usedLetters > 0 && !wasNestedElem) ? " " : "") + text;
+                       usedLetters += ((usedLetters > 0 && !wasNestedElem) ? 1 : 0) + text.length;
+
+                       if (usedLetters > maxLetters)
+                               appendBR = true;
+
+                       wasNestedElem = false;
+               } else if (child.tagName == "BR") {
+                       wasNestedElem = false;
+
+                       if (!child.nextSibling) {
+                               return -1;
+                       }
+
+                       if (child.nextSibling.tagName == "BR") {
+                               usedLetters = 0;
+
+                               if (currentPar) {
+                                       var nextSibling = child.nextSibling;
+
+                                       nextChild = child.nextSibling.nextSibling;
+
+                                       currentPar.appendChild(child);
+
+                                       if (usedLetters) {
+                                               currentPar.appendChild(nextSibling);
+                                       } else {
+                                               nextSibling.remove();
+                                       }
+
+                                       child = nextChild;
+                                       continue;
+                               }
+                       } else {
+                               nextChild = child.nextSibling;
+
+                               child.remove();
+
+                               child = nextChild;
+                               continue;
+                       }
+               } else if (child.tagName == "IMG") {
+                       // just skip it, do not count it into the line length
+                       wasNestedElem = false;
+               } else if (child.tagName == "B" ||
+                          child.tagName == "I" ||
+                          child.tagName == "U" ||
+                          child.tagName == "S" ||
+                          child.tagName == "SUB" ||
+                          child.tagName == "SUP" ||
+                          child.tagName == "FONT" ||
+                          child.tagName == "SPAN" ||
+                          child.tagName == "A") {
+                       usedLetters = EvoEditor.wrapParagraph(child, maxLetters, null, usedLetters, true);
+                       if (usedLetters == -1)
+                               usedLetters = 0;
+                       wasNestedElem = true;
+               } else if (child.nodeType == child.ELEMENT_NODE) {
+                       // everything else works like a line stopper, with a new line added after it
+                       appendBR = true;
+                       wasNestedElem = false;
+               }
+
+               nextChild = child.nextSibling;
+
+               if (currentPar)
+                       currentPar.appendChild(child);
+
+               if (appendBR) {
+                       usedLetters = 0;
+
+                       if (nextChild) {
+                               if (currentPar)
+                                       currentPar.appendChild(document.createElement("BR"));
+                               else
+                                       nextChild.parentElement.insertBefore(document.createElement("BR"), 
nextChild);
+                       }
+               }
+
+               child = nextChild;
+       }
+
+       return usedLetters;
+}
+
+EvoEditor.WrapSelection = function()
+{
+       var nodeFrom, nodeTo;
+
+       nodeFrom = EvoEditor.GetParentBlockNode(document.getSelection().anchorNode);
+       nodeTo = EvoEditor.GetParentBlockNode(document.getSelection().focusNode);
+
+       if (!nodeFrom || !nodeTo) {
+               return;
+       }
+
+       if (nodeFrom != nodeTo) {
+               // selection can go from top to bottom, but also from bottom to top; normalize the path order
+               var commonParent = EvoEditor.GetCommonParent(nodeFrom, nodeTo, true), childFrom, childTo, ii, 
sz;
+
+               childFrom = nodeFrom;
+               while (childFrom && childFrom != commonParent && childFrom.parentElement != commonParent) {
+                       childFrom = childFrom.parentElement;
+               }
+
+               childTo = nodeTo;
+               while (childTo && childTo != commonParent && childTo.parentElement != commonParent) {
+                       childTo = childTo.parentElement;
+               }
+
+               if (!childFrom || !childTo) {
+                       throw "EvoEditor.WrapSelection: Should not be reached (childFrom and childTo cannot 
be null)";
+               }
+
+               sz = commonParent.children.length;
+               for (ii = 0; ii < sz; ii++) {
+                       if (commonParent.children[ii] === childFrom) {
+                               nodeFrom = childFrom;
+                               nodeTo = childTo;
+                               break;
+                       } else if (commonParent.children[ii] === childTo) {
+                               nodeFrom = childTo;
+                               nodeTo = childFrom;
+                               break;
+                       }
+               }
+       }
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "WrapSelection", nodeFrom, nodeTo, 
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+       try {
+               var maxLetters, usedLetters, currentPar, lastParTagName = nodeFrom.tagName;
+
+               maxLetters = EvoEditor.NORMAL_PARAGRAPH_WIDTH;
+               usedLetters = 0;
+               currentPar = null;
+
+               while (nodeFrom) {
+                       EvoEditor.removeQuoteMarks(nodeFrom);
+
+                       if (lastParTagName != nodeFrom.tagName) {
+                               lastParTagName = nodeFrom.tagName;
+                               currentPar = null;
+                               usedLetters = 0;
+                       }
+
+                       if (nodeFrom.tagName == "DIV" || nodeFrom.tagName == "P" || nodeFrom.tagName == 
"PRE") {
+                               if (nodeFrom.childNodes.length == 1 && nodeFrom.childNodes[0].tagName == 
"BR") {
+                                       currentPar = null;
+                                       usedLetters = 0;
+                               } else {
+                                       var blockquoteLevel = 0;
+
+                                       if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT)
+                                               blockquoteLevel = EvoEditor.getBlockquoteLevel(nodeFrom);
+
+                                       usedLetters = EvoEditor.wrapParagraph(nodeFrom, maxLetters - (2 * 
blockquoteLevel), currentPar, usedLetters, false);
+
+                                       if (blockquoteLevel)
+                                               EvoEditor.requoteNodeParagraph(nodeFrom);
+
+                                       if (usedLetters == -1) {
+                                               currentPar = null;
+                                               usedLetters = 0;
+                                       } else if (!currentPar) {
+                                               currentPar = nodeFrom;
+                                       }
+                               }
+                       }
+
+                       // cannot break the cycle now, because want to delete the last empty paragraph
+                       var done = nodeFrom === nodeTo;
+
+                       if (!nodeFrom.childNodes.length) {
+                               var node = nodeFrom;
+
+                               nodeFrom = nodeFrom.nextSibling;
+
+                               if (node.parentElement)
+                                       node.remove();
+                       } else {
+                               nodeFrom = nodeFrom.nextSibling;
+                       }
+
+                       if (done)
+                               break;
+               }
+
+               // Place the cursor at the end of the wrapped paragraph(s)
+               if (currentPar)
+                       nodeTo = currentPar;
+
+               while (nodeTo.lastChild) {
+                       nodeTo = nodeTo.lastChild;
+               }
+
+               document.getSelection().setPosition(nodeTo, nodeTo.nodeType == nodeTo.TEXT_NODE ? 
nodeTo.nodeValue.length : 0);
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "WrapSelection");
+       }
+}
+
+EvoEditor.onContextMenu = function(event)
+{
+       var node = event.target;
+
+       if (!node)
+               node = document.getSelection().focusNode;
+       if (!node)
+               node = document.getSelection().anchorNode;
+
+       EvoEditor.contextMenuNode = node;
+
+       var nodeFlags = EvoEditor.E_CONTENT_EDITOR_NODE_UNKNOWN, res;
+
+       while (node && node.tagName != "BODY") {
+               if (node.tagName == "A")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_ANCHOR;
+               else if (node.tagName == "HR")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_H_RULE;
+               else if (node.tagName == "IMG")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_IMAGE;
+               else if (node.tagName == "TABLE")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TABLE;
+               else if (node.tagName == "TD" || node.tagName == "TH")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TABLE_CELL;
+
+               node = node.parentElement;
+       }
+
+       if (!nodeFlags && EvoEditor.contextMenuNode)
+               nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TEXT;
+
+       if (document.getSelection().isCollapsed)
+               nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED;
+
+       res = [];
+
+       res["nodeFlags"] = nodeFlags;
+       res["caretWord"] = EvoEditor.GetCaretWord();
+
+       window.webkit.messageHandlers.contextMenuRequested.postMessage(res);
+}
+
+document.oncontextmenu = EvoEditor.onContextMenu;
+document.onload = EvoEditor.initializeContent;
+
+document.onselectionchange = function() {
+       if (EvoEditor.checkInheritFontsOnChange) {
+               EvoEditor.checkInheritFontsOnChange = false;
+               EvoEditor.maybeReplaceInheritFonts();
+       }
+
+       EvoEditor.maybeUpdateFormattingState(EvoEditor.forceFormatStateUpdate ? EvoEditor.FORCE_YES : 
EvoEditor.FORCE_MAYBE);
+       EvoEditor.forceFormatStateUpdate = false;
+
+       window.webkit.messageHandlers.selectionChanged.postMessage(document.getSelection().isCollapsed);
+};
+
+EvoEditor.initializeContent();
diff --git a/data/webkit/e-selection.js b/data/webkit/e-selection.js
new file mode 100644
index 0000000000..76a0d9fd7b
--- /dev/null
+++ b/data/webkit/e-selection.js
@@ -0,0 +1,435 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2019 Red Hat (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/>.
+ */
+
+'use strict';
+
+/* semi-convention: private functions start with lower-case letter,
+   public functions start with upper-case letter. */
+
+var EvoSelection = {
+};
+
+/* The node path is described as an array of child indexes between parent
+   and the childNode (in this order). */
+EvoSelection.GetChildPath = function(parent, childNode)
+{
+       if (!childNode) {
+               return null;
+       }
+
+       var array = [], node;
+
+       if (childNode.nodeType == childNode.TEXT_NODE) {
+               childNode = childNode.parentElement;
+       }
+
+       for (node = childNode; node && !(node === parent); node = node.parentElement) {
+               var child, index = 0;
+
+               for (child = node.previousElementSibling; child; child = child.previousElementSibling) {
+                       index++;
+               }
+
+               array[array.length] = index;
+       }
+
+       return array.reverse();
+}
+
+/* Finds the element (not node) referenced by the 'path', which had been created
+   by EvoSelection.GetChildPath(). There should be used the same 'parent' element
+   in both calls. */
+EvoSelection.FindElementByPath = function(parent, path)
+{
+       if (!parent || !path) {
+               return null;
+       }
+
+       var ii, child = parent;
+
+       for (ii = 0; ii < path.length; ii++) {
+               var idx = path[ii];
+
+               if (idx < 0 || idx >= child.children.length) {
+                       throw "EvoSelection.FindElementByPath:: Index '" + idx + "' out of range '" + 
child.children.length + "'";
+               }
+
+               child = child.children.item(idx);
+       }
+
+       return child;
+}
+
+/* This is when the text nodes are split, then the text length of
+   the previous text node influences offset of the next node. */
+EvoSelection.GetOverallTextOffset = function(node)
+{
+       if (!node) {
+               return 0;
+       }
+
+       var text_offset = 0, sibling;
+
+       for (sibling = node.previousSibling; sibling; sibling = sibling.previousSibling) {
+               if (sibling.nodeType == sibling.TEXT_NODE) {
+                       text_offset += sibling.textContent.length;
+               }
+       }
+
+       return text_offset;
+}
+
+/* Traverses direct text nodes under element until it reaches the first within
+   the textOffset. */
+EvoSelection.GetTextOffsetNode = function(element, textOffset)
+{
+       if (!element) {
+               return null;
+       }
+
+       var node, adept = null;
+
+       for (node = element.firstChild; node; node = node.nextSibling) {
+               if (node.nodeType == node.TEXT_NODE) {
+                       var txt_len = node.textContent.length;
+
+                       if (textOffset > txt_len) {
+                               textOffset -= txt_len;
+                               adept = node;
+                       } else {
+                               break;
+                       }
+               }
+       }
+
+       return node ? node : (adept ? adept : element);
+}
+
+/* Returns an object, where the current selection in the doc is stored */
+EvoSelection.Store = function(doc)
+{
+       if (!doc || !doc.getSelection()) {
+               return null;
+       }
+
+       var selection = {}, sel = doc.getSelection();
+
+       selection.anchorElem = sel.anchorNode ? EvoSelection.GetChildPath(doc.body, sel.anchorNode) : [];
+       selection.anchorOffset = sel.anchorOffset + EvoSelection.GetOverallTextOffset(sel.anchorNode);
+
+       if (sel.anchorNode && sel.anchorNode.nodeType == sel.anchorNode.ELEMENT_NODE) {
+               selection.anchorIsElement = true;
+       }
+
+       if (!sel.isCollapsed) {
+               selection.focusElem = EvoSelection.GetChildPath(doc.body, sel.focusNode);
+               selection.focusOffset = sel.focusOffset + EvoSelection.GetOverallTextOffset(sel.focusNode);
+
+               if (sel.focusNode && sel.focusNode.nodeType == sel.focusNode.ELEMENT_NODE) {
+                       selection.focusIsElement = true;
+               }
+       }
+
+       return selection;
+}
+
+/* Restores selection in the doc according to the information stored in 'selection',
+   obtained by EvoSelection.Store(). */
+EvoSelection.Restore = function(doc, selection)
+{
+       if (!doc || !selection || !doc.getSelection()) {
+               return;
+       }
+
+       var anchorNode, anchorOffset, focusNode, focusOffset;
+
+       anchorNode = EvoSelection.FindElementByPath(doc.body, selection.anchorElem);
+       anchorOffset = selection.anchorOffset;
+
+       if (!anchorNode) {
+               return;
+       }
+
+       if (!anchorOffset) {
+               anchorOffset = 0;
+       }
+
+       if (!selection.anchorIsElement) {
+               anchorNode = EvoSelection.GetTextOffsetNode(anchorNode, anchorOffset);
+               anchorOffset -= EvoSelection.GetOverallTextOffset(anchorNode);
+       }
+
+       focusNode = EvoSelection.FindElementByPath(doc.body, selection.focusElem);
+       focusOffset = selection.focusOffset;
+
+       if (focusNode) {
+               if (!selection.focusIsElement) {
+                       focusNode = EvoSelection.GetTextOffsetNode(focusNode, focusOffset);
+                       focusOffset -= EvoSelection.GetOverallTextOffset(focusNode);
+               }
+       }
+
+       if (focusNode)
+               doc.getSelection().setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
+       else
+               doc.getSelection().setPosition(anchorNode, anchorOffset);
+}
+
+/* Encodes selection information to a string */
+EvoSelection.ToString = function(selection)
+{
+       if (!selection) {
+               return "";
+       }
+
+       var utils = {
+               arrayToString : function(array) {
+                       var ii, str = "[";
+
+                       if (!array) {
+                               return str + "]";
+                       }
+
+                       for (ii = 0; ii < array.length; ii++) {
+                               if (ii) {
+                                       str += ",";
+                               }
+                               str += array[ii];
+                       }
+
+                       return str + "]";
+               }
+       };
+
+       var str = "", anchorElem, anchorOffset, focusElem, focusOffset;
+
+       anchorElem = selection.anchorElem;
+       anchorOffset = selection.anchorOffset;
+       focusElem = selection.focusElem;
+       focusOffset = selection.focusOffset;
+
+       str += "anchorElem=" + utils.arrayToString(anchorElem);
+       str += " anchorOffset=" + (anchorOffset ? anchorOffset : 0);
+
+       if (selection.anchorIsElement) {
+               str += " anchorIsElement=1";
+       }
+
+       if (focusElem) {
+               str += " focusElem=" + utils.arrayToString(focusElem);
+               str += " focusOffset=" + (focusOffset ? focusOffset : 0);
+
+               if (selection.focusIsElement) {
+                       str += " focusIsElement=1";
+               }
+       }
+
+       return str;
+}
+
+/* Decodes selection information from a string */
+EvoSelection.FromString = function(str)
+{
+       if (!str) {
+               return null;
+       }
+
+       var utils = {
+               arrayFromString : function(str) {
+                       if (!str || !str.startsWith("[") || !str.endsWith("]")) {
+                               return null;
+                       }
+
+                       var ii, array;
+
+                       array = str.substr(1, str.length - 2).split(",");
+
+                       if (!array) {
+                               return null;
+                       }
+
+                       if (array.length == 1 && array[0] == "") {
+                               array.length = 0;
+                       } else {
+                               for (ii = 0; ii < array.length; ii++) {
+                                       array[ii] = parseInt(array[ii], 10);
+
+                                       if (!Number.isInteger(array[ii])) {
+                                               return null;
+                                       }
+                               }
+                       }
+
+                       return array;
+               }
+       };
+
+       var selection = {}, ii, split_str;
+
+       split_str = str.split(" ");
+
+       if (!split_str || !split_str.length) {
+               return null;
+       }
+
+       for (ii = 0; ii < split_str.length; ii++) {
+               var name;
+
+               name = "anchorElem";
+               if (split_str[ii].startsWith(name + "=")) {
+                       selection[name] = utils.arrayFromString(split_str[ii].slice(name.length + 1));
+                       continue;
+               }
+
+               name = "anchorOffset";
+               if (split_str[ii].startsWith(name + "=")) {
+                       var value;
+
+                       value = parseInt(split_str[ii].slice(name.length + 1), 10);
+                       if (Number.isInteger(value)) {
+                               selection[name] = value;
+                       }
+                       continue;
+               }
+
+               name = "anchorIsElement";
+               if (split_str[ii].startsWith(name + "=")) {
+                       var value;
+
+                       value = parseInt(split_str[ii].slice(name.length + 1), 10);
+                       if (Number.isInteger(value) && value == 1) {
+                               selection[name] = true;
+                       }
+                       continue;
+               }
+
+               name = "focusElem";
+               if (split_str[ii].startsWith(name + "=")) {
+                       selection[name] = utils.arrayFromString(split_str[ii].slice(name.length + 1));
+                       continue;
+               }
+
+               name = "focusOffset";
+               if (split_str[ii].startsWith(name + "=")) {
+                       var value;
+
+                       value = parseInt(split_str[ii].slice(name.length + 1), 10);
+                       if (Number.isInteger(value)) {
+                               selection[name] = value;
+                       }
+               }
+
+               name = "focusIsElement";
+               if (split_str[ii].startsWith(name + "=")) {
+                       var value;
+
+                       value = parseInt(split_str[ii].slice(name.length + 1), 10);
+                       if (Number.isInteger(value) && value == 1) {
+                               selection[name] = true;
+                       }
+                       continue;
+               }
+       }
+
+       /* The "anchorElem" is required, the rest is optional */
+       if (!selection.anchorElem)
+               return null;
+
+       return selection;
+}
+
+/* The so-called updater object has several methods to work with when removing
+   elements from the structure, which tries to preserve selection in the new
+   document structure. The methods are:
+
+   beforeRemove(node) - called before going to remove the 'node'
+   afterRemove(newNode) - with what the 'node' from beforeRemove() had been replaced
+   restore() - called at the end, to restore the selection
+ */
+EvoSelection.CreateUpdaterObject = function()
+{
+       var obj = {
+               selectionBefore : null,
+               selectionAnchorNode : null,
+               selectionAnchorOffset : -1,
+               selectionFocusNode : null,
+               selectionFocusOffset : -1,
+               changeAnchor : false,
+               changeFocus : false,
+
+               beforeRemove : function(node) {
+                       this.changeAnchor = false;
+                       this.changeFocus = false;
+
+                       if (this.selectionAnchorNode) {
+                               this.changeAnchor = node === this.selectionAnchorNode ||
+                                       (this.selectionAnchorNode.noteType == 
this.selectionAnchorNode.TEXT_NODE &&
+                                        this.selectionAnchorNode.parentElement === node);
+                       }
+
+                       if (this.selectionFocusNode) {
+                               this.changeFocus = node === this.selectionFocusNode ||
+                                       (this.selectionFocusNode.noteType == 
this.selectionFocusNode.TEXT_NODE &&
+                                        this.selectionFocusNode.parentElement === node);
+                       }
+               },
+
+               afterRemove : function(newNode) {
+                       if (this.changeAnchor) {
+                               this.selectionAnchorNode = newNode;
+                               this.selectionAnchorOffset += EvoSelection.GetOverallTextOffset(newNode);
+                       }
+
+                       if (this.changeFocus) {
+                               this.selectionFocusNode = newNode;
+                               this.selectionFocusOffset += EvoSelection.GetOverallTextOffset(newNode);
+                       }
+
+                       this.changeAnchor = false;
+                       this.changeFocus = false;
+               },
+
+               restore : function() {
+                       if (this.selectionAnchorNode && this.selectionAnchorNode.parentElement) {
+                               var selection = {
+                                       anchorElem : EvoSelection.GetChildPath(document.body, 
this.selectionAnchorNode),
+                                       anchorOffset : this.selectionAnchorOffset
+                               };
+
+                               if (this.selectionFocusNode) {
+                                       selection.focusElem = EvoSelection.GetChildPath(document.body, 
this.selectionFocusNode);
+                                       selection.focusOffset = this.selectionFocusOffset;
+                               }
+
+                               EvoSelection.Restore(document, selection);
+                       } else {
+                               EvoSelection.Restore(document, this.selectionBefore);
+                       }
+               }
+       };
+
+       obj.selectionBefore = EvoSelection.Store(document);
+       obj.selectionAnchorNode = document.getSelection().anchorNode;
+       obj.selectionAnchorOffset = document.getSelection().anchorOffset + 
EvoSelection.GetOverallTextOffset(obj.selectionAnchorNode);
+
+       if (!document.getSelection().isCollapsed) {
+               obj.selectionFocusNode = document.getSelection().focusNode;
+               obj.selectionFocusOffset = document.getSelection().focusOffset + 
EvoSelection.GetOverallTextOffset(obj.selectionFocusNode);
+       }
+
+       return obj;
+}
diff --git a/data/webkit/e-undo-redo.js b/data/webkit/e-undo-redo.js
new file mode 100644
index 0000000000..2092004f7b
--- /dev/null
+++ b/data/webkit/e-undo-redo.js
@@ -0,0 +1,940 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2019 Red Hat (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/>.
+ */
+
+'use strict';
+
+/* semi-convention: private functions start with lower-case letter,
+   public functions start with upper-case letter. */
+
+var EvoUndoRedo = {
+       E_UNDO_REDO_STATE_NONE : 0,
+       E_UNDO_REDO_STATE_CAN_UNDO : 1 << 0,
+       E_UNDO_REDO_STATE_CAN_REDO : 1 << 1,
+
+       stack : {
+               // to not claim changes when none being made
+               state : -1,
+               undoOpType : "",
+               redoOpType : "",
+
+               maybeStateChanged : function() {
+                       var undoRecord, redoRecord, undoAvailable, undoOpType, redoAvailable, redoOpType;
+
+                       undoRecord = EvoUndoRedo.stack.getCurrentUndoRecord();
+                       redoRecord = EvoUndoRedo.stack.getCurrentRedoRecord();
+                       undoAvailable = undoRecord != null;
+                       undoOpType = undoRecord ? undoRecord.opType : "";
+                       redoAvailable = redoRecord != null;
+                       redoOpType = redoRecord ? redoRecord.opType : "";
+
+                       var state = EvoUndoRedo.E_UNDO_REDO_STATE_NONE;
+
+                       if (undoAvailable) {
+                               state |= EvoUndoRedo.E_UNDO_REDO_STATE_CAN_UNDO;
+                       }
+
+                       if (redoAvailable) {
+                               state |= EvoUndoRedo.E_UNDO_REDO_STATE_CAN_REDO;
+                       }
+
+                       if (EvoUndoRedo.state != state ||
+                           EvoUndoRedo.undoOpType != (undoAvailable ? undoOpType : "") ||
+                           EvoUndoRedo.redoOpType != (redoAvailable ? redoOpType : "")) {
+                               EvoUndoRedo.state = state;
+                               EvoUndoRedo.undoOpType = (undoAvailable ? undoOpType : "");
+                               EvoUndoRedo.redoOpType = (redoAvailable ? redoOpType : "");
+
+                               var params = {};
+
+                               params.state = EvoUndoRedo.state;
+                               params.undoOpType = EvoUndoRedo.undoOpType;
+                               params.redoOpType = EvoUndoRedo.redoOpType;
+
+                               window.webkit.messageHandlers.undoRedoStateChanged.postMessage(params);
+                       }
+               },
+
+               MAX_DEPTH : 10000 + 1, /* it's one item less, due to the 'bottom' being always ignored */
+
+               array : [],
+               bottom : 0,
+               top : 0,
+               current : 0,
+
+               clampIndex : function(index) {
+                       index = (index) % EvoUndoRedo.stack.MAX_DEPTH;
+
+                       if (index < 0)
+                               index += EvoUndoRedo.stack.MAX_DEPTH;
+
+                       return index;
+               },
+
+               /* Returns currently active record for Undo operation, or null */
+               getCurrentUndoRecord : function() {
+                       if (EvoUndoRedo.stack.current == EvoUndoRedo.stack.bottom || 
!EvoUndoRedo.stack.array.length ||
+                           EvoUndoRedo.stack.current < 0 || EvoUndoRedo.stack.current > 
EvoUndoRedo.stack.array.length) {
+                               return null;
+                       }
+
+                       return EvoUndoRedo.stack.array[EvoUndoRedo.stack.current];
+               },
+
+               /* Returns currently active record for Redo operation, or null */
+               getCurrentRedoRecord : function() {
+                       if (EvoUndoRedo.stack.current == EvoUndoRedo.stack.top) {
+                               return null;
+                       }
+
+                       var idx = EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current + 1);
+
+                       if (idx < 0 || idx > EvoUndoRedo.stack.array.length) {
+                               return null;
+                       }
+
+                       return EvoUndoRedo.stack.array[idx];
+               },
+
+               /* Clears the undo stack */
+               clear : function() {
+                       EvoUndoRedo.stack.array.length = 0;
+                       EvoUndoRedo.stack.bottom = 0;
+                       EvoUndoRedo.stack.top = 0;
+                       EvoUndoRedo.stack.current = 0;
+
+                       EvoUndoRedo.stack.maybeStateChanged();
+               },
+
+               /* Adds a new record into the stack; if any undo had been made, then
+                  those records are freed. It can also overwrite old undo steps, if
+                  the stack size would overflow MAX_DEPTH. */
+               push : function(record) {
+                       if (!EvoUndoRedo.stack.array.length) {
+                               EvoUndoRedo.stack.array[0] = null;
+                       }
+
+                       var next = EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current + 1);
+
+                       if (EvoUndoRedo.stack.current != EvoUndoRedo.stack.top) {
+                               var tt, bb, cc;
+
+                               tt = EvoUndoRedo.stack.top;
+                               bb = EvoUndoRedo.stack.bottom;
+                               cc = EvoUndoRedo.stack.current;
+
+                               if (bb > tt) {
+                                       tt += EvoUndoRedo.stack.MAX_DEPTH;
+                                       cc += EvoUndoRedo.stack.MAX_DEPTH;
+                               }
+
+                               while (cc + 1 <= tt) {
+                                       EvoUndoRedo.stack.array[EvoUndoRedo.stack.clampIndex(cc + 1)] = null;
+                                       cc++;
+                               }
+                       }
+
+                       if (next == EvoUndoRedo.stack.bottom) {
+                               EvoUndoRedo.stack.bottom = 
EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.bottom + 1);
+                               EvoUndoRedo.stack.array[EvoUndoRedo.stack.bottom] = null;
+                       }
+
+                       EvoUndoRedo.stack.current = next;
+                       EvoUndoRedo.stack.top = next;
+                       EvoUndoRedo.stack.array[next] = record;
+
+                       EvoUndoRedo.stack.maybeStateChanged();
+               },
+
+               /* Moves the 'current' index in the stack and returns the undo record
+                  to be undone; or 'null', when there's no undo record available. */
+               undo : function() {
+                       var record = EvoUndoRedo.stack.getCurrentUndoRecord();
+
+                       if (record) {
+                               EvoUndoRedo.stack.current = 
EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current - 1);
+                       }
+
+                       EvoUndoRedo.stack.maybeStateChanged();
+
+                       return record;
+               },
+
+               /* Moves the 'current' index in the stack and returns the redo record
+                  to be redone; or 'null', when there's no redo record available. */
+               redo : function() {
+                       var record = EvoUndoRedo.stack.getCurrentRedoRecord();
+
+                       if (record) {
+                               EvoUndoRedo.stack.current = 
EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current + 1);
+                       }
+
+                       EvoUndoRedo.stack.maybeStateChanged();
+
+                       return record;
+               },
+
+               pathMatches : function(path1, path2) {
+                       if (!path1)
+                               return !path2;
+                       else if (!path2 || path1.length != path2.length)
+                               return false;
+
+                       var ii;
+
+                       for (ii = 0; ii < path1.length; ii++) {
+                               if (path1[ii] != path2[ii])
+                                       return false;
+                       }
+
+                       return true;
+               },
+
+               topInsertTextAtSamePlace : function() {
+                       if (EvoUndoRedo.stack.current != EvoUndoRedo.stack.top ||
+                           EvoUndoRedo.stack.current == EvoUndoRedo.stack.bottom) {
+                               return false;
+                       }
+
+                       var curr, prev;
+
+                       curr = EvoUndoRedo.stack.array[EvoUndoRedo.stack.current];
+                       prev = EvoUndoRedo.stack.array[EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current 
- 1)];
+
+                       return curr && prev &&
+                               curr.kind == EvoUndoRedo.RECORD_KIND_EVENT &&
+                               curr.opType == "insertText" &&
+                               !curr.selectionBefore.focusElem &&
+                               prev.kind == EvoUndoRedo.RECORD_KIND_EVENT &&
+                               prev.opType == "insertText" &&
+                               !prev.selectionBefore.focusElem &&
+                               curr.firstChildIndex == prev.firstChildIndex &&
+                               curr.restChildrenCount == prev.restChildrenCount &&
+                               curr.selectionBefore.anchorOffset == prev.selectionAfter.anchorOffset &&
+                               EvoUndoRedo.stack.pathMatches(curr.path, prev.path) &&
+                               EvoUndoRedo.stack.pathMatches(curr.selectionBefore.anchorElem, 
prev.selectionAfter.anchorElem);
+               },
+
+               maybeMergeConsecutive : function(skipFirst, opType) {
+                       if (EvoUndoRedo.stack.current != EvoUndoRedo.stack.top ||
+                           EvoUndoRedo.stack.current == EvoUndoRedo.stack.bottom) {
+                               return;
+                       }
+
+                       var ii, from, curr, keep = null;
+
+                       from = EvoUndoRedo.stack.current;
+                       curr = EvoUndoRedo.stack.array[from];
+
+                       if (skipFirst) {
+                               keep = curr;
+                               from = EvoUndoRedo.stack.clampIndex(from - 1);
+                               curr = EvoUndoRedo.stack.array[from];
+                       }
+
+                       if (!curr ||
+                           curr.kind != EvoUndoRedo.RECORD_KIND_EVENT ||
+                           curr.opType != opType ||
+                           curr.selectionBefore.focusElem) {
+                               return;
+                       }
+
+                       for (ii = EvoUndoRedo.stack.clampIndex(from - 1);
+                            ii != EvoUndoRedo.stack.bottom;
+                            ii = EvoUndoRedo.stack.clampIndex(ii - 1)) {
+                               var prev;
+
+                               prev = EvoUndoRedo.stack.array[ii];
+
+                               if (prev.kind != EvoUndoRedo.RECORD_KIND_EVENT ||
+                                   prev.opType != opType ||
+                                   prev.selectionBefore.focusElem ||
+                                   curr.firstChildIndex != prev.firstChildIndex ||
+                                   curr.restChildrenCount != prev.restChildrenCount ||
+                                   curr.selectionBefore.anchorOffset != prev.selectionAfter.anchorOffset ||
+                                   !EvoUndoRedo.stack.pathMatches(curr.path, prev.path) ||
+                                   !EvoUndoRedo.stack.pathMatches(curr.selectionBefore.anchorElem, 
prev.selectionAfter.anchorElem)) {
+                                       break;
+                               }
+
+                               if (opType == "insertText")
+                                       prev.opType = opType + "::merged";
+                               prev.selectionAfter = curr.selectionAfter;
+                               prev.htmlAfter = curr.htmlAfter;
+
+                               curr = prev;
+                               EvoUndoRedo.stack.array[EvoUndoRedo.stack.clampIndex(ii + 1)] = keep;
+                               if (keep) {
+                                       EvoUndoRedo.stack.array[EvoUndoRedo.stack.clampIndex(ii + 2)] = null;
+                               }
+
+                               EvoUndoRedo.stack.top = ii + (keep ? 1 : 0);
+                               EvoUndoRedo.stack.current = EvoUndoRedo.stack.top;
+                       }
+
+                       EvoUndoRedo.stack.maybeStateChanged();
+               },
+
+               maybeMergeInsertText : function(skipFirst) {
+                       EvoUndoRedo.stack.maybeMergeConsecutive(skipFirst, "insertText");
+                       EvoUndoRedo.stack.maybeMergeConsecutive(skipFirst, "insertText::WordDelim");
+               },
+
+               maybeMergeDragDrop : function() {
+                       if (EvoUndoRedo.stack.current != EvoUndoRedo.stack.top ||
+                           EvoUndoRedo.stack.current == EvoUndoRedo.stack.bottom ||
+                           EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current - 1) == 
EvoUndoRedo.stack.bottom) {
+                               return;
+                       }
+
+                       var curr, prev;
+
+                       curr = EvoUndoRedo.stack.array[EvoUndoRedo.stack.current];
+                       prev = EvoUndoRedo.stack.array[EvoUndoRedo.stack.clampIndex(EvoUndoRedo.stack.current 
- 1)];
+
+                       if (curr && prev &&
+                           curr.kind == EvoUndoRedo.RECORD_KIND_EVENT &&
+                           prev.kind == EvoUndoRedo.RECORD_KIND_EVENT &&
+                           curr.opType == "insertFromDrop" &&
+                           prev.opType == "deleteByDrag") {
+                               EvoUndoRedo.GroupTopRecords(2, "dragDrop::merged");
+                       }
+               }
+       },
+
+       RECORD_KIND_EVENT       : 1, /* managed by EvoUndoRedo itself, in DOM events */
+       RECORD_KIND_DOCUMENT    : 2, /* saving whole document */
+       RECORD_KIND_GROUP       : 3, /* not saving anything, just grouping several records together */
+       RECORD_KIND_CUSTOM      : 4, /* custom record */
+
+       /*
+       Record {
+               int kind;               // RECORD_KIND_...
+               string opType;          // operation type, like the one from oninput
+               Array path;             // path to the common parent of the affteded elements
+               int firstChildIndex;    // the index of the first children affeted/recorded
+               int restChildrenCount;  // the Undo/Redo affects only some children, these are those which 
are unaffected after the path
+               Object selectionBefore; // stored selection as it was before the change
+               string htmlBefore;      // affected children before the change; can be null, when inserting 
new nodes
+               Object selectionAfter;  // stored selection as it was after the change
+               string htmlAfter;       // affected children before the change; can be null, when removed old 
nodes
+
+               Array records;          // nested records; can be null or undefined
+       }
+
+       The path, firstChildIndex and restChildrenCount together describe where the changes happened.
+       That is, for example when changing node 'b' into 'x' and 'y':
+          <body>       |   <body>
+            <a/>       |     <a/>
+            <b/>       |     <x/>
+            <c/>       |     <y/>
+            <d/>       |     <c/>
+          </body>      |     <d/>
+                       |   </body>
+       the 'path' points to 'body', the firstChildIndex=1 and restChildrenCount=2. Then undo/redo can
+       delete all nodes between index >= firstChildIndex && index < children.length - restChildrenCount.
+       */
+
+       dropTarget : null, // passed from dropCb() into beforeInputCb()/inputCb() for "insertFromDrop" event
+       disabled : 0,
+       ongoingRecordings : [] // the recordings can be nested
+};
+
+EvoUndoRedo.Attach = function()
+{
+       if (document.documentElement) {
+               document.documentElement.onbeforeinput = EvoUndoRedo.beforeInputCb;
+               document.documentElement.oninput = EvoUndoRedo.inputCb;
+               document.documentElement.ondrop = EvoUndoRedo.dropCb;
+       }
+}
+
+EvoUndoRedo.Detach = function()
+{
+       if (document.documentElement) {
+               document.documentElement.onbeforeinput = null;
+               document.documentElement.oninput = null;
+               document.documentElement.ondrop = null;
+       }
+}
+
+EvoUndoRedo.Enable = function()
+{
+       if (!EvoUndoRedo.disabled) {
+               throw "EvoUndoRedo:: Cannot Enable, when not disabled";
+       }
+
+       EvoUndoRedo.disabled--;
+}
+
+EvoUndoRedo.Disable = function()
+{
+       EvoUndoRedo.disabled++;
+
+       if (!EvoUndoRedo.disabled) {
+               throw "EvoUndoRedo:: Overflow in Disable";
+       }
+}
+
+EvoUndoRedo.isWordDelimEvent = function(inputEvent)
+{
+       return inputEvent.inputType == "insertText" &&
+               inputEvent.data &&
+               inputEvent.data.length == 1 &&
+               (inputEvent.data == " " || inputEvent.data == "\t");
+}
+
+EvoUndoRedo.beforeInputCb = function(inputEvent)
+{
+       if (EvoUndoRedo.disabled) {
+               return;
+       }
+
+       var opType = inputEvent.inputType, record, startNode = null, endNode = null;
+
+       if (EvoUndoRedo.isWordDelimEvent(inputEvent))
+               opType += "::WordDelim";
+
+       if (opType == "insertFromDrop")
+               startNode = EvoUndoRedo.dropTarget;
+
+       if (document.getSelection().isCollapsed) {
+               if (opType == "deleteWordBackward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "backward", "word");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteWordForward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "forward", "word");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteSoftLineBackward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "backward", "line");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteSoftLineForward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "forward", "line");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteEntireSoftLine") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "backward", "line");
+                       startNode = document.getSelection().anchorNode;
+                       document.getSelection().modify("move", "forward", "line");
+                       endNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteHardLineBackward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "backward", "paragraph");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteHardLineForward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "forward", "paragraph");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteContentBackward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "backward", "paragraph");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               } else if (opType == "deleteContentForward") {
+                       var sel = EvoSelection.Store(document);
+                       document.getSelection().modify("move", "forward", "paragraph");
+                       startNode = document.getSelection().anchorNode;
+                       EvoSelection.Restore(document, sel);
+               }
+       }
+
+       record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType, startNode, endNode,
+               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       /* Changing format with collapsed selection doesn't change HTML structure immediately */
+       if (record && opType.startsWith("format") && document.getSelection().isCollapsed) {
+               record.ignore = true;
+       }
+}
+
+EvoUndoRedo.inputCb = function(inputEvent)
+{
+       var isWordDelim = EvoUndoRedo.isWordDelimEvent(inputEvent);
+
+       if (EvoUndoRedo.disabled) {
+               EvoEditor.EmitContentChanged();
+               EvoEditor.AfterInputEvent(inputEvent, isWordDelim);
+               return;
+       }
+
+       var opType = inputEvent.inputType;
+
+       if (isWordDelim)
+               opType += "::WordDelim";
+
+       if (EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, opType)) {
+               EvoEditor.EmitContentChanged();
+
+               EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+       }
+
+       if (!EvoUndoRedo.ongoingRecordings.length && opType == "insertText" &&
+           !EvoUndoRedo.stack.topInsertTextAtSamePlace()) {
+               EvoUndoRedo.stack.maybeMergeInsertText(true);
+       }
+
+       EvoEditor.forceFormatStateUpdate = EvoEditor.forceFormatStateUpdate || opType == "" || 
opType.startsWith("format");
+
+       if (opType == "insertFromDrop") {
+               EvoUndoRedo.dropTarget = null;
+               EvoUndoRedo.stack.maybeMergeDragDrop();
+       }
+
+       EvoEditor.AfterInputEvent(inputEvent, isWordDelim);
+}
+
+EvoUndoRedo.dropCb = function(event)
+{
+       EvoUndoRedo.dropTarget = event.toElement;
+}
+
+EvoUndoRedo.applyRecord = function(record, isUndo, withSelection)
+{
+       if (!record) {
+               return;
+       }
+
+       var kind = record.kind;
+
+       if (kind == EvoUndoRedo.RECORD_KIND_GROUP) {
+               var ii, records;
+
+               records = record.records;
+
+               if (records && records.length) {
+                       for (ii = 0; ii < records.length; ii++) {
+                               EvoUndoRedo.applyRecord(records[isUndo ? (records.length - ii - 1) : ii], 
isUndo, false);
+                       }
+               }
+
+               if (withSelection) {
+                       EvoSelection.Restore(document, isUndo ? record.selectionBefore : 
record.selectionAfter);
+               }
+
+               return;
+       }
+
+       EvoUndoRedo.Disable();
+
+       try {
+               if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+                       if (isUndo) {
+                               document.documentElement.innerHTML = record.htmlBefore;
+                       } else {
+                               document.documentElement.innerHTML = record.htmlAfter;
+                       }
+
+                       if (record.apply != null) {
+                               record.apply(record, isUndo);
+                       }
+               } else if (kind == EvoUndoRedo.RECORD_KIND_CUSTOM && record.apply != null) {
+                       record.apply(record, isUndo);
+               } else {
+                       var commonParent;
+
+                       commonParent = EvoSelection.FindElementByPath(document.body, record.path);
+                       if (!commonParent) {
+                               throw "EvoUndoRedo::applyRecord: Cannot find parent at path " + record.path;
+                       }
+
+                       EvoUndoRedo.RestoreChildren(record, commonParent, isUndo);
+               }
+
+               if (withSelection) {
+                       EvoSelection.Restore(document, isUndo ? record.selectionBefore : 
record.selectionAfter);
+               }
+       } finally {
+               EvoUndoRedo.Enable();
+       }
+}
+
+EvoUndoRedo.StartRecord = function(kind, opType, startNode, endNode, flags)
+{
+       if (EvoUndoRedo.disabled) {
+               return null;
+       }
+
+       var record = {};
+
+       record.kind = kind;
+       record.opType = opType;
+       record.selectionBefore = EvoSelection.Store(document);
+
+       if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+               record.htmlBefore = document.documentElement.innerHTML;
+       } else if (kind != EvoUndoRedo.RECORD_KIND_GROUP) {
+               var affected;
+
+               affected = EvoEditor.ClaimAffectedContent(startNode, endNode, flags);
+
+               record.path = affected.path;
+               record.firstChildIndex = affected.firstChildIndex;
+               record.restChildrenCount = affected.restChildrenCount;
+
+               if ((flags & EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML) != 0)
+                       record.htmlBefore = affected.html;
+       }
+
+       EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length] = record;
+
+       return record;
+}
+
+EvoUndoRedo.StopRecord = function(kind, opType)
+{
+       if (EvoUndoRedo.disabled) {
+               return false;
+       }
+
+       if (!EvoUndoRedo.ongoingRecordings.length) {
+               // Workaround WebKitGTK+ bug not sending beforeInput event when deleting with backspace
+               // https://bugs.webkit.org/show_bug.cgi?id=206341
+               if (opType == "deleteContentBackward")
+                       return false;
+
+               throw "EvoUndoRedo:StopRecord: Nothing is recorded for kind:" + kind + " opType:'" + opType + 
"'";
+       }
+
+       var record = EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1];
+
+       // Events can overlap sometimes, like when doing drag&drop inside web view
+       if (record.kind != kind || record.opType != opType) {
+               var ii;
+
+               for (ii = EvoUndoRedo.ongoingRecordings.length - 2; ii >= 0; ii--) {
+                       record = EvoUndoRedo.ongoingRecordings[ii];
+
+                       if (record.kind == kind && record.opType == opType) {
+                               var jj;
+
+                               for (jj = ii + 1; jj < EvoUndoRedo.ongoingRecordings.length; jj++) {
+                                       EvoUndoRedo.ongoingRecordings[jj - 1] = 
EvoUndoRedo.ongoingRecordings[jj];
+                               }
+
+                               EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1] = 
record;
+                               break;
+                       }
+               }
+       }
+
+       if (record.kind != kind || record.opType != opType) {
+               // The "InsertContent", especially when inserting plain text, can receive multiple 'input' 
events
+               // with "insertParagraph", "insertText" and similar, which do not have its counterpart 
beforeInput event,
+               // thus ignore those
+
+               if (record.opType == "InsertContent" && opType.startsWith("insert"))
+                       return;
+
+               throw "EvoUndoRedo:StopRecord: Mismatch in record kind, expected " + record.kind + " (" + 
record.opType + "), but received " +
+                       kind + "(" + opType + "); ongoing recordings:" + EvoUndoRedo.ongoingRecordings.length;
+       }
+
+       EvoUndoRedo.ongoingRecordings.length = EvoUndoRedo.ongoingRecordings.length - 1;
+
+       // ignore empty group records
+       if (kind == EvoUndoRedo.RECORD_KIND_GROUP && (!record.records || !record.records.length)) {
+               record.ignore = true;
+       }
+
+       if (record.ignore) {
+               if (!EvoUndoRedo.ongoingRecordings.length &&
+                   (record.kind != EvoUndoRedo.RECORD_KIND_EVENT || record.opType != "insertText")) {
+                       EvoUndoRedo.stack.maybeMergeInsertText(false);
+               }
+
+               return false;
+       }
+
+       if (kind == EvoUndoRedo.RECORD_KIND_DOCUMENT) {
+               record.htmlAfter = document.documentElement.innerHTML;
+       } else if (record.htmlBefore != window.undefined) {
+               var commonParent;
+
+               commonParent = EvoSelection.FindElementByPath(document.body, record.path);
+
+               if (!commonParent) {
+                       throw "EvoUndoRedo.StopRecord:: Failed to stop '" + opType + "', cannot find common 
parent";
+               }
+
+               EvoUndoRedo.BackupChildrenAfter(record, commonParent);
+
+               // some formatting commands do not change HTML structure immediately, thus ignore those
+               if (kind == EvoUndoRedo.RECORD_KIND_EVENT && record.htmlBefore == record.htmlAfter) {
+                       if (!EvoUndoRedo.ongoingRecordings.length && record.opType != "insertText") {
+                               EvoUndoRedo.stack.maybeMergeInsertText(false);
+                       }
+
+                       return false;
+               }
+       }
+
+       record.selectionAfter = EvoSelection.Store(document);
+
+       if (EvoUndoRedo.ongoingRecordings.length && 
EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1].kind == 
EvoUndoRedo.RECORD_KIND_GROUP) {
+               var parentRecord = EvoUndoRedo.ongoingRecordings[EvoUndoRedo.ongoingRecordings.length - 1];
+               var records = parentRecord.records;
+
+               if (!records) {
+                       records = [];
+               }
+
+               records[records.length] = record;
+               parentRecord.records = records;
+       } else {
+               EvoUndoRedo.stack.push(record);
+
+               if (record.kind == EvoUndoRedo.RECORD_KIND_EVENT && record.opType == "insertText::WordDelim") 
{
+                       EvoUndoRedo.stack.maybeMergeConsecutive(true, "insertText");
+                       EvoUndoRedo.stack.maybeMergeConsecutive(false, "insertText::WordDelim");
+               } else if (record.kind != EvoUndoRedo.RECORD_KIND_EVENT || record.opType != "insertText") {
+                       EvoUndoRedo.stack.maybeMergeInsertText(true);
+               }
+       }
+
+       return true;
+}
+
+EvoUndoRedo.IsRecording = function()
+{
+       return !EvoUndoRedo.disabled && EvoUndoRedo.ongoingRecordings.length > 0;
+}
+
+EvoUndoRedo.Undo = function()
+{
+       var record = EvoUndoRedo.stack.undo();
+
+       if (!record)
+               return;
+
+       EvoUndoRedo.applyRecord(record, true, true);
+       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_YES);
+       EvoEditor.EmitContentChanged();
+}
+
+EvoUndoRedo.Redo = function()
+{
+       var record = EvoUndoRedo.stack.redo();
+
+       if (!record)
+               return;
+
+       EvoUndoRedo.applyRecord(record, false, true);
+       EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_YES);
+       EvoEditor.EmitContentChanged();
+}
+
+EvoUndoRedo.Clear = function()
+{
+       EvoUndoRedo.stack.clear();
+}
+
+EvoUndoRedo.GroupTopRecords = function(nRecords, opType)
+{
+       if (EvoUndoRedo.disabled)
+               return;
+
+       if (EvoUndoRedo.ongoingRecordings.length)
+               throw "EvoUndoRedo.GroupTopRecords: Cannot be called when there are ongoing recordings";
+
+       var group = {};
+
+       group.kind = EvoUndoRedo.RECORD_KIND_GROUP;
+       group.opType = opType;
+       group.selectionBefore = null;
+       group.selectionAfter = null;
+       group.records = [];
+
+       while (nRecords >= 1) {
+               nRecords--;
+
+               var record = EvoUndoRedo.stack.undo();
+
+               if (!record)
+                       break;
+
+               group.records[group.records.length] = record;
+               group.selectionBefore = record.selectionBefore;
+
+               if (!group.selectionAfter)
+                       group.selectionAfter = record.selectionAfter;
+
+               if (!group.opType)
+                       group.opType = record.opType + "::Grouped";
+       }
+
+       if (group.records.length) {
+               group.records = group.records.reverse();
+
+               EvoUndoRedo.stack.push(group);
+       }
+
+       EvoUndoRedo.stack.maybeStateChanged();
+}
+
+/* Backs up all the children elements between firstChildIndex and lastChildIndex inclusive,
+   saving their HTML content into record.htmlBefore; it stores only element children,
+   not text or other nodes. Use also EvoUndoRedo.BackupChildrenAfter() to save all data
+   needed by EvoUndoRedo.RestoreChildren(), which is used to restore saved data.
+   The firstChildIndex can be -1, to back up parent's innerHTML.
+*/
+EvoUndoRedo.BackupChildrenBefore = function(record, parent, firstChildIndex, lastChildIndex)
+{
+       var currentElemsArray = EvoEditor.RemoveCurrentElementAttr();
+
+       try {
+               record.firstChildIndex = firstChildIndex;
+
+               if (firstChildIndex == -1) {
+                       record.htmlBefore = parent.innerHTML;
+               } else {
+                       record.htmlBefore = "";
+
+                       var ii;
+
+                       for (ii = firstChildIndex; ii < parent.children.length; ii++) {
+                               record.htmlBefore += parent.children[ii].outerHTML;
+
+                               if (ii == lastChildIndex) {
+                                       ii++;
+                                       break;
+                               }
+                       }
+
+                       record.restChildrenCount = parent.children.length - ii;
+               }
+       } finally {
+               EvoEditor.RestoreCurrentElementAttr(currentElemsArray);
+       }
+}
+
+EvoUndoRedo.BackupChildrenAfter = function(record, parent)
+{
+       if (record.firstChildIndex == undefined)
+               throw "EvoUndoRedo.BackupChildrenAfter: 'record' doesn't contain 'firstChildIndex' property";
+       if (record.firstChildIndex != -1 && record.restChildrenCount == undefined)
+               throw "EvoUndoRedo.BackupChildrenAfter: 'record' doesn't contain 'restChildrenCount' 
property";
+       if (record.htmlBefore == undefined)
+               throw "EvoUndoRedo.BackupChildrenAfter: 'record' doesn't contain 'htmlBefore' property";
+
+       var currentElemsArray = EvoEditor.RemoveCurrentElementAttr();
+
+       try {
+               if (record.firstChildIndex == -1) {
+                       record.htmlAfter = parent.innerHTML;
+               } else {
+                       record.htmlAfter = "";
+
+                       var ii, first, last;
+
+                       first = record.firstChildIndex;
+
+                       // it can equal to the children.length, when the node had been removed
+                       if (first < 0 || first > parent.children.length) {
+                               throw "EvoUndoRedo.BackupChildrenAfter: firstChildIndex (" + first + ") out 
of bounds (" + parent.children.length + ")";
+                       }
+
+                       last = parent.children.length - record.restChildrenCount;
+                       if (last < 0 || last < first) {
+                               throw "EvoUndoRedo::BackupChildrenAfter: restChildrenCount (" + 
record.restChildrenCount + ") out of bounds (length:" +
+                                       parent.children.length + " first:" + first + " last:" + last + ")";
+                       }
+
+                       for (ii = first; ii < last; ii++) {
+                               if (ii >= 0 && ii < parent.children.length) {
+                                       record.htmlAfter += parent.children[ii].outerHTML;
+                               }
+                       }
+               }
+       } finally {
+               EvoEditor.RestoreCurrentElementAttr(currentElemsArray);
+       }
+}
+
+// restores content of 'parent' based on the information saved by EvoUndoRedo.BackupChildrenBefore()
+// and EvoUndoRedo.BackupChildrenAfter()
+EvoUndoRedo.RestoreChildren = function(record, parent, isUndo)
+{
+       var first, last, ii;
+
+       if (record.firstChildIndex == undefined)
+               throw "EvoUndoRedo.RestoreChildren: 'record' doesn't contain 'firstChildIndex' property";
+       if (record.firstChildIndex != -1 && record.restChildrenCount == undefined)
+               throw "EvoUndoRedo.RestoreChildren: 'record' doesn't contain 'restChildrenCount' property";
+       if (record.htmlBefore == undefined)
+               throw "EvoUndoRedo.RestoreChildren: 'record' doesn't contain 'htmlBefore' property";
+       if (record.htmlAfter == undefined)
+               throw "EvoUndoRedo.RestoreChildren: 'record' doesn't contain 'htmlAfter' property";
+
+       first = record.firstChildIndex;
+
+       if (first == -1) {
+               if (isUndo) {
+                       parent.innerHTML = record.htmlBefore;
+               } else {
+                       parent.innerHTML = record.htmlAfter;
+               }
+       } else {
+               // it can equal to the children.length, when the node had been removed
+               if (first < 0 || first > parent.children.length) {
+                       throw "EvoUndoRedo::RestoreChildren: firstChildIndex (" + first + ") out of bounds (" 
+ parent.children.length + ")";
+               }
+
+               last = parent.children.length - record.restChildrenCount;
+               if (last < 0 || last < first) {
+                       throw "EvoUndoRedo::RestoreChildren: restChildrenCount (" + record.restChildrenCount 
+ ") out of bounds (length:" +
+                               parent.children.length + " first:" + first + " last:" + last + ")";
+               }
+
+               for (ii = last - 1; ii >= first; ii--) {
+                       if (ii >= 0 && ii < parent.children.length) {
+                               parent.removeChild(parent.children[ii]);
+                       }
+               }
+
+               var tmpNode = document.createElement("evo-tmp");
+
+               if (isUndo) {
+                       tmpNode.innerHTML = record.htmlBefore;
+               } else {
+                       tmpNode.innerHTML = record.htmlAfter;
+               }
+
+               if (first < parent.children.length) {
+                       first = parent.children[first];
+
+                       while(tmpNode.firstElementChild) {
+                               parent.insertBefore(tmpNode.firstElementChild, first);
+                       }
+               } else {
+                       while(tmpNode.firstElementChild) {
+                               parent.appendChild(tmpNode.firstElementChild);
+                       }
+               }
+       }
+}
+
+EvoUndoRedo.Attach();
diff --git a/data/webkit/e-web-view.js b/data/webkit/e-web-view.js
index 771d35139b..0aa006f603 100644
--- a/data/webkit/e-web-view.js
+++ b/data/webkit/e-web-view.js
@@ -1,3 +1,20 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2019 Red Hat (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/>.
+ */
+
 'use strict';
 
 /* semi-convention: private functions start with lower-case letter,
@@ -405,12 +422,12 @@ Evo.getElementContent = function(node, format, useOuterHTML)
        var data;
 
        if (format == 1) {
-               data = EvoConvertToPlainText(node);
+               data = EvoConvert.ToPlainText(node);
        } else if (format == 2) {
                data = useOuterHTML ? node.outerHTML : node.innerHTML;
        } else if (format == 3) {
                data = {};
-               data["plain"] = EvoConvertToPlainText(node);
+               data["plain"] = EvoConvert.ToPlainText(node);
                data["html"] = useOuterHTML ? node.outerHTML : node.innerHTML;
        }
 
diff --git a/docs/reference/evolution-util/evolution-util-docs.sgml.in 
b/docs/reference/evolution-util/evolution-util-docs.sgml.in
index 95d3f63fc7..24df1d465b 100644
--- a/docs/reference/evolution-util/evolution-util-docs.sgml.in
+++ b/docs/reference/evolution-util/evolution-util-docs.sgml.in
@@ -315,7 +315,6 @@
     <xi:include href="xml/e-tree-view-frame.xml"/>
     <xi:include href="xml/e-url-entry.xml"/>
     <xi:include href="xml/e-util-enums.xml"/>
-    <xi:include href="xml/e-web-extension-container.xml"/>
     <xi:include href="xml/e-webdav-browser.xml"/>
     <xi:include href="xml/e-widget-undo.xml"/>
   </chapter>
diff --git a/src/composer/e-composer-actions.c b/src/composer/e-composer-actions.c
index 6990e19023..5cb03cab94 100644
--- a/src/composer/e-composer-actions.c
+++ b/src/composer/e-composer-actions.c
@@ -162,6 +162,30 @@ action_print_preview_cb (GtkAction *action,
        e_msg_composer_print (composer, print_action);
 }
 
+static void
+action_save_ready_cb (GObject *source_object,
+                     GAsyncResult *result,
+                     gpointer user_data)
+{
+       EMsgComposer *composer = user_data;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (E_IS_HTML_EDITOR (source_object));
+
+       if (!e_html_editor_save_finish (E_HTML_EDITOR (source_object), result, &error)) {
+               e_alert_submit (
+                       E_ALERT_SINK (composer),
+                       E_ALERT_NO_SAVE_FILE,
+                       e_html_editor_get_filename (E_HTML_EDITOR (source_object)), error ? error->message : 
_("Unknown error"), NULL);
+       } else {
+               composer_set_content_editor_changed (composer);
+       }
+
+       g_object_unref (composer);
+       g_clear_error (&error);
+}
+
 static void
 action_save_cb (GtkAction *action,
                 EMsgComposer *composer)
@@ -169,7 +193,6 @@ action_save_cb (GtkAction *action,
        EHTMLEditor *editor;
        const gchar *filename;
        gint fd;
-       GError *error = NULL;
 
        editor = e_msg_composer_get_editor (composer);
        filename = e_html_editor_get_filename (editor);
@@ -202,16 +225,7 @@ action_save_cb (GtkAction *action,
        } else
                close (fd);
 
-       if (!e_html_editor_save (editor, filename, TRUE, &error)) {
-               e_alert_submit (
-                       E_ALERT_SINK (composer),
-                       E_ALERT_NO_SAVE_FILE,
-                       filename, error->message, NULL);
-               g_error_free (error);
-               return;
-       }
-
-       composer_set_content_editor_changed (composer);
+       e_html_editor_save (editor, filename, TRUE, NULL, action_save_ready_cb, g_object_ref (composer));
 }
 
 static void
diff --git a/src/composer/e-composer-private.c b/src/composer/e-composer-private.c
index 8b6b8b4625..e4a1c17924 100644
--- a/src/composer/e-composer-private.c
+++ b/src/composer/e-composer-private.c
@@ -552,6 +552,8 @@ e_composer_private_finalize (EMsgComposer *composer)
        g_free (composer->priv->mime_type);
        g_free (composer->priv->mime_body);
        g_free (composer->priv->previous_identity_uid);
+
+       g_clear_pointer (&composer->priv->content_hash, e_content_editor_util_free_content_hash);
 }
 
 gchar *
diff --git a/src/composer/e-composer-private.h b/src/composer/e-composer-private.h
index 4a2e2e3e06..c415974cbc 100644
--- a/src/composer/e-composer-private.h
+++ b/src/composer/e-composer-private.h
@@ -121,6 +121,9 @@ struct _EMsgComposerPrivate {
        gulong drag_data_received_handler_id;
 
        gchar *previous_identity_uid;
+
+       guint content_hash_ref_count; /* when reaches 0, the content_hash is freed; to be able to reuse it */
+       EContentEditorContentHash *content_hash;
 };
 
 void           e_composer_private_constructed  (EMsgComposer *composer);
diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c
index 1c6db9adb1..41a9f0a71f 100644
--- a/src/composer/e-msg-composer.c
+++ b/src/composer/e-msg-composer.c
@@ -180,6 +180,19 @@ async_context_free (AsyncContext *context)
        g_slice_free (AsyncContext, context);
 }
 
+static void
+e_msg_composer_unref_content_hash (EMsgComposer *composer)
+{
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (composer->priv->content_hash_ref_count > 0);
+
+       composer->priv->content_hash_ref_count--;
+
+       if (!composer->priv->content_hash_ref_count) {
+               g_clear_pointer (&composer->priv->content_hash, e_content_editor_util_free_content_hash);
+       }
+}
+
 static void
 e_msg_composer_inc_soft_busy (EMsgComposer *composer)
 {
@@ -1425,7 +1438,7 @@ composer_build_message (EMsgComposer *composer,
                type = camel_content_type_decode (priv->mime_type);
 
        } else {
-               gchar *text;
+               const gchar *text;
                EHTMLEditor *editor;
                EContentEditor *cnt_editor;
 
@@ -1433,23 +1446,19 @@ composer_build_message (EMsgComposer *composer,
                cnt_editor = e_html_editor_get_content_editor (editor);
                data = g_byte_array_new ();
 
-               text = e_content_editor_get_content (
-                       cnt_editor,
-                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
-                       E_CONTENT_EDITOR_GET_PROCESSED,
-                       NULL, NULL);
+               text = e_content_editor_util_get_content_data (e_msg_composer_get_content_hash (composer),
+                       E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
 
                if (!text) {
                        g_warning ("%s: Failed to retrieve text/plain processed content", G_STRFUNC);
-                       text = g_strdup ("");
+                       text = "";
 
                        last_error = e_content_editor_dup_last_error (cnt_editor);
                }
 
                g_byte_array_append (data, (guint8 *) text, strlen (text));
-               if (!g_str_has_suffix (text, "\r\n"))
+               if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n"))
                        g_byte_array_append (data, (const guint8 *) "\r\n", 2);
-               g_free (text);
 
                type = camel_content_type_new ("text", "plain");
                charset = best_charset (
@@ -1509,16 +1518,11 @@ composer_build_message (EMsgComposer *composer,
 
        if ((flags & COMPOSER_FLAG_HTML_CONTENT) != 0 ||
            (flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) {
-               gchar *text;
+               const gchar *text;
                gsize length;
                gboolean pre_encode;
-               EHTMLEditor *editor;
-               EContentEditor *cnt_editor;
                GSList *inline_images_parts = NULL, *link;
 
-               editor = e_msg_composer_get_editor (composer);
-               cnt_editor = e_html_editor_get_content_editor (editor);
-
                data = g_byte_array_new ();
                if ((flags & COMPOSER_FLAG_SAVE_DRAFT) != 0) {
                        /* X-Evolution-Format */
@@ -1529,39 +1533,31 @@ composer_build_message (EMsgComposer *composer,
                        composer_add_evolution_composer_mode_header (
                                CAMEL_MEDIUM (context->message), composer);
 
-                       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);
+                       text = e_content_editor_util_get_content_data (e_msg_composer_get_content_hash 
(composer),
+                               E_CONTENT_EDITOR_GET_RAW_DRAFT);
 
                        if (!text) {
                                g_warning ("%s: Failed to retrieve draft content", G_STRFUNC);
-                               text = g_strdup ("");
+                               text = "";
                        }
                } 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);
+                       text = e_content_editor_util_get_content_data (e_msg_composer_get_content_hash 
(composer),
+                               E_CONTENT_EDITOR_GET_TO_SEND_HTML);
 
                        if (!text) {
                                g_warning ("%s: Failed to retrieve HTML processed content", G_STRFUNC);
-                               text = g_strdup ("");
+                               text = "";
                        }
                }
 
-               if (!last_error)
-                       last_error = e_content_editor_dup_last_error (cnt_editor);
+               inline_images_parts = e_content_editor_util_get_content_data (e_msg_composer_get_content_hash 
(composer),
+                       E_CONTENT_EDITOR_GET_INLINE_IMAGES);
 
                length = strlen (text);
                g_byte_array_append (data, (guint8 *) text, (guint) length);
-               if (!g_str_has_suffix (text, "\r\n"))
+               if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n"))
                        g_byte_array_append (data, (const guint8 *) "\r\n", 2);
                pre_encode = text_requires_quoted_printable (text, length);
-               g_free (text);
 
                mem_stream = camel_stream_mem_new_with_byte_array (data);
                stream = camel_stream_filter_new (mem_stream);
@@ -1633,7 +1629,7 @@ composer_build_message (EMsgComposer *composer,
                        for (link = inline_images_parts; link; link = g_slist_next (link)) {
                                CamelMimePart *part = link->data;
 
-                               camel_multipart_add_part (html_with_images, part);
+                               camel_multipart_add_part (html_with_images, g_object_ref (part));
                        }
 
                        context->top_level_part =
@@ -1642,7 +1638,6 @@ composer_build_message (EMsgComposer *composer,
                        context->top_level_part =
                                CAMEL_DATA_WRAPPER (body);
                }
-               g_slist_free_full (inline_images_parts, g_object_unref);
        }
 
        view = e_msg_composer_get_attachment_view (composer);
@@ -3167,7 +3162,6 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
        CamelContentType *content_type;
        CamelDataWrapper *wrapper;
        EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
 
        if (!mime_part)
                return;
@@ -3175,7 +3169,6 @@ 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);
-       cnt_editor = e_html_editor_get_content_editor (editor);
 
        if (CAMEL_IS_MULTIPART (wrapper)) {
                /* another layer of multipartness... */
@@ -3186,10 +3179,9 @@ add_attachments_handle_mime_part (EMsgComposer *composer,
                if (camel_content_type_is (content_type, "image", "*") && (
                    camel_mime_part_get_content_id (mime_part) ||
                    camel_mime_part_get_content_location (mime_part)))
-                       e_content_editor_insert_image_from_mime_part (
-                               cnt_editor, mime_part);
+                       e_html_editor_add_cid_part (editor, mime_part);
        } else if (related && camel_content_type_is (content_type, "image", "*")) {
-               e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part);
+               e_html_editor_add_cid_part (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
@@ -3609,12 +3601,10 @@ handle_multipart (EMsgComposer *composer,
                           camel_mime_part_get_content_location (mime_part))) {
                        /* special in-line attachment */
                        EHTMLEditor *editor;
-                       EContentEditor *cnt_editor;
 
                        editor = e_msg_composer_get_editor (composer);
-                       cnt_editor = e_html_editor_get_content_editor (editor);
 
-                       e_content_editor_insert_image_from_mime_part (cnt_editor, mime_part);
+                       e_html_editor_add_cid_part (editor, mime_part);
 
                        /* Add it to both, to not lose attachments not referenced in HTML body.
                           The inserted images are not included in the message when not referenced. */
@@ -4250,40 +4240,200 @@ e_msg_composer_get_shell (EMsgComposer *composer)
        return E_SHELL (composer->priv->shell);
 }
 
+/**
+ * e_msg_composer_get_content_hash:
+ * @composer: an #EMsgComposer
+ *
+ * Returns current #EContentEditorContentHash with content
+ * of the composer. It's valid, and available, only during
+ * operations/signals, which construct message from the @composer
+ * content. The @composer precaches the content, thus it can
+ * be accessed in a synchronous way (in constrast to EContentEditor,
+ * which allows getting the content only asynchronously).
+ * The content hash is owned by the @composer and it is freed
+ * as soon as the respective operation is finished.
+ *
+ * Returns: (transfer none) (nullable): an #EContentEditorContentHash
+ *    with current content data, or %NULL, when it is not loaded.
+ *
+ * Since: 3.38
+ **/
+EContentEditorContentHash *
+e_msg_composer_get_content_hash (EMsgComposer *composer)
+{
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+
+       /* Calling the function out of expected place should warn that something goes wrong */
+       g_warn_if_fail (composer->priv->content_hash != NULL);
+
+       return composer->priv->content_hash;
+}
+
+typedef void (* PrepareContentHashCallback) (EMsgComposer *composer,
+                                            gpointer user_data,
+                                            const GError *error);
+
+typedef struct _PrepareContentHashData {
+       EMsgComposer *composer;
+       PrepareContentHashCallback callback;
+       gpointer user_data;
+} PrepareContentHashData;
+
+static PrepareContentHashData *
+prepare_content_hash_data_new (EMsgComposer *composer,
+                              PrepareContentHashCallback callback,
+                              gpointer user_data)
+{
+       PrepareContentHashData *pchd;
+
+       pchd = g_slice_new (PrepareContentHashData);
+       pchd->composer = g_object_ref (composer);
+       pchd->callback = callback;
+       pchd->user_data = user_data;
+
+       return pchd;
+}
+
+static void
+prepare_content_hash_data_free (gpointer ptr)
+{
+       PrepareContentHashData *pchd = ptr;
+
+       if (pchd) {
+               g_clear_object (&pchd->composer);
+               g_slice_free (PrepareContentHashData, pchd);
+       }
+}
+
+static void
+e_msg_composer_prepare_content_hash_ready_cb (GObject *source_object,
+                                             GAsyncResult *result,
+                                             gpointer user_data)
+{
+       PrepareContentHashData *pchd = user_data;
+       EContentEditorContentHash *content_hash;
+       GError *error = NULL;
+
+       g_return_if_fail (pchd != NULL);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
+
+       if (content_hash) {
+               g_warn_if_fail (pchd->composer->priv->content_hash == NULL);
+               g_warn_if_fail (pchd->composer->priv->content_hash_ref_count == 0);
+
+               pchd->composer->priv->content_hash = content_hash;
+               pchd->composer->priv->content_hash_ref_count = 1;
+       }
+
+       pchd->callback (pchd->composer, pchd->user_data, error);
+
+       prepare_content_hash_data_free (pchd);
+       g_clear_error (&error);
+}
+
+static void
+e_msg_composer_prepare_content_hash (EMsgComposer *composer,
+                                    GCancellable *cancellable,
+                                    EActivity *activity,
+                                    PrepareContentHashCallback callback,
+                                    gpointer user_data)
+{
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       CamelInternetAddress *from;
+       PrepareContentHashData *pchd;
+       const gchar *from_domain = NULL;
+
+       g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+       g_return_if_fail (callback != NULL);
+
+       /* Cannot use e_msg_composer_get_content_hash() here, because it prints
+          a runtime warning when the content_hash is NULL. */
+       if (composer->priv->content_hash) {
+               composer->priv->content_hash_ref_count++;
+
+               callback (composer, user_data, NULL);
+               return;
+       }
+
+       if (activity)
+               e_activity_set_text (activity, _("Reading text content…"));
+
+       pchd = prepare_content_hash_data_new (composer, callback, user_data);
+       editor = e_msg_composer_get_editor (composer);
+       cnt_editor = e_html_editor_get_content_editor (editor);
+       from = e_msg_composer_get_from (composer);
+
+       if (from && camel_internet_address_get (from, 0, NULL, &from_domain)) {
+               const gchar *at = strchr (from_domain, '@');
+
+               if (at)
+                       from_domain = at + 1;
+               else
+                       from_domain = NULL;
+       }
+
+       if (!from_domain || !*from_domain)
+               from_domain = "localhost";
+
+       e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_ALL, from_domain, cancellable,
+               e_msg_composer_prepare_content_hash_ready_cb, pchd);
+
+       g_clear_object (&from);
+}
+
+static gboolean
+e_msg_composer_claim_no_build_message_error (EMsgComposer *composer,
+                                            EActivity *activity,
+                                            const GError *error,
+                                            gboolean unref_content_hash_on_error)
+{
+       g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), FALSE);
+
+       if (error) {
+               if (!e_activity_handle_cancellation (activity, error)) {
+                       EAlertSink *alert_sink;
+
+                       alert_sink = e_activity_get_alert_sink (activity);
+
+                       e_alert_submit (
+                               alert_sink,
+                               "mail-composer:no-build-message",
+                               error->message, NULL);
+               }
+
+               if (e_msg_composer_is_exiting (composer)) {
+                       gtk_window_present (GTK_WINDOW (composer));
+                       composer->priv->application_exiting = FALSE;
+               }
+
+               gtk_window_present (GTK_WINDOW (composer));
+
+               if (unref_content_hash_on_error)
+                       e_msg_composer_unref_content_hash (composer);
+       }
+
+       return error != NULL;
+}
+
 static void
 msg_composer_send_cb (EMsgComposer *composer,
                       GAsyncResult *result,
                       AsyncContext *context)
 {
        CamelMimeMessage *message;
-       EAlertSink *alert_sink;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
        GError *error = NULL;
 
-       alert_sink = e_activity_get_alert_sink (context->activity);
-
        message = e_msg_composer_get_message_finish (composer, result, &error);
 
-       if (e_activity_handle_cancellation (context->activity, error)) {
-               g_warn_if_fail (message == NULL);
-               async_context_free (context);
-               g_error_free (error);
-
-               gtk_window_present (GTK_WINDOW (composer));
-               return;
-       }
-
-       if (error != NULL) {
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, TRUE)) {
                g_warn_if_fail (message == NULL);
-               e_alert_submit (
-                       alert_sink,
-                       "mail-composer:no-build-message",
-                       error->message, NULL);
                async_context_free (context);
-               g_error_free (error);
-
-               gtk_window_present (GTK_WINDOW (composer));
+               g_clear_error (&error);
                return;
        }
 
@@ -4304,9 +4454,47 @@ msg_composer_send_cb (EMsgComposer *composer,
 
        g_object_unref (message);
 
+       e_msg_composer_unref_content_hash (composer);
        async_context_free (context);
 }
 
+static void
+e_msg_composer_send_content_hash_ready_cb (EMsgComposer *composer,
+                                          gpointer user_data,
+                                          const GError *error)
+{
+       AsyncContext *context = user_data;
+       gboolean proceed_with_send = TRUE;
+
+       g_return_if_fail (context != NULL);
+
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, FALSE)) {
+               async_context_free (context);
+               return;
+       }
+
+       /* This gives the user a chance to abort the send. */
+       g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_send);
+
+       if (!proceed_with_send) {
+               gtk_window_present (GTK_WINDOW (composer));
+               e_msg_composer_unref_content_hash (composer);
+
+               if (e_msg_composer_is_exiting (composer)) {
+                       gtk_window_present (GTK_WINDOW (composer));
+                       composer->priv->application_exiting = FALSE;
+               }
+
+               async_context_free (context);
+               return;
+       }
+
+       e_msg_composer_get_message (
+               composer, G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
+               (GAsyncReadyCallback) msg_composer_send_cb,
+               context);
+}
+
 /**
  * e_msg_composer_send:
  * @composer: an #EMsgComposer
@@ -4319,18 +4507,9 @@ e_msg_composer_send (EMsgComposer *composer)
        EHTMLEditor *editor;
        AsyncContext *context;
        GCancellable *cancellable;
-       gboolean proceed_with_send = TRUE;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       /* This gives the user a chance to abort the send. */
-       g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_send);
-
-       if (!proceed_with_send) {
-               gtk_window_present (GTK_WINDOW (composer));
-               return;
-       }
-
        editor = e_msg_composer_get_editor (composer);
 
        context = g_slice_new0 (AsyncContext);
@@ -4338,10 +4517,8 @@ e_msg_composer_send (EMsgComposer *composer)
 
        cancellable = e_activity_get_cancellable (context->activity);
 
-       e_msg_composer_get_message (
-               composer, G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) msg_composer_send_cb,
-               context);
+       e_msg_composer_prepare_content_hash (composer, cancellable, context->activity,
+               e_msg_composer_send_content_hash_ready_cb, context);
 }
 
 static void
@@ -4374,43 +4551,16 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
                                 AsyncContext *context)
 {
        CamelMimeMessage *message;
-       EAlertSink *alert_sink;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
        GError *error = NULL;
 
-       alert_sink = e_activity_get_alert_sink (context->activity);
-
-       message = e_msg_composer_get_message_draft_finish (
-               composer, result, &error);
-
-       if (e_activity_handle_cancellation (context->activity, error)) {
-               g_warn_if_fail (message == NULL);
-               async_context_free (context);
-               g_error_free (error);
-
-               if (e_msg_composer_is_exiting (composer)) {
-                       gtk_window_present (GTK_WINDOW (composer));
-                       composer->priv->application_exiting = FALSE;
-               }
+       message = e_msg_composer_get_message_draft_finish (composer, result, &error);
 
-               return;
-       }
-
-       if (error != NULL) {
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, TRUE)) {
                g_warn_if_fail (message == NULL);
-               e_alert_submit (
-                       alert_sink,
-                       "mail-composer:no-build-message",
-                       error->message, NULL);
                async_context_free (context);
-               g_error_free (error);
-
-               if (e_msg_composer_is_exiting (composer)) {
-                       gtk_window_present (GTK_WINDOW (composer));
-                       composer->priv->application_exiting = FALSE;
-               }
-
+               g_clear_error (&error);
                return;
        }
 
@@ -4432,9 +4582,34 @@ msg_composer_save_to_drafts_cb (EMsgComposer *composer,
                        G_OBJECT (context->activity),
                        msg_composer_save_to_drafts_done_cb, composer);
 
+       e_msg_composer_unref_content_hash (composer);
        async_context_free (context);
 }
 
+static void
+e_msg_composer_save_to_drafts_content_hash_ready_cb (EMsgComposer *composer,
+                                                    gpointer user_data,
+                                                    const GError *error)
+{
+       AsyncContext *context = user_data;
+
+       g_return_if_fail (context != NULL);
+
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, FALSE)) {
+               if (e_msg_composer_is_exiting (composer)) {
+                       gtk_window_present (GTK_WINDOW (composer));
+                       composer->priv->application_exiting = FALSE;
+               }
+               async_context_free (context);
+               return;
+       }
+
+       e_msg_composer_get_message_draft (
+               composer, G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
+               (GAsyncReadyCallback) msg_composer_save_to_drafts_cb,
+               context);
+}
+
 /**
  * e_msg_composer_save_to_drafts:
  * @composer: an #EMsgComposer
@@ -4458,10 +4633,8 @@ e_msg_composer_save_to_drafts (EMsgComposer *composer)
 
        cancellable = e_activity_get_cancellable (context->activity);
 
-       e_msg_composer_get_message_draft (
-               composer, G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) msg_composer_save_to_drafts_cb,
-               context);
+       e_msg_composer_prepare_content_hash (composer, cancellable, context->activity,
+               e_msg_composer_save_to_drafts_content_hash_ready_cb, context);
 }
 
 static void
@@ -4470,30 +4643,16 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
                                 AsyncContext *context)
 {
        CamelMimeMessage *message;
-       EAlertSink *alert_sink;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
        GError *error = NULL;
 
-       alert_sink = e_activity_get_alert_sink (context->activity);
-
        message = e_msg_composer_get_message_finish (composer, result, &error);
 
-       if (e_activity_handle_cancellation (context->activity, error)) {
-               g_warn_if_fail (message == NULL);
-               async_context_free (context);
-               g_error_free (error);
-               return;
-       }
-
-       if (error != NULL) {
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, TRUE)) {
                g_warn_if_fail (message == NULL);
-               e_alert_submit (
-                       alert_sink,
-                       "mail-composer:no-build-message",
-                       error->message, NULL);
                async_context_free (context);
-               g_error_free (error);
+               g_clear_error (&error);
                return;
        }
 
@@ -4505,11 +4664,49 @@ msg_composer_save_to_outbox_cb (EMsgComposer *composer,
 
        g_object_unref (message);
 
-       async_context_free (context);
-
        editor = e_msg_composer_get_editor (composer);
        cnt_editor = e_html_editor_get_content_editor (editor);
        e_content_editor_set_changed (cnt_editor, TRUE);
+
+       async_context_free (context);
+}
+
+static void
+e_msg_composer_save_to_outbox_content_hash_ready_cb (EMsgComposer *composer,
+                                                    gpointer user_data,
+                                                    const GError *error)
+{
+       AsyncContext *context = user_data;
+
+       g_return_if_fail (context != NULL);
+
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, FALSE)) {
+               async_context_free (context);
+               return;
+       }
+
+       if (!composer->priv->is_sending_message) {
+               gboolean proceed_with_save = TRUE;
+
+               /* This gives the user a chance to abort the save. */
+               g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_save);
+
+               if (!proceed_with_save) {
+                       if (e_msg_composer_is_exiting (composer)) {
+                               gtk_window_present (GTK_WINDOW (composer));
+                               composer->priv->application_exiting = FALSE;
+                       }
+
+                       e_msg_composer_unref_content_hash (composer);
+                       async_context_free (context);
+                       return;
+               }
+       }
+
+       e_msg_composer_get_message (
+               composer, G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
+               (GAsyncReadyCallback) msg_composer_save_to_outbox_cb,
+               context);
 }
 
 /**
@@ -4527,16 +4724,6 @@ e_msg_composer_save_to_outbox (EMsgComposer *composer)
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
-       if (!composer->priv->is_sending_message) {
-               gboolean proceed_with_save = TRUE;
-
-               /* This gives the user a chance to abort the save. */
-               g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_save);
-
-               if (!proceed_with_save)
-                       return;
-       }
-
        editor = e_msg_composer_get_editor (composer);
 
        context = g_slice_new0 (AsyncContext);
@@ -4544,10 +4731,8 @@ e_msg_composer_save_to_outbox (EMsgComposer *composer)
 
        cancellable = e_activity_get_cancellable (context->activity);
 
-       e_msg_composer_get_message (
-               composer, G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) msg_composer_save_to_outbox_cb,
-               context);
+       e_msg_composer_prepare_content_hash (composer, cancellable, context->activity,
+               e_msg_composer_save_to_outbox_content_hash_ready_cb, context);
 }
 
 static void
@@ -4556,29 +4741,14 @@ msg_composer_print_cb (EMsgComposer *composer,
                        AsyncContext *context)
 {
        CamelMimeMessage *message;
-       EAlertSink *alert_sink;
        GError *error = NULL;
 
-       alert_sink = e_activity_get_alert_sink (context->activity);
-
-       message = e_msg_composer_get_message_print_finish (
-               composer, result, &error);
+       message = e_msg_composer_get_message_print_finish (composer, result, &error);
 
-       if (e_activity_handle_cancellation (context->activity, error)) {
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, TRUE)) {
                g_warn_if_fail (message == NULL);
                async_context_free (context);
-               g_error_free (error);
-               return;
-       }
-
-       if (error != NULL) {
-               g_warn_if_fail (message == NULL);
-               async_context_free (context);
-               e_alert_submit (
-                       alert_sink,
-                       "mail-composer:no-build-message",
-                       error->message, NULL);
-               g_error_free (error);
+               g_clear_error (&error);
                return;
        }
 
@@ -4593,6 +4763,26 @@ msg_composer_print_cb (EMsgComposer *composer,
        async_context_free (context);
 }
 
+static void
+e_msg_composer_print_content_hash_ready_cb (EMsgComposer *composer,
+                                           gpointer user_data,
+                                           const GError *error)
+{
+       AsyncContext *context = user_data;
+
+       g_return_if_fail (context != NULL);
+
+       if (e_msg_composer_claim_no_build_message_error (composer, context->activity, error, FALSE)) {
+               async_context_free (context);
+               return;
+       }
+
+       e_msg_composer_get_message_print (
+               composer, G_PRIORITY_DEFAULT, e_activity_get_cancellable (context->activity),
+               (GAsyncReadyCallback) msg_composer_print_cb,
+               context);
+}
+
 /**
  * e_msg_composer_print:
  * @composer: an #EMsgComposer
@@ -4618,10 +4808,8 @@ e_msg_composer_print (EMsgComposer *composer,
 
        cancellable = e_activity_get_cancellable (context->activity);
 
-       e_msg_composer_get_message_print (
-               composer, G_PRIORITY_DEFAULT, cancellable,
-               (GAsyncReadyCallback) msg_composer_print_cb,
-               context);
+       e_msg_composer_prepare_content_hash (composer, cancellable, context->activity,
+               e_msg_composer_print_content_hash_ready_cb, context);
 }
 
 static GList *
@@ -5346,9 +5534,86 @@ composer_get_message_ready (EMsgComposer *composer,
 
        g_simple_async_result_complete (simple);
 
+       e_msg_composer_unref_content_hash (composer);
+
        g_object_unref (simple);
 }
 
+typedef struct _BuildMessageWrapperData {
+       EMsgComposer *composer;
+       ComposerFlags flags;
+       gint io_priority;
+       GCancellable *cancellable;
+       GSimpleAsyncResult *simple;
+} BuildMessageWrapperData;
+
+static BuildMessageWrapperData *
+build_message_wrapper_data_new (EMsgComposer *composer,
+                               ComposerFlags flags,
+                               gint io_priority,
+                               GCancellable *cancellable,
+                               GSimpleAsyncResult *simple)
+{
+       BuildMessageWrapperData *bmwd;
+
+       bmwd = g_slice_new (BuildMessageWrapperData);
+       bmwd->composer = g_object_ref (composer);
+       bmwd->flags = flags;
+       bmwd->io_priority = io_priority;
+       bmwd->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+       bmwd->simple = g_object_ref (simple);
+
+       return bmwd;
+}
+
+static void
+build_message_wrapper_data_free (gpointer ptr)
+{
+       BuildMessageWrapperData *bmwd = ptr;
+
+       if (bmwd) {
+               g_clear_object (&bmwd->composer);
+               g_clear_object (&bmwd->cancellable);
+               g_clear_object (&bmwd->simple);
+               g_slice_free (BuildMessageWrapperData, bmwd);
+       }
+}
+
+static void
+composer_build_message_wrapper_content_hash_ready_cb (EMsgComposer *composer,
+                                                     gpointer user_data,
+                                                     const GError *error)
+{
+       BuildMessageWrapperData *bmwd = user_data;
+
+       g_return_if_fail (bmwd != NULL);
+
+       if (error) {
+               g_simple_async_result_set_from_error (bmwd->simple, error);
+               g_simple_async_result_complete (bmwd->simple);
+       } else {
+               composer_build_message (composer, bmwd->flags, bmwd->io_priority,
+                       bmwd->cancellable, (GAsyncReadyCallback)
+                       composer_get_message_ready, bmwd->simple);
+       }
+
+       build_message_wrapper_data_free (bmwd);
+}
+
+static void
+composer_build_message_wrapper (EMsgComposer *composer,
+                               ComposerFlags flags,
+                               gint io_priority,
+                               GCancellable *cancellable,
+                               GSimpleAsyncResult *simple)
+{
+       BuildMessageWrapperData *bmwd;
+
+       bmwd = build_message_wrapper_data_new (composer, flags, io_priority, cancellable, simple);
+
+       e_msg_composer_prepare_content_hash (composer, cancellable, NULL, 
composer_build_message_wrapper_content_hash_ready_cb, bmwd);
+}
+
 /**
  * e_msg_composer_get_message:
  * @composer: an #EMsgComposer
@@ -5410,10 +5675,7 @@ e_msg_composer_get_message (EMsgComposer *composer,
                flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
 #endif
 
-       composer_build_message (
-               composer, flags, io_priority,
-               cancellable, (GAsyncReadyCallback)
-               composer_get_message_ready, simple);
+       composer_build_message_wrapper (composer, flags, io_priority, cancellable, simple);
 }
 
 CamelMimeMessage *
@@ -5461,10 +5723,7 @@ e_msg_composer_get_message_print (EMsgComposer *composer,
        flags |= COMPOSER_FLAG_HTML_CONTENT;
        flags |= COMPOSER_FLAG_SAVE_OBJECT_DATA;
 
-       composer_build_message (
-               composer, flags, io_priority,
-               cancellable, (GAsyncReadyCallback)
-               composer_get_message_ready, simple);
+       composer_build_message_wrapper (composer, flags, io_priority, cancellable, simple);
 }
 
 CamelMimeMessage *
@@ -5546,10 +5805,7 @@ e_msg_composer_get_message_draft (EMsgComposer *composer,
                flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
 #endif
 
-       composer_build_message (
-               composer, flags, io_priority,
-               cancellable, (GAsyncReadyCallback)
-               composer_get_message_ready, simple);
+       composer_build_message_wrapper (composer, flags, io_priority, cancellable, simple);
 }
 
 CamelMimeMessage *
@@ -5667,32 +5923,32 @@ e_msg_composer_get_reply_to (EMsgComposer *composer)
 GByteArray *
 e_msg_composer_get_raw_message_text_without_signature (EMsgComposer *composer)
 {
-       EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
-       gchar *content;
+       EContentEditorContentHash *content_hash;
+       const gchar *content;
+       gsize content_length;
        GByteArray *bytes;
        gboolean needs_crlf;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
-       editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
+       content_hash = e_msg_composer_get_content_hash (composer);
+       g_return_val_if_fail (content_hash != NULL, NULL);
 
-       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);
+       content = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED);
 
        if (!content) {
                g_warning ("%s: Failed to retrieve content", G_STRFUNC);
-               content = g_strdup ("");
+
+               content = "";
        }
 
-       needs_crlf = !g_str_has_suffix (content, "\r\n");
+       needs_crlf = !g_str_has_suffix (content, "\r\n") && !g_str_has_suffix (content, "\n");
 
-       bytes = g_byte_array_new_take ((guint8 *) content, strlen (content));
+       content_length = strlen (content);
+
+       bytes = g_byte_array_sized_new (content_length + 3);
+
+       g_byte_array_append (bytes, (const guint8 *) content, content_length);
 
        if (needs_crlf)
                g_byte_array_append (bytes, (const guint8 *) "\r\n", 2);
@@ -5708,31 +5964,32 @@ e_msg_composer_get_raw_message_text_without_signature (EMsgComposer *composer)
 GByteArray *
 e_msg_composer_get_raw_message_text (EMsgComposer *composer)
 {
-       EHTMLEditor *editor;
-       EContentEditor *cnt_editor;
-       gchar *content;
+       EContentEditorContentHash *content_hash;
+       const gchar *content;
+       gsize content_length;
        GByteArray *bytes;
        gboolean needs_crlf;
 
        g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
 
-       editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (editor);
+       content_hash = e_msg_composer_get_content_hash (composer);
+       g_return_val_if_fail (content_hash != NULL, NULL);
 
-       content = e_content_editor_get_content (
-               cnt_editor,
-               E_CONTENT_EDITOR_GET_BODY |
-               E_CONTENT_EDITOR_GET_TEXT_PLAIN,
-               NULL, NULL);
+       content = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN);
 
        if (!content) {
                g_warning ("%s: Failed to retrieve content", G_STRFUNC);
-               content = g_strdup ("");
+
+               content = "";
        }
 
-       needs_crlf = !g_str_has_suffix (content, "\r\n");
+       needs_crlf = !g_str_has_suffix (content, "\r\n") && !g_str_has_suffix (content, "\n");
+
+       content_length = strlen (content);
+
+       bytes = g_byte_array_sized_new (content_length + 3);
 
-       bytes = g_byte_array_new_take ((guint8 *) content, strlen (content));
+       g_byte_array_append (bytes, (const guint8 *) content, content_length);
 
        if (needs_crlf)
                g_byte_array_append (bytes, (const guint8 *) "\r\n", 2);
diff --git a/src/composer/e-msg-composer.h b/src/composer/e-msg-composer.h
index e62ca84297..f385889ba4 100644
--- a/src/composer/e-msg-composer.h
+++ b/src/composer/e-msg-composer.h
@@ -108,6 +108,8 @@ EFocusTracker *     e_msg_composer_get_focus_tracker
 CamelSession * e_msg_composer_ref_session      (EMsgComposer *composer);
 EShell *       e_msg_composer_get_shell        (EMsgComposer *composer);
 
+EContentEditorContentHash *
+               e_msg_composer_get_content_hash (EMsgComposer *composer);
 void           e_msg_composer_send             (EMsgComposer *composer);
 void           e_msg_composer_save_to_drafts   (EMsgComposer *composer);
 void           e_msg_composer_save_to_outbox   (EMsgComposer *composer);
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 62d8f48ed5..19632d07f2 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -271,7 +271,6 @@ set(SOURCES
        e-url-entry.c
        e-util-private.h
        e-webdav-browser.c
-       e-web-extension-container.c
        e-web-view.c
        e-web-view-jsc-utils.c
        e-web-view-preview.c
@@ -544,7 +543,6 @@ set(HEADERS
        e-url-entry.h
        e-util-enums.h
        e-webdav-browser.h
-       e-web-extension-container.h
        e-web-view.h
        e-web-view-jsc-utils.h
        e-web-view-preview.h
@@ -748,6 +746,7 @@ macro(add_private_program _name _sources)
 
        target_compile_definitions(${_name} PRIVATE
                -DG_LOG_DOMAIN=\"${_name}\"
+               -DEVOLUTION_ICONDIR=\"${icondir}\"
                -DEVOLUTION_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\"
                -DEVOLUTION_MODULEDIR=\"${moduledir}\"
                -DEVOLUTION_TESTGIOMODULESDIR=\"${CMAKE_CURRENT_BINARY_DIR}/test-gio-modules\"
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index 64a066cb6d..9f69638c39 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -23,6 +23,7 @@
 
 #include "e-html-editor.h"
 #include "e-util-enumtypes.h"
+#include "e-misc-utils.h"
 #include "e-content-editor.h"
 
 G_DEFINE_INTERFACE (EContentEditor, e_content_editor, GTK_TYPE_WIDGET);
@@ -36,6 +37,7 @@ enum {
        REPLACE_ALL_DONE,
        DROP_HANDLED,
        CONTENT_CHANGED,
+       REF_MIME_PART,
        LAST_SIGNAL
 };
 
@@ -297,18 +299,18 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                        G_PARAM_STATIC_STRINGS));
 
        /**
-        * EContentEditor:indented
+        * EContentEditor:indent-level
         *
-        * Holds whether current paragraph is indented. This does not include
+        * Holds current paragraph indent level. This does not include
         * citations.
         */
        g_object_interface_install_property (
                iface,
-               g_param_spec_boolean (
-                       "indented",
+               g_param_spec_int (
+                       "indent-level",
                        NULL,
                        NULL,
-                       FALSE,
+                       0, E_HTML_EDITOR_MAX_INDENT_LEVEL, 0,
                        G_PARAM_READABLE |
                        G_PARAM_STATIC_STRINGS));
 
@@ -328,22 +330,6 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                        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
         *
@@ -562,10 +548,11 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                E_TYPE_CONTENT_EDITOR,
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (EContentEditorInterface, context_menu_requested),
-               g_signal_accumulator_true_handled, NULL,
+               NULL, NULL,
                NULL,
-               G_TYPE_BOOLEAN, 2,
+               G_TYPE_NONE, 3,
                G_TYPE_INT,
+               G_TYPE_STRING,
                GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
        /**
@@ -630,6 +617,26 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                NULL, NULL,
                NULL,
                G_TYPE_NONE, 0);
+
+       /**
+        * EContentEditor:ref-mime-part
+        *
+        * This is used by the content editor, when it wants to get
+        * a #CamelMimePart of given URI (aka "cid:..."). The returned
+        * object, if not %NULL, should be freed with g_object_unref(),
+        * when no longer needed.
+        *
+        * Since: 3.38
+        */
+       signals[REF_MIME_PART] = g_signal_new (
+               "ref-mime-part",
+               E_TYPE_CONTENT_EDITOR,
+               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+               G_STRUCT_OFFSET (EContentEditorInterface, ref_mime_part),
+               NULL, NULL,
+               NULL,
+               CAMEL_TYPE_MIME_PART, 1,
+               G_TYPE_STRING);
 }
 
 ESpellChecker *
@@ -717,24 +724,24 @@ e_content_editor_can_redo (EContentEditor *editor)
 }
 
 /**
- * e_content_editor_is_indented:
+ * e_content_editor_indent_level:
  * @editor: an #EContentEditor
  *
- * Returns whether the current paragraph is indented. This does not include
- * citations.
+ * Returns the indent level for the current selection/caret position.
+ * This does not include citations.
  *
- * Returns: %TRUE when current paragraph is indented, %FALSE otherwise.
+ * Returns: the indent level.
  *
- * Since: 3.22
+ * Since: 3.38
  **/
-gboolean
-e_content_editor_is_indented (EContentEditor *editor)
+gint
+e_content_editor_indent_level (EContentEditor *editor)
 {
-       gboolean value = FALSE;
+       gint value = 0;
 
-       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), 0);
 
-       g_object_get (G_OBJECT (editor), "indented", &value, NULL);
+       g_object_get (G_OBJECT (editor), "indent-level", &value, NULL);
 
        return value;
 }
@@ -1162,48 +1169,6 @@ e_content_editor_is_italic (EContentEditor *editor)
        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
@@ -1574,41 +1539,351 @@ e_content_editor_insert_content (EContentEditor *editor,
        iface->insert_content (editor, content, flags);
 }
 
-gchar *
+/*
+ Finish the operation with e_content_editor_get_content_finish().
+ */
+void
 e_content_editor_get_content (EContentEditor *editor,
-                              EContentEditorGetContentFlags flags,
+                             guint32 flags,
                              const gchar *inline_images_from_domain,
-                             GSList **inline_images_parts /* newly created CamelMimePart * */)
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
+{
+       EContentEditorInterface *iface;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+
+       if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES))
+               g_return_if_fail (inline_images_from_domain != NULL);
+
+       iface = E_CONTENT_EDITOR_GET_IFACE (editor);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->get_content != NULL);
+
+       iface->get_content (editor, flags, inline_images_from_domain, cancellable, callback, user_data);
+}
+
+/*
+ Finishes previous call of e_content_editor_get_content(). The implementation
+ creates the GHashTable with e_content_editor_util_new_content_hash() and fills
+ it with e_content_editor_util_put_content_data(), e_content_editor_util_take_content_data()
+ or e_content_editor_util_take_content_data_images(). The caller can access
+ the members with e_content_editor_util_get_content_data().
+
+ The returned pointer should be freed with e_content_editor_util_free_content_hash(),
+ when done with it.
+ */
+EContentEditorContentHash *
+e_content_editor_get_content_finish (EContentEditor *editor,
+                                    GAsyncResult *result,
+                                    GError **error)
 {
        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);
+       return iface->get_content_finish (editor, result, error);
+}
+
+typedef struct _ContentHashData {
+       gpointer data;
+       GDestroyNotify destroy_data;
+} ContentHashData;
+
+static ContentHashData *
+content_hash_data_new (gpointer data,
+                      GDestroyNotify destroy_data)
+{
+       ContentHashData *chd;
+
+       chd = g_slice_new (ContentHashData);
+       chd->data = data;
+       chd->destroy_data = destroy_data;
+
+       return chd;
+}
+
+static void
+content_hash_data_free (gpointer ptr)
+{
+       ContentHashData *chd = ptr;
+
+       if (ptr) {
+               if (chd->destroy_data && chd->data)
+                       chd->destroy_data (chd->data);
+
+               g_slice_free (ContentHashData, chd);
+       }
+}
+
+static void
+content_data_free_obj_slist (gpointer ptr)
+{
+       GSList *lst = ptr;
+
+       g_slist_free_full (lst, g_object_unref);
+}
+
+EContentEditorContentHash *
+e_content_editor_util_new_content_hash (void)
+{
+       return (EContentEditorContentHash *) g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
content_hash_data_free);
 }
 
 void
-e_content_editor_insert_image_from_mime_part (EContentEditor *editor,
-                                              CamelMimePart *part)
+e_content_editor_util_free_content_hash (EContentEditorContentHash *content_hash)
 {
-       EContentEditorInterface *iface;
+       if (content_hash)
+               g_hash_table_unref ((GHashTable *) content_hash);
+}
 
-       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
-       g_return_if_fail (part != NULL);
+void
+e_content_editor_util_put_content_data (EContentEditorContentHash *content_hash,
+                                       EContentEditorGetContentFlags flag,
+                                       const gchar *data)
+{
+       g_return_if_fail (content_hash != NULL);
+       g_return_if_fail (flag != E_CONTENT_EDITOR_GET_ALL);
+       g_return_if_fail (data != 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);
+       e_content_editor_util_take_content_data (content_hash, flag, g_strdup (data), g_free);
+}
+
+void
+e_content_editor_util_take_content_data (EContentEditorContentHash *content_hash,
+                                        EContentEditorGetContentFlags flag,
+                                        gpointer data,
+                                        GDestroyNotify destroy_data)
+{
+       g_return_if_fail (content_hash != NULL);
+       g_return_if_fail (flag != E_CONTENT_EDITOR_GET_ALL);
+       g_return_if_fail (data != NULL);
+
+       g_hash_table_insert ((GHashTable *) content_hash, GUINT_TO_POINTER (flag), content_hash_data_new 
(data, destroy_data));
+}
+
+void
+e_content_editor_util_take_content_data_images (EContentEditorContentHash *content_hash,
+                                               GSList *image_parts) /* CamelMimePart * */
+{
+       g_return_if_fail (content_hash != NULL);
+       g_return_if_fail (image_parts != NULL);
 
-       iface->insert_image_from_mime_part (editor, part);
+       g_hash_table_insert ((GHashTable *) content_hash, GUINT_TO_POINTER 
(E_CONTENT_EDITOR_GET_INLINE_IMAGES),
+               content_hash_data_new (image_parts, content_data_free_obj_slist));
+}
+
+/* The actual data type depends on the @flag. The E_CONTENT_EDITOR_GET_INLINE_IMAGES returns
+   a GSList of CamelMimePart-s of inline images. All the other flags return plain strings.
+
+   The returned pointer is owned by content_hash and cannot be freed
+   neither modified. It's freed together with the content_hash, or
+   when its key is overwritten.
+ */
+gpointer
+e_content_editor_util_get_content_data (EContentEditorContentHash *content_hash,
+                                       EContentEditorGetContentFlags flag)
+{
+       ContentHashData *chd;
+
+       g_return_val_if_fail (content_hash != NULL, NULL);
+       g_return_val_if_fail (flag != E_CONTENT_EDITOR_GET_ALL, NULL);
+
+       chd = g_hash_table_lookup ((GHashTable *) content_hash, GUINT_TO_POINTER (flag));
+
+       return chd ? chd->data : NULL;
+}
+
+/* The same rules apply as with e_content_editor_util_get_content_data(). The difference is
+   that after calling this function the data is stoled from the content_hash and the caller
+   is responsible to free it. Any following calls with the same flag will return %NULL.
+ */
+gpointer
+e_content_editor_util_steal_content_data (EContentEditorContentHash *content_hash,
+                                         EContentEditorGetContentFlags flag,
+                                         GDestroyNotify *out_destroy_data)
+{
+       ContentHashData *chd;
+       gpointer data;
+
+       if (out_destroy_data)
+               *out_destroy_data = NULL;
+
+       g_return_val_if_fail (content_hash != NULL, NULL);
+       g_return_val_if_fail (flag != E_CONTENT_EDITOR_GET_ALL, NULL);
+
+       chd = g_hash_table_lookup ((GHashTable *) content_hash, GUINT_TO_POINTER (flag));
+
+       if (!chd)
+               return NULL;
+
+       data = chd->data;
+
+       if (out_destroy_data)
+               *out_destroy_data = chd->destroy_data;
+
+       chd->data = NULL;
+       chd->destroy_data = NULL;
+
+       return data;
+}
+
+/**
+ * e_content_editor_util_create_data_mimepart:
+ * @uri: a file:// or data: URI of the data to convert to MIME part
+ * @cid: content ID to use for the MIME part, should start with "cid:"; can be %NULL
+ * @as_inline: whether to use "inline" content disposition; will use "attachment", if set to %FALSE
+ * @prefer_filename: preferred file name to use, can be %NULL
+ * @prefer_mime_type: preferred MIME type for the part, can be %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ *
+ * Converts URI into a #CamelMimePart. Supports file:// and data: URIs.
+ * The @prefer_filename can override the file name from the @uri.
+ *
+ * Free the returned pointer, if not %NULL, with g_object_unref(), when
+ * no longer needed.
+ *
+ * Returns: (transfer full) (nullable): a new #CamelMimePart containing
+ *    the referenced data, or %NULL, when cannot be converted (due to
+ *    unsupported URI, file not found or such).
+ *
+ * Since: 3.38
+ **/
+CamelMimePart *
+e_content_editor_util_create_data_mimepart (const gchar *uri,
+                                           const gchar *cid,
+                                           gboolean as_inline,
+                                           const gchar *prefer_filename,
+                                           const gchar *prefer_mime_type,
+                                           GCancellable *cancellable)
+{
+       CamelMimePart *mime_part = NULL;
+       GInputStream *input_stream = NULL;
+       GFileInfo *file_info = NULL;
+       gchar *mime_type = NULL;
+       guchar *data = NULL;
+       gsize data_length = 0;
+
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       /* base64-encoded "data:" URIs */
+       if (g_ascii_strncasecmp (uri, "data:", 5) == 0) {
+               /* data:[<mime type>][;charset=<charset>][;base64],<encoded data> */
+               const gchar *ptr, *from;
+               gboolean is_base64 = FALSE;
+
+               ptr = uri + 5;
+               from = ptr;
+               while (*ptr && *ptr != ',') {
+                       ptr++;
+
+                       if (*ptr == ',' || *ptr == ';') {
+                               if (g_ascii_strncasecmp (from, "base64", ptr - from) == 0)
+                                       is_base64 = TRUE;
+
+                               if (from == uri + 5 && *ptr == ';' && !prefer_mime_type)
+                                       mime_type = g_strndup (from, ptr - from);
+
+                               from = ptr + 1;
+                       }
+               }
+
+               if (is_base64 && *ptr == ',') {
+                       data = g_base64_decode (ptr + 1, &data_length);
+
+                       if (data && data_length && !mime_type && !prefer_mime_type) {
+                               gchar *content_type;
+
+                               content_type = g_content_type_guess (NULL, data, data_length, NULL);
+
+                               if (content_type) {
+                                       mime_type = g_content_type_get_mime_type (content_type);
+                                       g_free (content_type);
+                               }
+                       }
+               }
+
+       /* files on the disk */
+       } else if (g_ascii_strncasecmp (uri, "file://", 7) == 0 ||
+                  g_ascii_strncasecmp (uri, "evo-file://", 11) == 0) {
+               GFileInputStream *file_stream;
+               GFile *file;
+
+               if (g_ascii_strncasecmp (uri, "evo-", 4) == 0)
+                       uri += 4;
+
+               file = g_file_new_for_uri (uri);
+               file_stream = g_file_read (file, NULL, NULL);
+
+               if (file_stream) {
+                       if (!prefer_filename) {
+                               file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, 
G_FILE_QUERY_INFO_NONE, cancellable, NULL);
+
+                               if (file_info)
+                                       prefer_filename = g_file_info_get_display_name (file_info);
+                       }
+
+                       if (!prefer_mime_type)
+                               mime_type = e_util_guess_mime_type (uri, TRUE);
+
+                       input_stream = (GInputStream *) file_stream;
+               }
+
+               g_clear_object (&file);
+       }
+
+       if (data || input_stream) {
+               if (!prefer_mime_type)
+                       prefer_mime_type = mime_type;
+
+               if (!prefer_mime_type)
+                       prefer_mime_type = "application/octet-stream";
+
+               if (input_stream) {
+                       CamelDataWrapper *wrapper;
+
+                       wrapper = camel_data_wrapper_new ();
+
+                       if (camel_data_wrapper_construct_from_input_stream_sync (wrapper, input_stream, 
cancellable, NULL)) {
+                               camel_data_wrapper_set_mime_type (wrapper, prefer_mime_type);
+
+                               mime_part = camel_mime_part_new ();
+                               camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper);
+                       }
+
+                       g_object_unref (wrapper);
+               } else {
+                       mime_part = camel_mime_part_new ();
+                       camel_mime_part_set_content (mime_part, (const gchar *) data, data_length, 
prefer_mime_type);
+               }
+
+               if (mime_part) {
+                       camel_mime_part_set_disposition (mime_part, as_inline ? "inline" : "attachment");
+
+                       if (cid && g_ascii_strncasecmp (cid, "cid:", 4) == 0)
+                               cid += 4;
+
+                       if (cid && *cid)
+                               camel_mime_part_set_content_id (mime_part, cid);
+
+                       if (prefer_filename && *prefer_filename)
+                               camel_mime_part_set_filename (mime_part, prefer_filename);
+
+                       camel_mime_part_set_encoding (mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
+               }
+       }
+
+       g_clear_object (&input_stream);
+       g_clear_object (&file_info);
+       g_free (mime_type);
+       g_free (data);
+
+       return mime_part;
 }
 
 /**
@@ -1799,30 +2074,6 @@ e_content_editor_select_all (EContentEditor *editor)
        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
@@ -1901,31 +2152,6 @@ e_content_editor_selection_unindent (EContentEditor *editor)
        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
@@ -2119,34 +2345,6 @@ e_content_editor_selection_wrap (EContentEditor *editor)
        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)
 {
@@ -2337,22 +2535,24 @@ e_content_editor_insert_row_below (EContentEditor *editor)
        iface->insert_row_below (editor);
 }
 
-gboolean
-e_content_editor_on_h_rule_dialog_open (EContentEditor *editor)
+void
+e_content_editor_on_dialog_open (EContentEditor *editor,
+                                const gchar *name)
 {
        EContentEditorInterface *iface;
 
-       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
 
        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);
+       g_return_if_fail (iface != NULL);
+       g_return_if_fail (iface->on_dialog_open != NULL);
 
-       return iface->on_h_rule_dialog_open (editor);
+       iface->on_dialog_open (editor, name);
 }
 
 void
-e_content_editor_on_h_rule_dialog_close (EContentEditor *editor)
+e_content_editor_on_dialog_close (EContentEditor *editor,
+                                 const gchar *name)
 {
        EContentEditorInterface *iface;
 
@@ -2360,9 +2560,9 @@ e_content_editor_on_h_rule_dialog_close (EContentEditor *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);
+       g_return_if_fail (iface->on_dialog_close != NULL);
 
-       iface->on_h_rule_dialog_close (editor);
+       iface->on_dialog_close (editor, name);
 }
 
 void
@@ -2484,34 +2684,6 @@ e_content_editor_h_rule_get_no_shade (EContentEditor *editor)
        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)
@@ -2834,21 +3006,9 @@ e_content_editor_image_set_height_follow (EContentEditor *editor,
 }
 
 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)
+e_content_editor_link_get_properties (EContentEditor *editor,
+                                     gchar **href,
+                                     gchar **text)
 {
        EContentEditorInterface *iface;
 
@@ -2856,15 +3016,15 @@ e_content_editor_on_link_dialog_close (EContentEditor *editor)
 
        iface = E_CONTENT_EDITOR_GET_IFACE (editor);
        g_return_if_fail (iface != NULL);
-       g_return_if_fail (iface->on_link_dialog_close != NULL);
+       g_return_if_fail (iface->link_get_properties != NULL);
 
-       iface->on_link_dialog_close (editor);
+       iface->link_get_properties (editor, href, text);
 }
 
 void
-e_content_editor_link_get_values (EContentEditor *editor,
-                                  gchar **href,
-                                  gchar **text)
+e_content_editor_link_set_properties (EContentEditor *editor,
+                                     const gchar *href,
+                                     const gchar *text)
 {
        EContentEditorInterface *iface;
 
@@ -2872,53 +3032,9 @@ e_content_editor_link_get_values (EContentEditor *editor,
 
        iface = E_CONTENT_EDITOR_GET_IFACE (editor);
        g_return_if_fail (iface != NULL);
-       g_return_if_fail (iface->link_get_values != NULL);
+       g_return_if_fail (iface->link_set_properties != 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);
+       iface->link_set_properties (editor, href, text);
 }
 
 void
@@ -3049,10 +3165,9 @@ e_content_editor_page_get_visited_link_color (EContentEditor *editor,
        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)
+e_content_editor_page_set_font_name (EContentEditor *editor,
+                                    const gchar *value)
 {
        EContentEditorInterface *iface;
 
@@ -3060,13 +3175,13 @@ e_content_editor_page_set_background_image_uri (EContentEditor *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);
+       g_return_if_fail (iface->page_set_font_name != NULL);
 
-       iface->page_set_background_image_uri (editor, uri);
+       iface->page_set_font_name (editor, value);
 }
 
-gchar *
-e_content_editor_page_get_background_image_uri (EContentEditor *editor)
+const gchar *
+e_content_editor_page_get_font_name (EContentEditor *editor)
 {
        EContentEditorInterface *iface;
 
@@ -3074,13 +3189,15 @@ e_content_editor_page_get_background_image_uri (EContentEditor *editor)
 
        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);
+       g_return_val_if_fail (iface->page_get_font_name != NULL, NULL);
 
-       return iface->page_get_background_image_uri (editor);
+       return iface->page_get_font_name (editor);
 }
 
+/* uri could be NULL -> removes the current image */
 void
-e_content_editor_on_cell_dialog_open (EContentEditor *editor)
+e_content_editor_page_set_background_image_uri (EContentEditor *editor,
+                                                const gchar *uri)
 {
        EContentEditorInterface *iface;
 
@@ -3088,23 +3205,23 @@ e_content_editor_on_cell_dialog_open (EContentEditor *editor)
 
        iface = E_CONTENT_EDITOR_GET_IFACE (editor);
        g_return_if_fail (iface != NULL);
-       g_return_if_fail (iface->on_cell_dialog_open != NULL);
+       g_return_if_fail (iface->page_set_background_image_uri != NULL);
 
-       iface->on_cell_dialog_open (editor);
+       iface->page_set_background_image_uri (editor, uri);
 }
 
-void
-e_content_editor_on_cell_dialog_close (EContentEditor *editor)
+gchar *
+e_content_editor_page_get_background_image_uri (EContentEditor *editor)
 {
        EContentEditorInterface *iface;
 
-       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
 
        iface = E_CONTENT_EDITOR_GET_IFACE (editor);
-       g_return_if_fail (iface != NULL);
-       g_return_if_fail (iface->on_cell_dialog_close != NULL);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->page_get_background_image_uri != NULL, NULL);
 
-       iface->on_cell_dialog_close (editor);
+       return iface->page_get_background_image_uri (editor);
 }
 
 void
@@ -3648,62 +3765,6 @@ e_content_editor_table_set_background_color (EContentEditor *editor,
        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)
@@ -3734,62 +3795,6 @@ e_content_editor_spell_check_prev_word (EContentEditor *editor,
        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)
 {
@@ -3822,18 +3827,15 @@ e_content_editor_emit_paste_primary_clipboard (EContentEditor *editor)
        return handled;
 }
 
-gboolean
+void
 e_content_editor_emit_context_menu_requested (EContentEditor *editor,
-                                              EContentEditorNodeFlags flags,
-                                              GdkEvent *event)
+                                             EContentEditorNodeFlags flags,
+                                             const gchar *caret_word,
+                                             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);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
 
-       return handled;
+       g_signal_emit (editor, signals[CONTEXT_MENU_REQUESTED], 0, flags, caret_word, event, NULL);
 }
 
 void
@@ -3869,3 +3871,17 @@ e_content_editor_emit_content_changed (EContentEditor *editor)
 
        g_signal_emit (editor, signals[CONTENT_CHANGED], 0);
 }
+
+CamelMimePart *
+e_content_editor_emit_ref_mime_part (EContentEditor *editor,
+                                    const gchar *uri)
+{
+       CamelMimePart *mime_part = NULL;
+
+       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       g_signal_emit (editor, signals[REF_MIME_PART], 0, uri, &mime_part);
+
+       return mime_part;
+}
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index f1630522a2..ae00b147f4 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -33,6 +33,16 @@
 
 #define DEFAULT_CONTENT_EDITOR_NAME "WebKit"
 
+#define E_CONTENT_EDITOR_DIALOG_HRULE          "hrule"
+#define E_CONTENT_EDITOR_DIALOG_IMAGE          "image"
+#define E_CONTENT_EDITOR_DIALOG_LINK           "link"
+#define E_CONTENT_EDITOR_DIALOG_PAGE           "page"
+#define E_CONTENT_EDITOR_DIALOG_CELL           "cell"
+#define E_CONTENT_EDITOR_DIALOG_TABLE          "table"
+#define E_CONTENT_EDITOR_DIALOG_SPELLCHECK     "spellcheck"
+#define E_CONTENT_EDITOR_DIALOG_FIND           "find"
+#define E_CONTENT_EDITOR_DIALOG_REPLACE                "replace"
+
 G_BEGIN_DECLS
 
 struct _EHTMLEditor;
@@ -40,6 +50,8 @@ struct _EHTMLEditor;
 #define E_TYPE_CONTENT_EDITOR e_content_editor_get_type ()
 G_DECLARE_INTERFACE (EContentEditor, e_content_editor, E, CONTENT_EDITOR, GtkWidget)
 
+typedef GHashTable EContentEditorContentHash;
+
 typedef void (*EContentEditorInitializedCallback)      (EContentEditor *content_editor,
                                                         gpointer user_data);
 
@@ -56,18 +68,20 @@ struct _EContentEditorInterface {
                                                         const gchar *content,
                                                         EContentEditorInsertContentFlags flags);
 
-       gchar *         (*get_content)                  (EContentEditor *editor,
-                                                        EContentEditorGetContentFlags flags,
+       void            (*get_content)                  (EContentEditor *editor,
+                                                        guint32 flags, /* bit-or of 
EContentEditorGetContentFlags */
                                                         const gchar *inline_images_from_domain,
-                                                        GSList **inline_images_parts /* newly created 
CamelMimePart * */);
+                                                        GCancellable *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer user_data);
+       EContentEditorContentHash *
+                       (*get_content_finish)           (EContentEditor *editor,
+                                                        GAsyncResult *result,
+                                                        GError **error);
 
        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);
 
@@ -93,8 +107,6 @@ struct _EContentEditorInterface {
        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,
@@ -106,9 +118,6 @@ struct _EContentEditorInterface {
 
        void            (*selection_unindent)           (EContentEditor *editor);
 
-       void            (*selection_create_link)        (EContentEditor *editor,
-                                                        const gchar *uri);
-
        void            (*selection_unlink)             (EContentEditor *editor);
 
        void            (*find)                         (EContentEditor *editor,
@@ -129,10 +138,6 @@ struct _EContentEditorInterface {
 
        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);
@@ -161,9 +166,11 @@ struct _EContentEditorInterface {
 
        void            (*insert_row_below)             (EContentEditor *editor);
 
-       gboolean        (*on_h_rule_dialog_open)        (EContentEditor *editor);
+       void            (*on_dialog_open)               (EContentEditor *editor,
+                                                        const gchar *name);
 
-       void            (*on_h_rule_dialog_close)       (EContentEditor *editor);
+       void            (*on_dialog_close)              (EContentEditor *editor,
+                                                        const gchar *name);
 
        void            (*h_rule_set_align)             (EContentEditor *editor,
                                                         const gchar *value);
@@ -187,10 +194,6 @@ struct _EContentEditorInterface {
 
        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);
 
@@ -246,22 +249,14 @@ struct _EContentEditorInterface {
 
        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,
+       void            (*link_get_properties)          (EContentEditor *editor,
                                                         gchar **href,
                                                         gchar **text);
 
-       void            (*link_set_values)              (EContentEditor *editor,
+       void            (*link_set_properties)          (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);
 
@@ -285,6 +280,9 @@ struct _EContentEditorInterface {
 
        void            (*page_get_visited_link_color)  (EContentEditor *editor,
                                                         GdkRGBA *value);
+       void            (*page_set_font_name)           (EContentEditor *editor,
+                                                        const gchar *value);
+       const gchar *   (*page_get_font_name)           (EContentEditor *editor);
 
        void            (*page_set_background_image_uri)
                                                        (EContentEditor *editor,
@@ -293,10 +291,6 @@ struct _EContentEditorInterface {
        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);
@@ -405,34 +399,19 @@ struct _EContentEditorInterface {
        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,
+       void            (*context_menu_requested)       (EContentEditor *editor,
                                                         EContentEditorNodeFlags flags,
+                                                        const gchar *caret_word,
                                                         GdkEvent *event);
        void            (*find_done)                    (EContentEditor *editor,
                                                         guint match_count);
@@ -440,6 +419,8 @@ struct _EContentEditorInterface {
                                                         guint replaced_count);
        void            (*drop_handled)                 (EContentEditor *editor);
        void            (*content_changed)              (EContentEditor *editor);
+       CamelMimePart * (*ref_mime_part)                (EContentEditor *editor,
+                                                        const gchar *uri);
 };
 
 /* Properties */
@@ -452,7 +433,7 @@ 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);
+gint           e_content_editor_indent_level   (EContentEditor *editor);
 gboolean       e_content_editor_get_spell_check_enabled
                                                (EContentEditor *editor);
 void           e_content_editor_set_spell_check_enabled
@@ -497,9 +478,6 @@ 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);
@@ -543,14 +521,48 @@ void              e_content_editor_insert_content (EContentEditor *editor,
                                                 const gchar *content,
                                                 EContentEditorInsertContentFlags flags);
 
-gchar *                e_content_editor_get_content    (EContentEditor *editor,
-                                                EContentEditorGetContentFlags flags,
+void           e_content_editor_get_content    (EContentEditor *editor,
+                                                guint32 flags, /* bit-or of EContentEditorGetContentFlags */
                                                 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);
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+EContentEditorContentHash *
+               e_content_editor_get_content_finish
+                                               (EContentEditor *editor,
+                                                GAsyncResult *result,
+                                                GError **error);
+EContentEditorContentHash *
+               e_content_editor_util_new_content_hash
+                                               (void);
+void           e_content_editor_util_free_content_hash
+                                               (EContentEditorContentHash *content_hash);
+void           e_content_editor_util_put_content_data
+                                               (EContentEditorContentHash *content_hash,
+                                                EContentEditorGetContentFlags flag,
+                                                const gchar *data);
+void           e_content_editor_util_take_content_data
+                                               (EContentEditorContentHash *content_hash,
+                                                EContentEditorGetContentFlags flag,
+                                                gpointer data,
+                                                GDestroyNotify destroy_data);
+void           e_content_editor_util_take_content_data_images
+                                               (EContentEditorContentHash *content_hash,
+                                                GSList *image_parts); /* CamelMimePart * */
+gpointer       e_content_editor_util_get_content_data
+                                               (EContentEditorContentHash *content_hash,
+                                                EContentEditorGetContentFlags flag);
+gpointer       e_content_editor_util_steal_content_data
+                                               (EContentEditorContentHash *content_hash,
+                                                EContentEditorGetContentFlags flag,
+                                                GDestroyNotify *out_destroy_data);
+CamelMimePart *        e_content_editor_util_create_data_mimepart
+                                               (const gchar *uri,
+                                                const gchar *cid,
+                                                gboolean as_inline,
+                                                const gchar *prefer_filename,
+                                                const gchar *prefer_mime_type,
+                                                GCancellable *cancellable);
 
 void           e_content_editor_insert_image   (EContentEditor *editor,
                                                 const gchar *uri);
@@ -586,9 +598,6 @@ void                e_content_editor_set_spell_checking_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
@@ -601,10 +610,6 @@ void               e_content_editor_selection_indent
 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);
 
@@ -627,12 +632,6 @@ void               e_content_editor_selection_restore
 
 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);
 
@@ -671,11 +670,10 @@ void              e_content_editor_insert_row_above
 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_on_dialog_open (EContentEditor *editor,
+                                                const gchar *name);
+void           e_content_editor_on_dialog_close(EContentEditor *editor,
+                                                const gchar *name);
 
 void           e_content_editor_h_rule_set_align
                                                (EContentEditor *editor,
@@ -707,12 +705,6 @@ void               e_content_editor_h_rule_set_no_shade
 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);
 
@@ -783,18 +775,12 @@ 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
+void           e_content_editor_link_get_properties
                                                (EContentEditor *editor,
                                                 gchar **href,
                                                 gchar **text);
 
-void           e_content_editor_link_set_values
+void           e_content_editor_link_set_properties
                                                (EContentEditor *editor,
                                                 const gchar *href,
                                                 const gchar *text);
@@ -803,12 +789,6 @@ 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);
@@ -836,6 +816,12 @@ void               e_content_editor_page_set_visited_link_color
 void           e_content_editor_page_get_visited_link_color
                                                (EContentEditor *editor,
                                                 GdkRGBA *value);
+void           e_content_editor_page_set_font_name
+                                               (EContentEditor *editor,
+                                                const gchar *value);
+
+const gchar *  e_content_editor_page_get_font_name
+                                               (EContentEditor *editor);
 
 void           e_content_editor_page_set_background_image_uri
                                                (EContentEditor *editor,
@@ -844,12 +830,6 @@ void               e_content_editor_page_set_background_image_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,
@@ -983,18 +963,6 @@ 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);
@@ -1008,18 +976,6 @@ void              e_content_editor_spell_check_replace_all
                                                 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
@@ -1028,9 +984,10 @@ 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
+void           e_content_editor_emit_context_menu_requested
                                                (EContentEditor *editor,
                                                 EContentEditorNodeFlags flags,
+                                                const gchar *caret_word,
                                                 GdkEvent *event);
 void           e_content_editor_emit_find_done (EContentEditor *editor,
                                                 guint match_count);
@@ -1041,6 +998,9 @@ void               e_content_editor_emit_drop_handled
                                                (EContentEditor *editor);
 void           e_content_editor_emit_content_changed
                                                (EContentEditor *editor);
+CamelMimePart *        e_content_editor_emit_ref_mime_part
+                                               (EContentEditor *editor,
+                                                const gchar *uri);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index 932ba9479c..056158a030 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -605,18 +605,21 @@ update_mode_combobox (gpointer data)
 }
 
 static void
-action_mode_cb (GtkRadioAction *action,
-                GtkRadioAction *current,
-                EHTMLEditor *editor)
+html_editor_actions_notify_html_mode_cb (EContentEditor *cnt_editor,
+                                        GParamSpec *param,
+                                        EHTMLEditor *editor)
 {
-       EContentEditor *cnt_editor;
        GtkActionGroup *action_group;
        GtkWidget *style_combo_box;
        gboolean is_html;
 
-       cnt_editor = e_html_editor_get_content_editor (editor);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
        is_html = e_content_editor_get_html_mode (cnt_editor);
 
+       g_object_set (G_OBJECT (editor->priv->html_actions), "sensitive", is_html, NULL);
+
        /* This must be done from idle callback, because apparently we can change
         * current value in callback of current value change */
        g_idle_add (update_mode_combobox, editor);
@@ -627,7 +630,8 @@ action_mode_cb (GtkRadioAction *action,
        action_group = editor->priv->html_context_actions;
        gtk_action_group_set_visible (action_group, is_html);
 
-       gtk_widget_set_sensitive (editor->priv->color_combo_box, is_html);
+       gtk_widget_set_sensitive (editor->priv->fg_color_combo_box, is_html);
+       gtk_widget_set_sensitive (editor->priv->bg_color_combo_box, is_html);
 
        if (is_html) {
                gtk_widget_show (editor->priv->html_toolbar);
@@ -656,6 +660,15 @@ action_mode_cb (GtkRadioAction *action,
        e_action_combo_box_update_model (E_ACTION_COMBO_BOX (style_combo_box));
 }
 
+static void
+action_mode_cb (GtkRadioAction *action,
+               GtkRadioAction *current,
+               EHTMLEditor *editor)
+{
+       /* Nothing to do here, wait for notification of
+          a property change from the EContentEditor */
+}
+
 static void
 clipboard_text_received_for_paste_as_text (GtkClipboard *clipboard,
                                            const gchar *text,
@@ -956,6 +969,96 @@ action_wrap_lines_cb (GtkAction *action,
                e_content_editor_selection_wrap (cnt_editor);
 }
 
+/* This is when the user toggled the action */
+static void
+manage_format_subsuperscript_toggled (EHTMLEditor *editor,
+                                     GtkToggleAction *changed_action,
+                                     const gchar *prop_name,
+                                     GtkToggleAction *second_action)
+{
+       EContentEditor *cnt_editor = e_html_editor_get_content_editor (editor);
+
+       g_signal_handlers_block_matched (cnt_editor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_block_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_block_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+
+       if (gtk_toggle_action_get_active (changed_action) &&
+           gtk_toggle_action_get_active (second_action))
+               gtk_toggle_action_set_active (second_action, FALSE);
+
+       g_object_set (G_OBJECT (cnt_editor), prop_name, gtk_toggle_action_get_active (changed_action), NULL);
+
+       g_signal_handlers_unblock_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_unblock_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_unblock_matched (cnt_editor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+}
+
+/* This is when the content editor claimed change on the property */
+static void
+manage_format_subsuperscript_notify (EHTMLEditor *editor,
+                                    GtkToggleAction *changed_action,
+                                    const gchar *prop_name,
+                                    GtkToggleAction *second_action)
+{
+       EContentEditor *cnt_editor = e_html_editor_get_content_editor (editor);
+       gboolean value = FALSE;
+
+       g_signal_handlers_block_matched (cnt_editor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_block_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_block_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+
+       g_object_get (G_OBJECT (cnt_editor), prop_name, &value, NULL);
+
+       gtk_toggle_action_set_active (changed_action, value);
+
+       if (gtk_toggle_action_get_active (changed_action) &&
+           gtk_toggle_action_get_active (second_action))
+               gtk_toggle_action_set_active (second_action, FALSE);
+
+       g_signal_handlers_unblock_matched (second_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_unblock_matched (changed_action, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+       g_signal_handlers_unblock_matched (cnt_editor, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor);
+}
+
+static void
+html_editor_actions_subscript_toggled_cb (GtkToggleAction *action,
+                                         EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       manage_format_subsuperscript_toggled (editor, GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)), "subscript", 
GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)));
+}
+
+static void
+html_editor_actions_notify_subscript_cb (EContentEditor *cnt_editor,
+                                        GParamSpec *param,
+                                        EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       manage_format_subsuperscript_notify (editor, GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)), "subscript", 
GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)));
+}
+
+static void
+html_editor_actions_superscript_toggled_cb (GtkToggleAction *action,
+                                           EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       manage_format_subsuperscript_toggled (editor, GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)), 
"superscript", GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)));
+}
+
+static void
+html_editor_actions_notify_superscript_cb (EContentEditor *cnt_editor,
+                                          GParamSpec *param,
+                                          EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       manage_format_subsuperscript_notify (editor, GTK_TOGGLE_ACTION (ACTION (SUPERSCRIPT)), "superscript", 
GTK_TOGGLE_ACTION (ACTION (SUBSCRIPT)));
+}
+
+
 /*****************************************************************************
  * Core Actions
  *
@@ -1161,6 +1264,13 @@ static GtkRadioActionEntry core_justify_entries[] = {
          N_("Center Alignment"),
          E_CONTENT_EDITOR_ALIGNMENT_CENTER },
 
+       { "justify-fill",
+         "format-justify-fill",
+         N_("_Justified"),
+         "<Control>j",
+         N_("Align Justified"),
+         E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY },
+
        { "justify-left",
          "format-justify-left",
          N_("_Left"),
@@ -1412,14 +1522,6 @@ static GtkToggleActionEntry html_toggle_entries[] = {
          NULL,
          FALSE },
 
-       { "monospaced",
-         "stock_text-monospaced",
-         N_("_Plain Text"),
-         "<Control>t",
-         N_("Plain Text"),
-         NULL,
-         FALSE },
-
        { "strikethrough",
          "format-text-strikethrough",
          N_("_Strikethrough"),
@@ -1428,6 +1530,22 @@ static GtkToggleActionEntry html_toggle_entries[] = {
          NULL,
          FALSE },
 
+       { "subscript",
+         NULL,
+         N_("Subs_cript"),
+         "<Control><Shift>b",
+         N_("Subscript"),
+         NULL,
+         FALSE },
+
+       { "superscript",
+         NULL,
+         N_("Su_perscript"),
+         "<Control><Shift>p",
+         N_("Superscript"),
+         NULL,
+         FALSE },
+
        { "underline",
          "format-text-underline",
          N_("_Underline"),
@@ -2110,6 +2228,42 @@ editor_actions_init (EHTMLEditor *editor)
        gtk_action_set_sensitive (ACTION (FIND_AGAIN), FALSE);
 }
 
+static gboolean
+e_html_editor_content_editor_font_name_to_combo_box (GBinding *binding,
+                                                    const GValue *from_value,
+                                                    GValue *to_value,
+                                                    gpointer user_data)
+{
+       gchar *id = NULL;
+
+       id = e_html_editor_util_dup_font_id (GTK_COMBO_BOX (g_binding_get_target (binding)), 
g_value_get_string (from_value));
+       g_value_take_string (to_value, id ? id : g_strdup (""));
+
+       return TRUE;
+}
+
+static gboolean
+e_html_editor_indent_level_to_bool_indent_cb (GBinding *binding,
+                                             const GValue *from_value,
+                                             GValue *to_value,
+                                             gpointer user_data)
+{
+       g_value_set_boolean (to_value, g_value_get_int (from_value) < E_HTML_EDITOR_MAX_INDENT_LEVEL);
+
+       return TRUE;
+}
+
+static gboolean
+e_html_editor_indent_level_to_bool_unindent_cb (GBinding *binding,
+                                               const GValue *from_value,
+                                               GValue *to_value,
+                                               gpointer user_data)
+{
+       g_value_set_boolean (to_value, g_value_get_int (from_value) > 0);
+
+       return TRUE;
+}
+
 void
 editor_actions_bind (EHTMLEditor *editor)
 {
@@ -2174,18 +2328,22 @@ editor_actions_bind (EHTMLEditor *editor)
                cnt_editor, "block-format",
                ACTION (STYLE_NORMAL), "current-value",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
-               cnt_editor, "indented",
+       e_binding_bind_property_full (
+               cnt_editor, "indent-level",
+               ACTION (INDENT), "sensitive",
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_indent_level_to_bool_indent_cb,
+               NULL, NULL, NULL);
+       e_binding_bind_property_full (
+               cnt_editor, "indent-level",
                ACTION (UNINDENT), "sensitive",
-               G_BINDING_SYNC_CREATE);
+               G_BINDING_SYNC_CREATE,
+               e_html_editor_indent_level_to_bool_unindent_cb,
+               NULL, NULL, NULL);
        e_binding_bind_property (
                cnt_editor, "italic",
                ACTION (ITALIC), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-       e_binding_bind_property (
-               cnt_editor, "monospaced",
-               ACTION (MONOSPACED), "active",
-               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
        e_binding_bind_property (
                cnt_editor, "strikethrough",
                ACTION (STRIKETHROUGH), "active",
@@ -2194,11 +2352,26 @@ editor_actions_bind (EHTMLEditor *editor)
                cnt_editor, "underline",
                ACTION (UNDERLINE), "active",
                G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       e_binding_bind_property_full (
+               cnt_editor, "font-name",
+               editor->priv->font_name_combo_box, "active-id",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
+               e_html_editor_content_editor_font_name_to_combo_box,
+               NULL,
+               NULL, NULL);
 
-       e_binding_bind_property (
-               cnt_editor, "html-mode",
-               editor->priv->html_actions, "sensitive",
-               G_BINDING_SYNC_CREATE);
+       /* Cannot use binding, due to subscript and superscript being mutually exclusive */
+       g_signal_connect_object (ACTION (SUBSCRIPT), "toggled",
+               G_CALLBACK (html_editor_actions_subscript_toggled_cb), editor, 0);
+       g_signal_connect_object (cnt_editor, "notify::subscript",
+               G_CALLBACK (html_editor_actions_notify_subscript_cb), editor, 0);
+       g_signal_connect_object (ACTION (SUPERSCRIPT), "toggled",
+               G_CALLBACK (html_editor_actions_superscript_toggled_cb), editor, 0);
+       g_signal_connect_object (cnt_editor, "notify::superscript",
+               G_CALLBACK (html_editor_actions_notify_superscript_cb), editor, 0);
+
+       g_signal_connect_object (cnt_editor, "notify::html-mode",
+               G_CALLBACK (html_editor_actions_notify_html_mode_cb), editor, 0);
 
        /* Disable all actions and toolbars when editor is not editable */
        e_binding_bind_property (
diff --git a/src/e-util/e-html-editor-actions.h b/src/e-util/e-html-editor-actions.h
index 74b2773eee..a7898168bb 100644
--- a/src/e-util/e-html-editor-actions.h
+++ b/src/e-util/e-html-editor-actions.h
@@ -83,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_INDENT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "indent")
 #define E_HTML_EDITOR_ACTION_INSERT_EMOTICON(editor) \
        E_HTML_EDITOR_ACTION ((editor), "insert-emoticon")
 #define E_HTML_EDITOR_ACTION_INSERT_EMOJI(editor) \
@@ -101,6 +103,8 @@
        E_HTML_EDITOR_ACTION ((editor), "italic")
 #define E_HTML_EDITOR_ACTION_JUSTIFY_CENTER(editor) \
        E_HTML_EDITOR_ACTION ((editor), "justify-center")
+#define E_HTML_EDITOR_ACTION_JUSTIFY_FILL(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "justify-fill")
 #define E_HTML_EDITOR_ACTION_JUSTIFY_LEFT(editor) \
        E_HTML_EDITOR_ACTION ((editor), "justify-left")
 #define E_HTML_EDITOR_ACTION_JUSTIFY_RIGHT(editor) \
@@ -111,8 +115,6 @@
        E_HTML_EDITOR_ACTION ((editor), "mode-html")
 #define E_HTML_EDITOR_ACTION_MODE_PLAIN(editor) \
        E_HTML_EDITOR_ACTION ((editor), "mode-plain")
-#define E_HTML_EDITOR_ACTION_MONOSPACED(editor) \
-       E_HTML_EDITOR_ACTION ((editor), "monospaced")
 #define E_HTML_EDITOR_ACTION_PASTE(editor) \
        E_HTML_EDITOR_ACTION ((editor), "paste")
 #define E_HTML_EDITOR_ACTION_PASTE_QUOTE(editor) \
@@ -151,6 +153,10 @@
        E_HTML_EDITOR_ACTION ((editor), "style-h6")
 #define E_HTML_EDITOR_ACTION_STYLE_NORMAL(editor) \
        E_HTML_EDITOR_ACTION ((editor), "style-normal")
+#define E_HTML_EDITOR_ACTION_SUBSCRIPT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "subscript")
+#define E_HTML_EDITOR_ACTION_SUPERSCRIPT(editor) \
+       E_HTML_EDITOR_ACTION ((editor), "superscript")
 #define E_HTML_EDITOR_ACTION_TEST_URL(editor) \
        E_HTML_EDITOR_ACTION ((editor), "test-url")
 #define E_HTML_EDITOR_ACTION_UNDERLINE(editor) \
diff --git a/src/e-util/e-html-editor-cell-dialog.c b/src/e-util/e-html-editor-cell-dialog.c
index 9f218e9b1f..87cad6fa62 100644
--- a/src/e-util/e-html-editor-cell-dialog.c
+++ b/src/e-util/e-html-editor-cell-dialog.c
@@ -304,7 +304,7 @@ html_editor_cell_dialog_show (GtkWidget *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_cell_dialog_open (cnt_editor);
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_CELL);
 
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
@@ -374,7 +374,7 @@ html_editor_cell_dialog_hide (GtkWidget *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_cell_dialog_close (cnt_editor);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_CELL);
 
        GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->hide (widget);
 }
diff --git a/src/e-util/e-html-editor-find-dialog.c b/src/e-util/e-html-editor-find-dialog.c
index 49def0b946..fe063ccc70 100644
--- a/src/e-util/e-html-editor-find-dialog.c
+++ b/src/e-util/e-html-editor-find-dialog.c
@@ -61,7 +61,7 @@ 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);
+       e_content_editor_on_dialog_close (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_FIND);
 
        /* Chain up to parent's implementation */
        GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->hide (widget);
@@ -75,7 +75,7 @@ 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);
+       e_content_editor_on_dialog_open (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_FIND);
 
        /* Chain up to parent's implementation */
        GTK_WIDGET_CLASS (e_html_editor_find_dialog_parent_class)->show (widget);
diff --git a/src/e-util/e-html-editor-hrule-dialog.c b/src/e-util/e-html-editor-hrule-dialog.c
index 342cdc1522..990196e686 100644
--- a/src/e-util/e-html-editor-hrule-dialog.c
+++ b/src/e-util/e-html-editor-hrule-dialog.c
@@ -187,7 +187,7 @@ html_editor_hrule_dialog_hide (GtkWidget *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_h_rule_dialog_close (cnt_editor);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_HRULE);
 
        GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget);
 }
@@ -198,39 +198,17 @@ html_editor_hrule_dialog_show (GtkWidget *widget)
        EHTMLEditorHRuleDialog *dialog;
        EHTMLEditor *editor;
        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));
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       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);
-               gtk_combo_box_set_active_id (
-                       GTK_COMBO_BOX (dialog->priv->unit_combo), "units-percent");
-               gtk_spin_button_set_value (
-                       GTK_SPIN_BUTTON (dialog->priv->size_edit), 2.0);
-               gtk_combo_box_set_active_id (
-                       GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
-               gtk_toggle_button_set_active (
-                       GTK_TOGGLE_BUTTON (dialog->priv->shaded_check), FALSE);
-
-               html_editor_hrule_dialog_set_alignment (dialog);
-               html_editor_hrule_dialog_set_size (dialog);
-               html_editor_hrule_dialog_set_alignment (dialog);
-               html_editor_hrule_dialog_set_shading (dialog);
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_HRULE);
 
-               e_content_editor_set_changed (cnt_editor, TRUE);
-       }
+       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);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->show (widget);
diff --git a/src/e-util/e-html-editor-image-dialog.c b/src/e-util/e-html-editor-image-dialog.c
index aa72f7b13a..7eafa1d302 100644
--- a/src/e-util/e-html-editor-image-dialog.c
+++ b/src/e-util/e-html-editor-image-dialog.c
@@ -359,7 +359,7 @@ html_editor_image_dialog_show (GtkWidget *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_image_dialog_open (cnt_editor);
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_IMAGE);
 
        value = e_content_editor_image_get_src (cnt_editor);
        if (value && *value) {
@@ -432,7 +432,7 @@ html_editor_image_dialog_hide (GtkWidget *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_image_dialog_close (cnt_editor);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_IMAGE);
 
        GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
 }
diff --git a/src/e-util/e-html-editor-link-dialog.c b/src/e-util/e-html-editor-link-dialog.c
index 368dad3c11..ce5337545f 100644
--- a/src/e-util/e-html-editor-link-dialog.c
+++ b/src/e-util/e-html-editor-link-dialog.c
@@ -94,7 +94,7 @@ html_editor_link_dialog_ok (EHTMLEditorLinkDialog *dialog)
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       e_content_editor_link_set_values (
+       e_content_editor_link_set_properties (
                cnt_editor,
                gtk_entry_get_text (GTK_ENTRY (dialog->priv->url_edit)),
                gtk_entry_get_text (GTK_ENTRY (dialog->priv->label_edit)));
@@ -127,7 +127,7 @@ html_editor_link_dialog_hide (GtkWidget *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);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_LINK);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_link_dialog_parent_class)->hide (widget);
@@ -153,21 +153,18 @@ html_editor_link_dialog_show (GtkWidget *widget)
 
        dialog->priv->label_autofill = TRUE;
 
-       e_content_editor_on_link_dialog_open (cnt_editor);
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_LINK);
 
-       e_content_editor_link_get_values (cnt_editor, &href, &text);
+       e_content_editor_link_get_properties (cnt_editor, &href, &text);
        if (href && *href)
-               gtk_entry_set_text (
-                       GTK_ENTRY (dialog->priv->url_edit), href);
+               gtk_entry_set_text (GTK_ENTRY (dialog->priv->url_edit), href);
        else
-               gtk_widget_set_sensitive (
-                       dialog->priv->remove_link_button, FALSE);
+               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);
+               gtk_entry_set_text (GTK_ENTRY (dialog->priv->label_edit), text);
                dialog->priv->label_autofill = FALSE;
        }
        g_free (text);
diff --git a/src/e-util/e-html-editor-manager.ui b/src/e-util/e-html-editor-manager.ui
index b097027a54..f263c13ef0 100644
--- a/src/e-util/e-html-editor-manager.ui
+++ b/src/e-util/e-html-editor-manager.ui
@@ -53,12 +53,13 @@
       <menuitem action='mode-plain'/>
       <separator/>
       <menu action='font-style-menu'>
-        <menuitem action='monospaced'/>
-        <separator/>
         <menuitem action='bold'/>
         <menuitem action='italic'/>
         <menuitem action='underline'/>
         <menuitem action='strikethrough'/>
+        <separator/>
+        <menuitem action='superscript'/>
+        <menuitem action='subscript'/>
       </menu>
       <menu action='font-size-menu'>
         <menuitem action='size-minus-two'/>
@@ -91,6 +92,7 @@
         <menuitem action='justify-left'/>
         <menuitem action='justify-center'/>
         <menuitem action='justify-right'/>
+        <menuitem action='justify-fill'/>
       </menu>
       <separator/>
       <menuitem action='indent'/>
@@ -117,6 +119,7 @@
     <toolitem action='justify-left'/>
     <toolitem action='justify-center'/>
     <toolitem action='justify-right'/>
+    <toolitem action='justify-fill'/>
     <separator/>
     <toolitem action='unindent'/>
     <toolitem action='indent'/>
@@ -125,7 +128,6 @@
   </toolbar>
   <toolbar name='html-toolbar'>
     <separator/>
-    <toolitem action='monospaced'/>
     <toolitem action='bold'/>
     <toolitem action='italic'/>
     <toolitem action='underline'/>
diff --git a/src/e-util/e-html-editor-page-dialog.c b/src/e-util/e-html-editor-page-dialog.c
index e86f2c9ee7..f2d5ccc1b7 100644
--- a/src/e-util/e-html-editor-page-dialog.c
+++ b/src/e-util/e-html-editor-page-dialog.c
@@ -27,6 +27,7 @@
 #include "e-color-combo.h"
 #include "e-misc-utils.h"
 #include "e-dialog-widgets.h"
+#include "e-html-editor-private.h"
 
 #define E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE(obj) \
        (G_TYPE_INSTANCE_GET_PRIVATE \
@@ -38,6 +39,8 @@ struct _EHTMLEditorPageDialogPrivate {
        GtkWidget *visited_link_color_picker;
        GtkWidget *background_color_picker;
 
+       GtkWidget *text_font_name_combo;
+
        GtkWidget *background_template_combo;
        GtkWidget *background_image_filechooser;
 
@@ -206,6 +209,18 @@ html_editor_page_dialog_set_background_color (EHTMLEditorPageDialog *dialog)
        e_content_editor_page_set_background_color (cnt_editor, &rgba);
 }
 
+static void
+html_editor_page_dialog_set_text_font_name (EHTMLEditorPageDialog *dialog)
+{
+       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_page_set_font_name (cnt_editor, gtk_combo_box_get_active_id (GTK_COMBO_BOX 
(dialog->priv->text_font_name_combo)));
+}
+
 static void
 html_editor_page_dialog_set_background_from_template (EHTMLEditorPageDialog *dialog)
 {
@@ -286,13 +301,13 @@ html_editor_page_dialog_show (GtkWidget *widget)
        EContentEditor *cnt_editor;
        EHTMLEditorPageDialog *dialog;
        GdkRGBA rgba;
-       gchar *uri;
+       gchar *uri, *font_name;
 
        dialog = E_HTML_EDITOR_PAGE_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_page_dialog_open (cnt_editor);
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_PAGE);
 
        uri = e_content_editor_page_get_background_image_uri (cnt_editor);
        if (uri && *uri) {
@@ -331,6 +346,11 @@ html_editor_page_dialog_show (GtkWidget *widget)
        e_color_combo_set_current_color (
                E_COLOR_COMBO (dialog->priv->background_color_picker), &rgba);
 
+       font_name = e_html_editor_util_dup_font_id (GTK_COMBO_BOX (dialog->priv->text_font_name_combo),
+               e_content_editor_page_get_font_name (cnt_editor));
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (dialog->priv->text_font_name_combo), font_name ? 
font_name : "");
+       g_free (font_name);
+
        GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->show (widget);
 }
 
@@ -345,7 +365,7 @@ html_editor_page_dialog_hide (GtkWidget *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_page_dialog_close (cnt_editor);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_PAGE);
 
        GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->hide (widget);
 }
@@ -368,15 +388,19 @@ e_html_editor_page_dialog_init (EHTMLEditorPageDialog *dialog)
        GtkBox *box;
        GtkGrid *grid, *main_layout;
        GtkWidget *widget;
+       PangoAttrList *bold;
        gint ii;
 
        dialog->priv = E_HTML_EDITOR_PAGE_DIALOG_GET_PRIVATE (dialog);
 
        main_layout = e_html_editor_dialog_get_container (E_HTML_EDITOR_DIALOG (dialog));
 
+       bold = pango_attr_list_new ();
+       pango_attr_list_insert (bold, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
+
        /* == Colors == */
-       widget = gtk_label_new ("");
-       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Colors</b>"));
+       widget = gtk_label_new (_("Colors"));
+       gtk_label_set_attributes (GTK_LABEL (widget), bold);
        gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
        gtk_grid_attach (main_layout, widget, 0, 0, 1, 1);
 
@@ -446,16 +470,42 @@ e_html_editor_page_dialog_init (EHTMLEditorPageDialog *dialog)
                GTK_LABEL (widget), dialog->priv->background_color_picker);
        gtk_grid_attach (grid, widget, 0, 3, 1, 1);
 
-       /* == Background Image == */
-       widget = gtk_label_new ("");
-       gtk_label_set_markup (GTK_LABEL (widget), _("<b>Background Image</b>"));
+       /* == Text == */
+
+       widget = gtk_label_new (_("Text"));
+       gtk_label_set_attributes (GTK_LABEL (widget), bold);
        gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
        gtk_grid_attach (main_layout, widget, 0, 2, 1, 1);
 
        grid = GTK_GRID (gtk_grid_new ());
        gtk_grid_set_row_spacing (grid, 5);
        gtk_grid_set_column_spacing (grid, 5);
-       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 4, 1, 1);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 3, 1, 1);
+       gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
+
+       widget = e_html_editor_util_create_font_name_combo ();
+       g_signal_connect_swapped (
+               widget, "notify::active-id",
+               G_CALLBACK (html_editor_page_dialog_set_text_font_name), dialog);
+       gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+       dialog->priv->text_font_name_combo = widget;
+
+       widget = gtk_label_new_with_mnemonic (_("_Font Name:"));
+       gtk_label_set_justify (GTK_LABEL (widget), GTK_JUSTIFY_RIGHT);
+       gtk_label_set_mnemonic_widget (
+               GTK_LABEL (widget), dialog->priv->text_font_name_combo);
+       gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+
+       /* == Background Image == */
+       widget = gtk_label_new (_("Background Image"));
+       gtk_label_set_attributes (GTK_LABEL (widget), bold);
+       gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+       gtk_grid_attach (main_layout, widget, 0, 4, 1, 1);
+
+       grid = GTK_GRID (gtk_grid_new ());
+       gtk_grid_set_row_spacing (grid, 5);
+       gtk_grid_set_column_spacing (grid, 5);
+       gtk_grid_attach (main_layout, GTK_WIDGET (grid), 0, 5, 1, 1);
        gtk_widget_set_margin_left (GTK_WIDGET (grid), 10);
 
        /* Template */
@@ -503,6 +553,8 @@ e_html_editor_page_dialog_init (EHTMLEditorPageDialog *dialog)
        gtk_box_reorder_child (box, widget, 0);
 
        gtk_widget_show_all (GTK_WIDGET (main_layout));
+
+       pango_attr_list_unref (bold);
 }
 
 GtkWidget *
diff --git a/src/e-util/e-html-editor-paragraph-dialog.c b/src/e-util/e-html-editor-paragraph-dialog.c
index 014de3b98b..15ccebc00b 100644
--- a/src/e-util/e-html-editor-paragraph-dialog.c
+++ b/src/e-util/e-html-editor-paragraph-dialog.c
@@ -41,6 +41,7 @@ struct _EHTMLEditorParagraphDialogPrivate {
        GtkWidget *left_button;
        GtkWidget *center_button;
        GtkWidget *right_button;
+       GtkWidget *justified_button;
 };
 
 static void
@@ -122,6 +123,15 @@ html_editor_paragraph_dialog_constructed (GObject *object)
                e_html_editor_get_action (editor, "justify-right"));
        dialog->priv->right_button = widget;
 
+       /* Justified */
+       widget = gtk_toggle_button_new_with_label (_("_Justified"));
+       gtk_button_set_use_stock (GTK_BUTTON (widget), TRUE);
+       gtk_grid_attach (grid, widget, 2, 0, 1, 1);
+       gtk_activatable_set_related_action (
+               GTK_ACTIVATABLE (widget),
+               e_html_editor_get_action (editor, "justify-fill"));
+       dialog->priv->justified_button = widget;
+
        gtk_widget_show_all (GTK_WIDGET (main_layout));
 }
 
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index 5d66c203b2..695ce0a72b 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -77,14 +77,17 @@ struct _EHTMLEditorPrivate {
        GtkWidget *cell_dialog;
        GtkWidget *spell_check_dialog;
 
-       GtkWidget *color_combo_box;
+       GtkWidget *fg_color_combo_box;
+       GtkWidget *bg_color_combo_box;
        GtkWidget *mode_combo_box;
        GtkWidget *size_combo_box;
        GtkWidget *style_combo_box;
+       GtkWidget *font_name_combo_box;
        GtkWidget *scrolled_window;
 
        GtkWidget *emoji_chooser;
 
+       GHashTable *cid_parts; /* gchar *cid: URI ~> CamelMimePart * */
        GHashTable *content_editors;
        EContentEditor *use_content_editor;
 
@@ -94,8 +97,6 @@ struct _EHTMLEditorPrivate {
        guint recent_spell_languages_merge_id;
 
        gint editor_layout_row;
-
-       gboolean is_testing;
 };
 
 void           editor_actions_init             (EHTMLEditor *editor);
@@ -105,6 +106,10 @@ void               editor_actions_update_spellcheck_languages_menu
                                                 const gchar * const *languages);
 const gchar *  e_html_editor_get_content_editor_name
                                                (EHTMLEditor *editor);
+GtkWidget *    e_html_editor_util_create_font_name_combo
+                                               (void);
+gchar *                e_html_editor_util_dup_font_id  (GtkComboBox *combo_box,
+                                                const gchar *font_name);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-html-editor-replace-dialog.c b/src/e-util/e-html-editor-replace-dialog.c
index d217b758f0..e33e7dfcbc 100644
--- a/src/e-util/e-html-editor-replace-dialog.c
+++ b/src/e-util/e-html-editor-replace-dialog.c
@@ -169,7 +169,7 @@ 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);
+       e_content_editor_on_dialog_open (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_REPLACE);
 
        gtk_widget_grab_focus (dialog->priv->search_entry);
        gtk_widget_hide (dialog->priv->result_label);
@@ -183,7 +183,7 @@ 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);
+       e_content_editor_on_dialog_close (dialog->priv->cnt_editor, E_CONTENT_EDITOR_DIALOG_REPLACE);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_replace_dialog_parent_class)->hide (widget);
diff --git a/src/e-util/e-html-editor-spell-check-dialog.c b/src/e-util/e-html-editor-spell-check-dialog.c
index 3d815ce774..a5b2a09ae8 100644
--- a/src/e-util/e-html-editor-spell-check-dialog.c
+++ b/src/e-util/e-html-editor-spell-check-dialog.c
@@ -288,24 +288,24 @@ html_editor_spell_check_dialog_set_dictionary (EHTMLEditorSpellCheckDialog *dial
 static void
 html_editor_spell_check_dialog_show (GtkWidget *widget)
 {
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
        EHTMLEditorSpellCheckDialog *dialog;
 
        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);
 
        g_free (dialog->priv->word);
        dialog->priv->word = NULL;
 
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
+
        /* Select the first word or quit */
        if (html_editor_spell_check_dialog_next (dialog)) {
-               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);
+       } else {
+               e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
        }
 }
 
@@ -319,7 +319,7 @@ html_editor_spell_check_dialog_hide (GtkWidget *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);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_SPELLCHECK);
 
        /* Chain up to parent implementation */
        GTK_WIDGET_CLASS (e_html_editor_spell_check_dialog_parent_class)->hide (widget);
diff --git a/src/e-util/e-html-editor-table-dialog.c b/src/e-util/e-html-editor-table-dialog.c
index d613104856..e13d914fff 100644
--- a/src/e-util/e-html-editor-table-dialog.c
+++ b/src/e-util/e-html-editor-table-dialog.c
@@ -214,8 +214,8 @@ html_editor_table_dialog_get_alignment (EHTMLEditorTableDialog *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), value);
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (dialog->priv->alignment_combo),
+               value && *value ? value : "left");
        g_free (value);
 }
 
@@ -394,32 +394,21 @@ html_editor_table_dialog_get_values (EHTMLEditorTableDialog *dialog)
 static void
 html_editor_table_dialog_reset_values (EHTMLEditorTableDialog *dialog)
 {
-       gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->rows_edit), 3);
-       gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->columns_edit), 3);
-       gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->rows_edit), 3);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->columns_edit), 3);
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (dialog->priv->alignment_combo), "left");
 
-       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), 100);
-       gtk_combo_box_set_active_id (
-               GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
+       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), 100);
+       gtk_combo_box_set_active_id (GTK_COMBO_BOX (dialog->priv->width_units), "units-percent");
 
-       gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->spacing_edit), 2);
-       gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->padding_edit), 1);
-       gtk_spin_button_set_value (
-               GTK_SPIN_BUTTON (dialog->priv->border_edit), 1);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->spacing_edit), 2);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->padding_edit), 1);
+       gtk_spin_button_set_value (GTK_SPIN_BUTTON (dialog->priv->border_edit), 1);
 
-       e_color_combo_set_current_color (
-               E_COLOR_COMBO (dialog->priv->background_color_picker), &transparent);
+       e_color_combo_set_current_color (E_COLOR_COMBO (dialog->priv->background_color_picker), &transparent);
 
-       gtk_file_chooser_unselect_all (
-               GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
+       gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog->priv->background_image_chooser));
 
        html_editor_table_dialog_set_row_count (dialog);
        html_editor_table_dialog_set_column_count (dialog);
@@ -443,7 +432,9 @@ html_editor_table_dialog_show (GtkWidget *widget)
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       if (e_content_editor_on_table_dialog_open (cnt_editor))
+       e_content_editor_on_dialog_open (cnt_editor, E_CONTENT_EDITOR_DIALOG_TABLE);
+
+       if (!e_content_editor_table_get_row_count (cnt_editor))
                html_editor_table_dialog_reset_values (dialog);
        else
                html_editor_table_dialog_get_values (dialog);
@@ -480,7 +471,7 @@ html_editor_table_dialog_hide (GtkWidget *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_table_dialog_close (cnt_editor);
+       e_content_editor_on_dialog_close (cnt_editor, E_CONTENT_EDITOR_DIALOG_TABLE);
 
        GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
 }
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index 59ab93e472..bb0c998ec1 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -95,6 +95,134 @@ G_DEFINE_TYPE_WITH_CODE (
                E_TYPE_ALERT_SINK,
                e_html_editor_alert_sink_init))
 
+/* See: https://www.w3schools.com/cssref/css_websafe_fonts.asp */
+static struct _SupportedFonts {
+       const gchar *display_name;
+       const gchar *css_value;
+} supported_fonts[] = {
+       { "Arial", "Arial, Helvetica, sans-serif" },
+       { "Arial Black", "\"Arial Black\", Gadget, sans-serif" },
+       { "Comic Sans MS", "\"Comic Sans MS\", cursive, sans-serif" },
+       { "Courier New", "\"Courier New\", Courier, monospace" },
+       { "Georgia", "Georgia, serif" },
+       { "Impact", "Impact, Charcoal, sans-serif" },
+       { "Lucida Console", "\"Lucida Console\", Monaco, monospace" },
+       { "Lucida Sans", "\"Lucida Sans Unicode\", \"Lucida Grande\", sans-serif" },
+       { "Monospace", "monospace" },
+       { "Palatino", "\"Palatino Linotype\", \"Book Antiqua\", Palatino, serif" },
+       { "Tahoma", "Tahoma, Geneva, sans-serif" },
+       { "Times New Roman", "\"Times New Roman\", Times, serif" },
+       { "Trebuchet MS", "\"Trebuchet MS\", Helvetica, sans-serif" },
+       { "Verdana", "Verdana, Geneva, sans-serif" }
+};
+
+GtkWidget *
+e_html_editor_util_create_font_name_combo (void)
+{
+       GtkComboBoxText *combo_box;
+       gint ii;
+
+       combo_box = GTK_COMBO_BOX_TEXT (gtk_combo_box_text_new ());
+
+       gtk_combo_box_text_append (combo_box, "", _("Default"));
+
+       for (ii = 0; ii < G_N_ELEMENTS (supported_fonts); ii++) {
+               gtk_combo_box_text_append (combo_box, supported_fonts[ii].css_value, 
supported_fonts[ii].display_name);
+       }
+
+       return GTK_WIDGET (combo_box);
+}
+
+gchar *
+e_html_editor_util_dup_font_id (GtkComboBox *combo_box,
+                               const gchar *font_name)
+{
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       GSList *free_str = NULL;
+       gchar *id = NULL, **variants;
+       gint id_column, ii;
+
+       g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (combo_box), NULL);
+
+       if (!font_name || !*font_name)
+               return NULL;
+
+       for (ii = 0; ii < G_N_ELEMENTS (supported_fonts); ii++) {
+               if (camel_strcase_equal (supported_fonts[ii].css_value, font_name))
+                       return g_strdup (font_name);
+       }
+
+       id_column = gtk_combo_box_get_id_column (combo_box);
+       model = gtk_combo_box_get_model (combo_box);
+
+       if (gtk_tree_model_get_iter_first (model, &iter)) {
+               do {
+                       gchar *stored_id = NULL;
+
+                       gtk_tree_model_get (model, &iter, id_column, &stored_id, -1);
+
+                       if (stored_id && *stored_id) {
+                               if (camel_strcase_equal (stored_id, font_name)) {
+                                       id = stored_id;
+                                       break;
+                               }
+
+                               free_str = g_slist_prepend (free_str, stored_id);
+                       } else {
+                               g_free (stored_id);
+                       }
+               } while (gtk_tree_model_iter_next (model, &iter));
+       }
+
+       if (!id) {
+               GHashTable *known_fonts;
+               GSList *free_strv = NULL, *link;
+
+               known_fonts = g_hash_table_new (camel_strcase_hash, camel_strcase_equal);
+
+               for (link = free_str; link; link = g_slist_next (link)) {
+                       gchar *stored_id = link->data;
+
+                       variants = g_strsplit (stored_id, ",", -1);
+                       for (ii = 0; variants[ii]; ii++) {
+                               if (variants[ii][0] &&
+                                   !g_hash_table_contains (known_fonts, variants[ii])) {
+                                       g_hash_table_insert (known_fonts, variants[ii], stored_id);
+                               }
+                       }
+
+                       free_strv = g_slist_prepend (free_strv, variants);
+               }
+
+               variants = g_strsplit (font_name, ",", -1);
+               for (ii = 0; variants[ii]; ii++) {
+                       if (variants[ii][0]) {
+                               const gchar *stored_id;
+
+                               stored_id = g_hash_table_lookup (known_fonts, variants[ii]);
+                               if (stored_id) {
+                                       id = g_strdup (stored_id);
+                                       break;
+                               }
+                       }
+               }
+
+               if (!id) {
+                       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combo_box), font_name, variants[0]);
+                       id = g_strdup (font_name);
+               }
+
+               g_hash_table_destroy (known_fonts);
+               g_slist_free_full (free_strv, (GDestroyNotify) g_strfreev);
+               g_strfreev (variants);
+       }
+
+       g_slist_free_full (free_str, g_free);
+
+       return id;
+}
+
 /* Action callback for context menu spelling suggestions.
  * XXX This should really be in e-html-editor-actions.c */
 static void
@@ -112,7 +240,8 @@ action_context_spell_suggest_cb (GtkAction *action,
 }
 
 static void
-html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
+html_editor_inline_spelling_suggestions (EHTMLEditor *editor,
+                                        const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -120,7 +249,6 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
        GtkUIManager *manager;
        gchar **suggestions;
        const gchar *path;
-       gchar *word;
        guint count = 0;
        guint length;
        guint merge_id;
@@ -128,12 +256,11 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
        gint ii;
 
        cnt_editor = e_html_editor_get_content_editor (editor);
-       word = e_content_editor_get_caret_word (cnt_editor);
-       if (word == NULL || *word == '\0')
+       if (!caret_word || !*caret_word)
                return;
 
        spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
-       suggestions = e_spell_checker_get_guesses_for_word (spell_checker, word);
+       suggestions = e_spell_checker_get_guesses_for_word (spell_checker, caret_word);
 
        path = "/context-menu/context-spell-suggest/";
        manager = e_html_editor_get_ui_manager (editor);
@@ -167,12 +294,9 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 
                /* Action name just needs to be unique. */
                action_name = g_strdup_printf ("suggest-%d", count++);
+               action_label = g_markup_printf_escaped ("<b>%s</b>", suggestion);
 
-               action_label = g_markup_printf_escaped (
-                       "<b>%s</b>", suggestion);
-
-               action = gtk_action_new (
-                       action_name, action_label, NULL, NULL);
+               action = gtk_action_new (action_name, action_label, NULL, NULL);
 
                g_object_set_data_full (
                        G_OBJECT (action), "word",
@@ -201,7 +325,6 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
                g_free (action_label);
        }
 
-       g_free (word);
        g_strfreev (suggestions);
        g_clear_object (&spell_checker);
 }
@@ -209,7 +332,8 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 /* Helper for html_editor_update_actions() */
 static void
 html_editor_spell_checkers_foreach (EHTMLEditor *editor,
-                                    const gchar *language_code)
+                                   const gchar *language_code,
+                                   const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -218,22 +342,18 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
        GtkUIManager *manager;
        GList *list, *link;
        gchar *path;
-       gchar *word;
        gint ii = 0;
        guint merge_id;
 
        cnt_editor = e_html_editor_get_content_editor (editor);
-       word = e_content_editor_get_caret_word (cnt_editor);
-       if (word == NULL || *word == '\0')
+       if (!caret_word || !*caret_word)
                return;
 
        spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
 
-       dictionary = e_spell_checker_ref_dictionary (
-               spell_checker, language_code);
+       dictionary = e_spell_checker_ref_dictionary (spell_checker, language_code);
        if (dictionary != NULL) {
-               list = e_spell_dictionary_get_suggestions (
-                       dictionary, word, -1);
+               list = e_spell_dictionary_get_suggestions (dictionary, caret_word, -1);
                g_object_unref (dictionary);
        } else {
                list = NULL;
@@ -256,14 +376,10 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
                GSList *proxies;
 
                /* Action name just needs to be unique. */
-               action_name = g_strdup_printf (
-                       "suggest-%s-%d", language_code, ii);
-
-               action_label = g_markup_printf_escaped (
-                       "%s", suggestion);
+               action_name = g_strdup_printf ("suggest-%s-%d", language_code, ii);
+               action_label = g_markup_printf_escaped ("%s", suggestion);
 
-               action = gtk_action_new (
-                       action_name, action_label, NULL, NULL);
+               action = gtk_action_new (action_name, action_label, NULL, NULL);
 
                g_object_set_data_full (
                        G_OBJECT (action), "word",
@@ -297,7 +413,6 @@ 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);
 }
 
 void
@@ -332,7 +447,8 @@ action_set_visible_and_sensitive (GtkAction *action,
 
 static void
 html_editor_update_actions (EHTMLEditor *editor,
-                            EContentEditorNodeFlags flags)
+                           EContentEditorNodeFlags flags,
+                           const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -422,13 +538,11 @@ html_editor_update_actions (EHTMLEditor *editor,
        /* Decide if we should show spell checking items. */
        visible = FALSE;
        if (n_languages > 0) {
-               gchar *word = e_content_editor_get_caret_word (cnt_editor);
-               if (word && *word) {
-                       visible = !e_spell_checker_check_word (spell_checker, word, -1);
+               if (caret_word && *caret_word) {
+                       visible = !e_spell_checker_check_word (spell_checker, caret_word, -1);
                } else {
                        visible = FALSE;
                }
-               g_free (word);
        }
 
        action_group = editor->priv->spell_check_actions;
@@ -447,7 +561,7 @@ html_editor_update_actions (EHTMLEditor *editor,
 
        /* Handle a single active language as a special case. */
        if (n_languages == 1) {
-               html_editor_inline_spelling_suggestions (editor);
+               html_editor_inline_spelling_suggestions (editor, caret_word);
                g_strfreev (languages);
 
                e_html_editor_update_spell_actions (editor);
@@ -456,7 +570,7 @@ html_editor_update_actions (EHTMLEditor *editor,
 
        /* Add actions and context menu content for active languages. */
        for (ii = 0; ii < n_languages; ii++)
-               html_editor_spell_checkers_foreach (editor, languages[ii]);
+               html_editor_spell_checkers_foreach (editor, languages[ii], caret_word);
 
        g_strfreev (languages);
 
@@ -493,6 +607,7 @@ html_editor_spell_languages_changed (EHTMLEditor *editor)
 typedef struct _ContextMenuData {
        GWeakRef *editor_weakref; /* EHTMLEditor * */
        EContentEditorNodeFlags flags;
+       gchar *caret_word;
        GdkEvent *event;
 } ContextMenuData;
 
@@ -504,6 +619,7 @@ context_menu_data_free (gpointer ptr)
        if (cmd) {
                g_clear_pointer (&cmd->event, gdk_event_free);
                e_weak_ref_free (cmd->editor_weakref);
+               g_free (cmd->caret_word);
                g_slice_free (ContextMenuData, cmd);
        }
 }
@@ -532,7 +648,7 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
 
                menu = e_html_editor_get_managed_widget (editor, "/context-menu");
 
-               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags);
+               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags, cmd->caret_word);
 
                if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
                        gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
@@ -550,25 +666,25 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
        return FALSE;
 }
 
-static gboolean
+static void
 html_editor_context_menu_requested_cb (EContentEditor *cnt_editor,
-                                       EContentEditorNodeFlags flags,
-                                       GdkEvent *event,
-                                       EHTMLEditor *editor)
+                                      EContentEditorNodeFlags flags,
+                                      const gchar *caret_word,
+                                      GdkEvent *event,
+                                      EHTMLEditor *editor)
 {
        ContextMenuData *cmd;
 
-       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
        cmd = g_slice_new0 (ContextMenuData);
        cmd->editor_weakref = e_weak_ref_new (editor);
        cmd->flags = flags;
+       cmd->caret_word = g_strdup (caret_word);
        cmd->event = gdk_event_copy (event);
 
        g_idle_add_full (G_PRIORITY_LOW, html_editor_show_context_menu_idle_cb,
                cmd, context_menu_data_free);
-
-       return TRUE;
 }
 
 static gchar *
@@ -657,6 +773,7 @@ html_editor_constructed (GObject *object)
 {
        EHTMLEditor *editor = E_HTML_EDITOR (object);
        EHTMLEditorPrivate *priv = editor->priv;
+       GdkRGBA transparent = { 0, 0, 0, 0 };
        GtkWidget *widget;
        GtkToolbar *toolbar;
        GtkToolItem *tool_item;
@@ -768,7 +885,18 @@ html_editor_constructed (GObject *object)
        gtk_container_add (GTK_CONTAINER (tool_item), widget);
        gtk_widget_set_tooltip_text (widget, _("Font Color"));
        gtk_toolbar_insert (toolbar, tool_item, 0);
-       priv->color_combo_box = g_object_ref (widget);
+       priv->fg_color_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
+       tool_item = gtk_tool_item_new ();
+       widget = e_color_combo_new ();
+       e_color_combo_set_default_color (E_COLOR_COMBO (widget), &transparent);
+       e_color_combo_set_current_color (E_COLOR_COMBO (widget), &transparent);
+       e_color_combo_set_default_transparent (E_COLOR_COMBO (widget), TRUE);
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Background Color"));
+       gtk_toolbar_insert (toolbar, tool_item, 1);
+       priv->bg_color_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
 
        tool_item = gtk_tool_item_new ();
@@ -781,6 +909,15 @@ html_editor_constructed (GObject *object)
        priv->size_combo_box = g_object_ref (widget);
        gtk_widget_show_all (GTK_WIDGET (tool_item));
 
+       tool_item = gtk_tool_item_new ();
+       widget = e_html_editor_util_create_font_name_combo ();
+       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (widget), FALSE);
+       gtk_container_add (GTK_CONTAINER (tool_item), widget);
+       gtk_widget_set_tooltip_text (widget, _("Font Name"));
+       gtk_toolbar_insert (toolbar, tool_item, 0);
+       priv->font_name_combo_box = g_object_ref (widget);
+       gtk_widget_show_all (GTK_WIDGET (tool_item));
+
        g_signal_connect_after (object, "realize", G_CALLBACK (html_editor_realize), NULL);
 }
 
@@ -809,9 +946,11 @@ html_editor_dispose (GObject *object)
        g_clear_object (&priv->alert_bar);
        g_clear_object (&priv->edit_area);
 
-       g_clear_object (&priv->color_combo_box);
+       g_clear_object (&priv->fg_color_combo_box);
+       g_clear_object (&priv->bg_color_combo_box);
        g_clear_object (&priv->mode_combo_box);
        g_clear_object (&priv->size_combo_box);
+       g_clear_object (&priv->font_name_combo_box);
        g_clear_object (&priv->style_combo_box);
 
        /* Chain up to parent's dispose() method. */
@@ -823,6 +962,7 @@ html_editor_finalize (GObject *object)
 {
        EHTMLEditor *editor = E_HTML_EDITOR (object);
 
+       g_hash_table_destroy (editor->priv->cid_parts);
        g_hash_table_destroy (editor->priv->content_editors);
 
        /* Chain up to parent's method. */
@@ -878,9 +1018,10 @@ e_html_editor_class_init (EHTMLEditorClass *class)
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (EHTMLEditorClass, update_actions),
                NULL, NULL,
-               g_cclosure_marshal_VOID__UINT,
-               G_TYPE_NONE, 1,
-               G_TYPE_UINT);
+               NULL,
+               G_TYPE_NONE, 2,
+               G_TYPE_UINT,
+               G_TYPE_STRING);
 
        signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
                "spell-languages-changed",
@@ -918,6 +1059,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->cid_parts = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, 
g_object_unref);
        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");
@@ -942,12 +1084,20 @@ e_html_editor_content_editor_initialized (EContentEditor *content_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",
+               html_editor->priv->fg_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",
+               html_editor->priv->fg_color_combo_box, "sensitive",
+               G_BINDING_SYNC_CREATE);
+       e_binding_bind_property (
+               html_editor->priv->bg_color_combo_box, "current-color",
+               content_editor, "background-color",
+               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+       e_binding_bind_property (
+               content_editor, "editable",
+               html_editor->priv->bg_color_combo_box, "sensitive",
                G_BINDING_SYNC_CREATE);
        editor_actions_bind (html_editor);
 
@@ -1057,8 +1207,12 @@ e_html_editor_get_content_editor (EHTMLEditor *editor)
                        }
                }
 
-               if (editor->priv->use_content_editor)
+               if (editor->priv->use_content_editor) {
                        e_content_editor_setup_editor (editor->priv->use_content_editor, editor);
+
+                       g_signal_connect_swapped (editor->priv->use_content_editor, "ref-mime-part",
+                               G_CALLBACK (e_html_editor_ref_cid_part), editor);
+               }
        }
 
        return editor->priv->use_content_editor;
@@ -1313,66 +1467,261 @@ e_html_editor_pack_above (EHTMLEditor *editor,
        editor->priv->editor_layout_row++;
 }
 
+typedef struct _SaveContentData {
+       GOutputStream *stream;
+       GCancellable *cancellable;
+} SaveContentData;
+
+static SaveContentData *
+save_content_data_new (GOutputStream *stream,
+                      GCancellable *cancellable)
+{
+       SaveContentData *scd;
+
+       scd = g_slice_new (SaveContentData);
+       scd->stream = stream;
+       scd->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+       return scd;
+}
+
+static void
+save_content_data_free (gpointer ptr)
+{
+       SaveContentData *scd = ptr;
+
+       if (scd) {
+               g_clear_object (&scd->stream);
+               g_clear_object (&scd->cancellable);
+               g_slice_free (SaveContentData, scd);
+       }
+}
+
+static void
+e_html_editor_save_content_ready_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+       ESimpleAsyncResult *simple = user_data;
+       EContentEditorContentHash *content_hash;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+       g_return_if_fail (E_IS_SIMPLE_ASYNC_RESULT (simple));
+
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
+
+       if (content_hash) {
+               const gchar *content;
+
+               content = e_content_editor_util_get_content_data (content_hash, GPOINTER_TO_UINT 
(e_simple_async_result_get_op_pointer (simple)));
+
+               if (!content) {
+                       g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to obtain 
content of editor"));
+               } else {
+                       SaveContentData *scd;
+                       gsize written;
+
+                       scd = e_simple_async_result_get_user_data (simple);
+
+                       g_output_stream_write_all (scd->stream, content, strlen (content), &written, 
scd->cancellable, &error);
+               }
+
+               e_content_editor_util_free_content_hash (content_hash);
+
+               if (error)
+                       e_simple_async_result_take_error (simple, error);
+       } else {
+               e_simple_async_result_take_error (simple, error);
+       }
+
+       e_simple_async_result_complete (simple);
+       g_object_unref (simple);
+}
+
 /**
  * e_html_editor_save:
  * @editor: an #EHTMLEditor
  * @filename: file into which to save the content
  * @as_html: whether the content should be saved as HTML or plain text
- * @error:[out] a #GError
+ * @cancellable: an optional #GCancellable, or %NULL
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the save is finished
+ * @user_data: (closure callback): user data passed to @callback
  *
- * Saves current content of the #EContentEditor into given file. When @as_html
- * is @FALSE, the content is first converted into plain text.
+ * Starts an asynchronous save of the 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.
- */
-gboolean
+ * Finish the call with e_html_editor_save_finish() from the @callback.
+ *
+ * Since: 3.38
+ **/
+void
 e_html_editor_save (EHTMLEditor *editor,
-                    const gchar *filename,
-                    gboolean as_html,
-                    GError **error)
+                   const gchar *filename,
+                   gboolean as_html,
+                   GCancellable *cancellable,
+                   GAsyncReadyCallback callback,
+                   gpointer user_data)
 {
        EContentEditor *cnt_editor;
+       ESimpleAsyncResult *simple;
+       EContentEditorGetContentFlags flag;
+       SaveContentData *scd;
        GFile *file;
        GFileOutputStream *stream;
-       gchar *content;
-       gsize written;
+       GError *local_error = NULL;
+
+       simple = e_simple_async_result_new (G_OBJECT (editor), callback, user_data, e_html_editor_save);
 
        file = g_file_new_for_path (filename);
-       stream = g_file_replace (
-               file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error);
-       if ((error && *error) || !stream)
-               return FALSE;
+       stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &local_error);
+       if (local_error || !stream) {
+               e_simple_async_result_take_error (simple, local_error);
+               e_simple_async_result_complete_idle (simple);
 
-       cnt_editor = e_html_editor_get_content_editor (editor);
+               g_object_unref (simple);
+               g_object_unref (file);
 
-       if (as_html)
-               content = e_content_editor_get_content (
-                       cnt_editor,
-                       E_CONTENT_EDITOR_GET_TEXT_HTML |
-                       E_CONTENT_EDITOR_GET_PROCESSED,
-                       NULL, NULL);
-       else
-               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_free (content);
-               g_set_error (
-                       error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                       "Failed to obtain content of editor");
-               return FALSE;
+               return;
        }
 
-       g_output_stream_write_all (
-               G_OUTPUT_STREAM (stream), content, strlen (content),
-               &written, NULL, error);
+       flag = as_html ? E_CONTENT_EDITOR_GET_TO_SEND_HTML : E_CONTENT_EDITOR_GET_TO_SEND_PLAIN;
+
+       scd = save_content_data_new (G_OUTPUT_STREAM (stream), cancellable);
+
+       e_simple_async_result_set_user_data (simple, scd, save_content_data_free);
+       e_simple_async_result_set_op_pointer (simple, GUINT_TO_POINTER (flag), NULL);
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       e_content_editor_get_content (cnt_editor, flag, NULL, cancellable, 
e_html_editor_save_content_ready_cb, simple);
 
-       g_free (content);
-       g_object_unref (stream);
        g_object_unref (file);
+}
+
+/**
+ * e_html_editor_save_finish:
+ * @editor: an #EHTMLEditor
+ * @result: a #GAsyncResult of the operation
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finished the previous call of e_html_editor_save().
+ *
+ * Returns: whether the save succeeded.
+ *
+ * Since: 3.38
+ **/
+gboolean
+e_html_editor_save_finish (EHTMLEditor *editor,
+                          GAsyncResult *result,
+                          GError **error)
+{
+       g_return_val_if_fail (e_simple_async_result_is_valid (result, G_OBJECT (editor), e_html_editor_save), 
FALSE);
+
+       return !e_simple_async_result_propagate_error (E_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+/**
+ * e_html_editor_add_cid_part:
+ * @editor: an #EHTMLEditor
+ * @mime_part: a #CamelMimePart
+ *
+ * Add the @mime_part with its Content-ID (if not set, one is assigned),
+ * which can be later obtained with e_html_editor_ref_cid_part();
+ *
+ * Since: 3.38
+ **/
+void
+e_html_editor_add_cid_part (EHTMLEditor *editor,
+                           CamelMimePart *mime_part)
+{
+       const gchar *cid;
+       gchar *cid_uri;
+
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
+
+       cid = camel_mime_part_get_content_id (mime_part);
+
+       if (!cid) {
+               camel_mime_part_set_content_id (mime_part, NULL);
+               cid = camel_mime_part_get_content_id (mime_part);
+       }
+
+       cid_uri = g_strconcat ("cid:", cid, NULL);
+
+       g_hash_table_insert (editor->priv->cid_parts, cid_uri, g_object_ref (mime_part));
+}
+
+/**
+ * e_html_editor_remove_cid_part:
+ * @editor: an #EHTMLEditor
+ * @cid_uri: a Content ID URI (starts with "cid:") to remove
+ *
+ * Removes CID part with given @cid_uri, previously added with
+ * e_html_editor_add_cid_part(). The function does nothing if no
+ * such part is stored.
+ *
+ * Since: 3.38
+ **/
+void
+e_html_editor_remove_cid_part (EHTMLEditor *editor,
+                              const gchar *cid_uri)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+       g_return_if_fail (cid_uri != NULL);
+
+       g_hash_table_remove (editor->priv->cid_parts, cid_uri);
+}
+
+/**
+ * e_html_editor_remove_all_cid_parts:
+ * @editor: an #EHTMLEditor
+ *
+ * Removes all CID parts previously added with
+ * e_html_editor_add_cid_part().
+ *
+ * Since: 3.38
+ **/
+void
+e_html_editor_remove_all_cid_parts (EHTMLEditor *editor)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
+
+       g_hash_table_remove_all (editor->priv->cid_parts);
+}
+
+/**
+ * e_html_editor_ref_cid_part:
+ * @editor: an #EHTMLEditor
+ * @cid_uri: a Content ID URI (starts with "cid:") to obtain the part for
+ *
+ * References a #CamelMimePart with given @cid_uri as Content ID, if
+ * previously added with e_html_editor_add_cid_part(). The @cid_uri
+ * should start with "cid:".
+ *
+ * The returned non-%NULL object is references, thus it should be freed
+ * with g_object_unref(), when no longer needed.
+ *
+ * Returns: (transfer full) (nullable): a #CamelMimePart with given Content ID,
+ *    or %NULL, if not found.
+ *
+ * Since: 3.38
+ **/
+CamelMimePart *
+e_html_editor_ref_cid_part (EHTMLEditor *editor,
+                          const gchar *cid_uri)
+{
+       CamelMimePart *mime_part;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), NULL);
+       g_return_val_if_fail (cid_uri != NULL, NULL);
+
+       mime_part = g_hash_table_lookup (editor->priv->cid_parts, cid_uri);
+
+       if (mime_part)
+               g_object_ref (mime_part);
 
-       return TRUE;
+       return mime_part;
 }
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index 6c5a63a62d..8be1af19ea 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -49,6 +49,8 @@
        (G_TYPE_INSTANCE_GET_CLASS \
        ((obj), E_TYPE_HTML_EDITOR, EHTMLEditorClass))
 
+#define E_HTML_EDITOR_MAX_INDENT_LEVEL 10
+
 G_BEGIN_DECLS
 
 typedef struct _EHTMLEditor EHTMLEditor;
@@ -64,7 +66,8 @@ struct _EHTMLEditorClass {
        GtkGridClass parent_class;
 
        void            (*update_actions)       (EHTMLEditor *editor,
-                                                EContentEditorNodeFlags flags);
+                                                EContentEditorNodeFlags flags,
+                                                const gchar *caret_word);
 
        void            (*spell_languages_changed)
                                                (EHTMLEditor *editor);
@@ -104,16 +107,23 @@ void              e_html_editor_pack_above        (EHTMLEditor *editor,
                                                 GtkWidget *child);
 void           e_html_editor_update_spell_actions
                                                (EHTMLEditor *editor);
-
-
-/*****************************************************************************
- * High-Level Editing Interface
- *****************************************************************************/
-
-gboolean       e_html_editor_save              (EHTMLEditor *editor,
+void           e_html_editor_save              (EHTMLEditor *editor,
                                                 const gchar *filename,
                                                 gboolean as_html,
+                                                GCancellable *cancellable,
+                                                GAsyncReadyCallback callback,
+                                                gpointer user_data);
+gboolean       e_html_editor_save_finish       (EHTMLEditor *editor,
+                                                GAsyncResult *result,
                                                 GError **error);
+void           e_html_editor_add_cid_part      (EHTMLEditor *editor,
+                                                CamelMimePart *mime_part);
+void           e_html_editor_remove_cid_part   (EHTMLEditor *editor,
+                                                const gchar *cid_uri);
+void           e_html_editor_remove_all_cid_parts
+                                               (EHTMLEditor *editor);
+CamelMimePart * e_html_editor_ref_cid_part     (EHTMLEditor *editor,
+                                                const gchar *cid_uri);
 
 G_END_DECLS
 
diff --git a/src/e-util/e-mail-signature-editor.c b/src/e-util/e-mail-signature-editor.c
index 2ba2955ded..a2fa5d1149 100644
--- a/src/e-util/e-mail-signature-editor.c
+++ b/src/e-util/e-mail-signature-editor.c
@@ -46,10 +46,13 @@ struct _EMailSignatureEditorPrivate {
 };
 
 struct _AsyncContext {
+       ESourceRegistry *registry;
        ESource *source;
        GCancellable *cancellable;
+       EContentEditorGetContentFlags contents_flag;
        gchar *contents;
        gsize length;
+       GDestroyNotify destroy_contents;
 };
 
 enum {
@@ -86,13 +89,14 @@ G_DEFINE_TYPE (
 static void
 async_context_free (AsyncContext *async_context)
 {
-       if (async_context->source != NULL)
-               g_object_unref (async_context->source);
-
-       if (async_context->cancellable != NULL)
-               g_object_unref (async_context->cancellable);
+       g_clear_object (&async_context->registry);
+       g_clear_object (&async_context->source);
+       g_clear_object (&async_context->cancellable);
 
-       g_free (async_context->contents);
+       if (async_context->destroy_contents)
+               async_context->destroy_contents (async_context->contents);
+       else
+               g_free (async_context->contents);
 
        g_slice_free (AsyncContext, async_context);
 }
@@ -225,16 +229,56 @@ action_close_cb (GtkAction *action,
        gtk_widget_destroy (GTK_WIDGET (window));
 }
 
+static void
+mail_signature_editor_commit_ready_cb (GObject *source_object,
+                                      GAsyncResult *result,
+                                      gpointer user_data)
+{
+       EMailSignatureEditor *editor;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_MAIL_SIGNATURE_EDITOR (source_object));
+
+       editor = E_MAIL_SIGNATURE_EDITOR (source_object);
+
+       e_mail_signature_editor_commit_finish (editor, result, &error);
+
+       /* Ignore cancellations. */
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+               g_error_free (error);
+
+       } else if (error != NULL) {
+               e_alert_submit (
+                       E_ALERT_SINK (e_mail_signature_editor_get_editor (editor)),
+                       "widgets:no-save-signature",
+                       error->message, NULL);
+               g_error_free (error);
+
+       /* Only destroy the editor if the save was successful. */
+       } else {
+               ESourceRegistry *registry;
+               ESource *source;
+
+               registry = e_mail_signature_editor_get_registry (editor);
+               source = e_mail_signature_editor_get_source (editor);
+
+               /* Only make sure that the 'source-changed' is called,
+                * thus the preview of the signature is updated on save.
+                * It is not called when only signature body is changed
+                * (and ESource properties are left unchanged). */
+               g_signal_emit_by_name (registry, "source-changed", source);
+
+               gtk_widget_destroy (GTK_WIDGET (editor));
+       }
+}
+
 static void
 action_save_and_close_cb (GtkAction *action,
                           EMailSignatureEditor *editor)
 {
        GtkEntry *entry;
-       EAsyncClosure *closure;
-       GAsyncResult *result;
        ESource *source;
        gchar *display_name;
-       GError *error = NULL;
 
        entry = GTK_ENTRY (editor->priv->entry);
        source = e_mail_signature_editor_get_source (editor);
@@ -263,43 +307,9 @@ action_save_and_close_cb (GtkAction *action,
 
        editor->priv->cancellable = g_cancellable_new ();
 
-       closure = e_async_closure_new ();
-
        e_mail_signature_editor_commit (
                editor, editor->priv->cancellable,
-               e_async_closure_callback, closure);
-
-       result = e_async_closure_wait (closure);
-
-       e_mail_signature_editor_commit_finish (editor, result, &error);
-
-       e_async_closure_free (closure);
-
-       /* Ignore cancellations. */
-       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-               g_error_free (error);
-
-       } else if (error != NULL) {
-               e_alert_submit (
-                       E_ALERT_SINK (e_mail_signature_editor_get_editor (editor)),
-                       "widgets:no-save-signature",
-                       error->message, NULL);
-               g_error_free (error);
-
-       /* Only destroy the editor if the save was successful. */
-       } else {
-               ESourceRegistry *registry;
-
-               registry = e_mail_signature_editor_get_registry (editor);
-
-               /* Only make sure that the 'source-changed' is called,
-                * thus the preview of the signature is updated on save.
-                * It is not called when only signature body is changed
-                * (and ESource properties are left unchanged). */
-               g_signal_emit_by_name (registry, "source-changed", source);
-
-               gtk_widget_destroy (GTK_WIDGET (editor));
-       }
+               mail_signature_editor_commit_ready_cb, NULL);
 }
 
 static GtkActionEntry entries[] = {
@@ -917,6 +927,55 @@ mail_signature_editor_commit_cb (GObject *object,
                simple);
 }
 
+static void
+mail_signature_editor_content_hash_ready_cb (GObject *source_object,
+                                            GAsyncResult *result,
+                                            gpointer user_data)
+{
+       GSimpleAsyncResult *simple = user_data;
+       EContentEditorContentHash *content_hash;
+       ESourceMailSignature *extension;
+       AsyncContext *async_context;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
+
+       if (!content_hash) {
+               g_simple_async_result_take_error (simple, error);
+               g_simple_async_result_complete (simple);
+               g_object_unref (simple);
+               return;
+       }
+
+       async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+       async_context->contents = e_content_editor_util_steal_content_data (content_hash,
+               async_context->contents_flag, &async_context->destroy_contents);
+
+       e_content_editor_util_free_content_hash (content_hash);
+
+       if (!async_context->contents) {
+               g_warning ("%s: Failed to retrieve content", G_STRFUNC);
+
+               async_context->contents = g_strdup ("");
+               async_context->destroy_contents = NULL;
+       }
+
+       async_context->length = strlen (async_context->contents);
+
+       extension = e_source_get_extension (async_context->source, E_SOURCE_EXTENSION_MAIL_SIGNATURE);
+       e_source_mail_signature_set_mime_type (extension,
+               async_context->contents_flag == E_CONTENT_EDITOR_GET_RAW_BODY_HTML ? "text/html" : 
"text/plain");
+
+       e_source_registry_commit_source (
+               async_context->registry, async_context->source,
+               async_context->cancellable,
+               mail_signature_editor_commit_cb,
+               simple);
+}
+
 void
 e_mail_signature_editor_commit (EMailSignatureEditor *window,
                                 GCancellable *cancellable,
@@ -925,12 +984,8 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
 {
        GSimpleAsyncResult *simple;
        AsyncContext *async_context;
-       ESourceMailSignature *extension;
        ESourceRegistry *registry;
        ESource *source;
-       const gchar *extension_name;
-       const gchar *mime_type;
-       gchar *contents;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
 
@@ -942,26 +997,10 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
        editor = e_mail_signature_editor_get_editor (window);
        cnt_editor = e_html_editor_get_content_editor (editor);
 
-       mime_type = "text/html";
-       contents = e_content_editor_get_content (
-               cnt_editor,
-               E_CONTENT_EDITOR_GET_TEXT_HTML |
-               E_CONTENT_EDITOR_GET_BODY,
-               NULL, NULL);
-
-       if (!contents) {
-               g_warning ("%s: Failed to retrieve content", G_STRFUNC);
-               contents = g_strdup ("");
-       }
-
-       extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE;
-       extension = e_source_get_extension (source, extension_name);
-       e_source_mail_signature_set_mime_type (extension, mime_type);
-
        async_context = g_slice_new0 (AsyncContext);
+       async_context->registry = g_object_ref (registry);
        async_context->source = g_object_ref (source);
-       async_context->contents = contents;  /* takes ownership */
-       async_context->length = strlen (contents);
+       async_context->contents_flag = e_content_editor_get_html_mode (cnt_editor) ? 
E_CONTENT_EDITOR_GET_RAW_BODY_HTML : E_CONTENT_EDITOR_GET_TO_SEND_PLAIN;
 
        if (G_IS_CANCELLABLE (cancellable))
                async_context->cancellable = g_object_ref (cancellable);
@@ -973,11 +1012,8 @@ e_mail_signature_editor_commit (EMailSignatureEditor *window,
        g_simple_async_result_set_op_res_gpointer (
                simple, async_context, (GDestroyNotify) async_context_free);
 
-       e_source_registry_commit_source (
-               registry, source,
-               async_context->cancellable,
-               mail_signature_editor_commit_cb,
-               simple);
+       e_content_editor_get_content (cnt_editor, async_context->contents_flag, NULL,
+               cancellable, mail_signature_editor_content_hash_ready_cb, simple);
 }
 
 gboolean
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index fc96e4a3e4..d6c9e2ecd5 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -3691,276 +3691,6 @@ e_util_check_gtk_bindings_in_key_press_event_cb (GtkWidget *widget,
        return FALSE;
 }
 
-/**
- * e_util_claim_dbus_proxy_call_error:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name of the @dbus_proxy
- * @in_error: (allow-none): a #GError with the failure, or %NULL
- *
- * Claims the @in_error on the console as a failure to call
- * method @method_name of the @dbus_proxy. It's safe to call this
- * with a %NULL @in_error, then the function does nothing.
- * Some errors can be ignored, like the G_IO_ERROR_CANCELLED is.
- *
- * Since: 3.22
- **/
-void
-e_util_claim_dbus_proxy_call_error (GDBusProxy *dbus_proxy,
-                                   const gchar *method_name,
-                                   const GError *in_error)
-{
-       g_return_if_fail (G_IS_DBUS_PROXY (dbus_proxy));
-       g_return_if_fail (method_name != NULL);
-
-       if (in_error &&
-           !g_error_matches (in_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
-           !g_error_matches (in_error, G_IO_ERROR, G_IO_ERROR_CLOSED) &&
-           !g_error_matches (in_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED) &&
-           !g_error_matches (in_error, G_DBUS_ERROR, G_DBUS_ERROR_DISCONNECTED))
-               g_message ("Failed to call a DBus Proxy method %s::%s: %s",
-                       g_dbus_proxy_get_name (dbus_proxy), method_name, in_error->message);
-}
-
-static void
-e_util_finish_dbus_proxy_call_cb (GObject *source_object,
-                                 GAsyncResult *result,
-                                 gpointer user_data)
-{
-       gchar *method_name = user_data;
-       GDBusProxy *dbus_proxy;
-       GVariant *ret;
-       GError *error = NULL;
-
-       g_return_if_fail (G_IS_DBUS_PROXY (source_object));
-
-       dbus_proxy = G_DBUS_PROXY (source_object);
-
-       ret = g_dbus_proxy_call_finish (dbus_proxy, result, &error);
-
-       if (ret)
-               g_variant_unref (ret);
-
-       if (error)
-               g_dbus_error_strip_remote_error (error);
-
-       e_util_claim_dbus_proxy_call_error (dbus_proxy, method_name, error);
-
-       g_clear_error (&error);
-       g_free (method_name);
-}
-
-/**
- * e_util_invoke_g_dbus_proxy_call_with_error_check:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name to invoke
- * @parameters: (allow-none): parameters of the method, or %NULL
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- *
- * Calls g_dbus_proxy_call() on the @dbus_proxy for the @method_name with @parameters
- * and adds its own callback for the result. Then, if an error is returned, passes this
- * error into e_util_claim_dbus_proxy_call_error().
- *
- * See: e_util_invoke_g_dbus_proxy_call_with_error_check_full()
- *
- * Since: 3.22
- **/
-void
-e_util_invoke_g_dbus_proxy_call_with_error_check (GDBusProxy *dbus_proxy,
-                                                 const gchar *method_name,
-                                                 GVariant *parameters,
-                                                 GCancellable *cancellable)
-{
-       g_return_if_fail (G_IS_DBUS_PROXY (dbus_proxy));
-       g_return_if_fail (method_name != NULL);
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check_full (
-               dbus_proxy, method_name, parameters,
-               G_DBUS_CALL_FLAGS_NONE, -1, cancellable);
-}
-
-/**
- * e_util_invoke_g_dbus_proxy_call_with_error_check_full:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name to invoke
- * @parameters: (allow-none): parameters of the method, or %NULL
- * @flags: a bit-or of #GDBusCallFlags, with the same meaning as in the g_dbus_proxy_call()
- * @timeout_msec: timeout in milliseconds, with the same meaning as in the g_dbus_proxy_call().
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- *
- * Calls g_dbus_proxy_call() on the @dbus_proxy for the @method_name with @parameters
- * and adds its own callback for the result. Then, if an error is returned, passes this
- * error into e_util_claim_dbus_proxy_call_error().
- *
- * See: e_util_invoke_g_dbus_proxy_call_with_error_check()
- *
- * Since: 3.22
- **/
-void
-e_util_invoke_g_dbus_proxy_call_with_error_check_full (GDBusProxy *dbus_proxy,
-                                                      const gchar *method_name,
-                                                      GVariant *parameters,
-                                                      GDBusCallFlags flags,
-                                                      gint timeout_msec,
-                                                      GCancellable *cancellable)
-{
-       g_return_if_fail (G_IS_DBUS_PROXY (dbus_proxy));
-       g_return_if_fail (method_name != NULL);
-
-       g_dbus_proxy_call (dbus_proxy, method_name, parameters,
-               flags, timeout_msec, cancellable,
-               e_util_finish_dbus_proxy_call_cb, g_strdup (method_name));
-}
-
-/**
- * e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name to invoke
- * @parameters: (allow-none): parameters of the method, or %NULL
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- *
- * Calls e_util_invoke_g_dbus_proxy_call_sync_wrapper_full() with some default
- * values for flags and timeout_msec, while also providing its own GError and
- * after the call is finished it calls e_util_claim_dbus_proxy_call_error()
- * with the returned error, if any.
- *
- * Returns: (transfer full): The result of the method call, or %NULL on error. Free with g_variant_unref().
- *
- * Since: 3.22
- **/
-GVariant *
-e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (GDBusProxy *dbus_proxy,
-                                                              const gchar *method_name,
-                                                              GVariant *parameters,
-                                                              GCancellable *cancellable)
-{
-       GVariant *result;
-       GError *error = NULL;
-
-       g_return_val_if_fail (G_IS_DBUS_PROXY (dbus_proxy), NULL);
-       g_return_val_if_fail (method_name != NULL, NULL);
-
-       /* Reference the D-Bus proxy, to not have it disappeared under its hands */
-       g_object_ref (dbus_proxy);
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (dbus_proxy, method_name, parameters,
-               G_DBUS_CALL_FLAGS_NONE, -1, cancellable, &error);
-
-       if (error)
-               g_dbus_error_strip_remote_error (error);
-
-       e_util_claim_dbus_proxy_call_error (dbus_proxy, method_name, error);
-       g_clear_error (&error);
-
-       g_object_unref (dbus_proxy);
-
-       return result;
-}
-
-/**
- * e_util_invoke_g_dbus_proxy_call_sync_wrapper:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name to invoke
- * @parameters: (allow-none): parameters of the method, or %NULL
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- * @error: (allow-none): Return location for error, or %NULL
- *
- * Wraps GDBusProxy synchronous call into an asynchronous without blocking
- * the main context. This can be useful when doing calls on a WebExtension,
- * because it can avoid freeze when this is called in the UI process and
- * the WebProcess also does its own IPC call.
- *
- * This function should be called only from the main thread.
- *
- * See e_util_invoke_g_dbus_proxy_call_sync_wrapper_full().
- *
- * Returns: (transfer full): The result of the method call, or %NULL on error. Free with g_variant_unref().
- *
- * Since: 3.34
- **/
-GVariant *
-e_util_invoke_g_dbus_proxy_call_sync_wrapper (GDBusProxy *dbus_proxy,
-                                             const gchar *method_name,
-                                             GVariant *parameters,
-                                             GCancellable *cancellable,
-                                             GError **error)
-{
-       return e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (dbus_proxy, method_name, parameters,
-               G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error);
-}
-
-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);
-}
-
-/**
- * e_util_invoke_g_dbus_proxy_call_sync_wrapper_full:
- * @dbus_proxy: a #GDBusProxy instance
- * @method_name: a method name to invoke
- * @parameters: (allow-none): parameters of the method, or %NULL
- * @flags: a bit-or of #GDBusCallFlags, with the same meaning as in the g_dbus_proxy_call()
- * @timeout_msec: timeout in milliseconds, with the same meaning as in the g_dbus_proxy_call().
- * @cancellable: (allow-none): a #GCancellable, or %NULL
- * @error: (allow-none): Return location for error, or %NULL
- *
- * Wraps GDBusProxy synchronous call into an asynchronous without blocking
- * the main context. This can be useful when doing calls on a WebExtension,
- * because it can avoid freeze when this is called in the UI process and
- * the WebProcess also does its own IPC call.
- *
- * This function should be called only from the main thread.
- *
- * Returns: (transfer full): The result of the method call, or %NULL on error. Free with g_variant_unref().
- *
- * Since: 3.22
- **/
-GVariant *
-e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (GDBusProxy *dbus_proxy,
-                                                  const gchar *method_name,
-                                                  GVariant *parameters,
-                                                  GDBusCallFlags flags,
-                                                  gint timeout_msec,
-                                                  GCancellable *cancellable,
-                                                  GError **error)
-{
-       GAsyncResult *async_result = NULL;
-       GVariant *var_result;
-       GMainContext *main_context;
-
-       g_return_val_if_fail (G_IS_DBUS_PROXY (dbus_proxy), NULL);
-       g_return_val_if_fail (method_name != NULL, NULL);
-
-       g_warn_if_fail (e_util_is_main_thread (g_thread_self ()));
-
-       /* Reference the D-Bus proxy, to not have it disappeared under its hands */
-       g_object_ref (dbus_proxy);
-
-       g_dbus_proxy_call (
-               dbus_proxy, method_name, parameters, flags, timeout_msec, cancellable,
-               sync_wrapper_result_callback, &async_result);
-
-       main_context = g_main_context_get_thread_default ();
-
-       while (!async_result) {
-               g_main_context_iteration (main_context, TRUE);
-       }
-
-       var_result = g_dbus_proxy_call_finish (dbus_proxy, async_result, error);
-
-       g_clear_object (&async_result);
-       g_object_unref (dbus_proxy);
-
-       return var_result;
-}
-
 /**
  * e_util_save_file_chooser_folder:
  * @file_chooser: a #GtkFileChooser
diff --git a/src/e-util/e-misc-utils.h b/src/e-util/e-misc-utils.h
index 27fb594426..5c8848ff0f 100644
--- a/src/e-util/e-misc-utils.h
+++ b/src/e-util/e-misc-utils.h
@@ -303,41 +303,6 @@ gchar *            e_util_save_image_from_clipboard
 gboolean       e_util_check_gtk_bindings_in_key_press_event_cb
                                                (GtkWidget *widget,
                                                 GdkEvent *event);
-void           e_util_claim_dbus_proxy_call_error
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                const GError *in_error);
-void           e_util_invoke_g_dbus_proxy_call_with_error_check
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                GVariant *parameters,
-                                                GCancellable *cancellable);
-void           e_util_invoke_g_dbus_proxy_call_with_error_check_full
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                GVariant *parameters,
-                                                GDBusCallFlags flags,
-                                                gint timeout_msec,
-                                                GCancellable *cancellable);
-GVariant *     e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                GVariant *parameters,
-                                                GCancellable *cancellable);
-GVariant *     e_util_invoke_g_dbus_proxy_call_sync_wrapper
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                GVariant *parameters,
-                                                GCancellable *cancellable,
-                                                GError **error);
-GVariant *     e_util_invoke_g_dbus_proxy_call_sync_wrapper_full
-                                               (GDBusProxy *dbus_proxy,
-                                                const gchar *method_name,
-                                                GVariant *parameters,
-                                                GDBusCallFlags flags,
-                                                gint timeout_msec,
-                                                GCancellable *cancellable,
-                                                GError **error);
 void           e_util_save_file_chooser_folder (GtkFileChooser *file_chooser);
 void           e_util_load_file_chooser_folder (GtkFileChooser *file_chooser);
 gboolean       e_util_get_webkit_developer_mode_enabled
diff --git a/src/e-util/e-spell-checker.c b/src/e-util/e-spell-checker.c
index 3e121de430..5c72859beb 100644
--- a/src/e-util/e-spell-checker.c
+++ b/src/e-util/e-spell-checker.c
@@ -312,7 +312,9 @@ e_spell_checker_list_available_dicts (ESpellChecker *checker)
 
        if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) {
                e_spell_checker_init_global_memory ();
+               G_LOCK (global_memory);
                g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker);
+               G_UNLOCK (global_memory);
        }
 
        list = g_hash_table_get_values (checker->priv->dictionaries_cache);
@@ -333,7 +335,9 @@ e_spell_checker_count_available_dicts (ESpellChecker *checker)
 
        if (g_hash_table_size (checker->priv->dictionaries_cache) == 0) {
                e_spell_checker_init_global_memory ();
+               G_LOCK (global_memory);
                g_hash_table_foreach (global_language_tags, copy_enchant_dicts, checker);
+               G_UNLOCK (global_memory);
        }
 
        return g_hash_table_size (checker->priv->dictionaries_cache);
diff --git a/src/e-util/e-util-enums.h b/src/e-util/e-util-enums.h
index a951f6b223..8d3fe55a47 100644
--- a/src/e-util/e-util-enums.h
+++ b/src/e-util/e-util-enums.h
@@ -164,22 +164,29 @@ typedef enum {
 
 /**
  * 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
+ * @E_CONTENT_EDITOR_GET_INLINE_IMAGES: Return also list of inline images
+ * @E_CONTENT_EDITOR_GET_RAW_BODY_HTML: text/html version of the body only, as used by the editor
+ * @E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN: text/plain version of the body only, as used by the editor
+ * @E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED: text/plain version of the body only, without signature, quoted 
text and such
+ * @E_CONTENT_EDITOR_GET_RAW_DRAFT: a version of the content, to use as draft message
+ * @E_CONTENT_EDITOR_GET_TO_SEND_HTML: text/html version of the content, suitable to be sent
+ * @E_CONTENT_EDITOR_GET_TO_SEND_PLAIN:        text/plain version of the content, suitable to be sent
+ * @E_CONTENT_EDITOR_GET_ALL: a shortcut for all flags
+ *
+ * Influences what content should be returned. Each flag means one
+ * version, or part, of the content.
+ *
+ * Since: 3.38
  **/
 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
+       E_CONTENT_EDITOR_GET_INLINE_IMAGES      = 1 << 0,
+       E_CONTENT_EDITOR_GET_RAW_BODY_HTML      = 1 << 1,
+       E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN     = 1 << 2,
+       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED  = 1 << 3,
+       E_CONTENT_EDITOR_GET_RAW_DRAFT          = 1 << 4,
+       E_CONTENT_EDITOR_GET_TO_SEND_HTML       = 1 << 5,
+       E_CONTENT_EDITOR_GET_TO_SEND_PLAIN      = 1 << 6,
+       E_CONTENT_EDITOR_GET_ALL                = ~0
 } EContentEditorGetContentFlags;
 
 /**
@@ -206,30 +213,6 @@ typedef enum {
        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:
@@ -292,16 +275,20 @@ typedef enum {
 
 /**
  * EContentEditorAlignment:
+ * @E_CONTENT_EDITOR_ALIGNMENT_NONE:
  * @E_CONTENT_EDITOR_ALIGNMENT_LEFT:
  * @E_CONTENT_EDITOR_ALIGNMENT_CENTER:
  * @E_CONTENT_EDITOR_ALIGNMENT_RIGHT:
+ * @E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY:
  *
  * Since: 3.22
  **/
 typedef enum {
+       E_CONTENT_EDITOR_ALIGNMENT_NONE = -1,
        E_CONTENT_EDITOR_ALIGNMENT_LEFT = 0,
        E_CONTENT_EDITOR_ALIGNMENT_CENTER,
-       E_CONTENT_EDITOR_ALIGNMENT_RIGHT
+       E_CONTENT_EDITOR_ALIGNMENT_RIGHT,
+       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY
 } EContentEditorAlignment;
 
 /**
@@ -535,18 +522,20 @@ typedef enum {
 } EContentEditorFindFlags;
 
 /**
- * EClipboardFlags:
- * @E_CLIPBOARD_CAN_COPY: It's possible to copy the currently selected content.
+ * EUndoRedoState:
+ * @E_UNDO_REDO_STATE_NONE: Cannot undo, neither redo.
+ * @E_UNDO_REDO_STATE_CAN_UNDO: Undo is available.
+ * @E_UNDO_REDO_STATE_CAN_REDO: Redo is available.
  *
- * Specifies clipboard's current state.
+ * Flags in what state Undo/Redo stack is.
  *
- * Since: 3.24
+ * Since: 3.38
  **/
 typedef enum {
-       E_CLIPBOARD_CAN_COPY    = 1 << 0
-       /* E_CLIPBOARD_CAN_CUT  = 1 << 1,
-       E_CLIPBOARD_CAN_PASTE   = 1 << 2 */
-} EClipboardFlags;
+       E_UNDO_REDO_STATE_NONE          = 0,
+       E_UNDO_REDO_STATE_CAN_UNDO      = 1 << 0,
+       E_UNDO_REDO_STATE_CAN_REDO      = 1 << 1
+} EUndoRedoState;
 
 /**
  * EDnDTargetType:
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index 4df98d7f6e..fa1243a79e 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -263,7 +263,6 @@
 #include <e-util/e-util-enums.h>
 #include <e-util/e-util-enumtypes.h>
 #include <e-util/e-webdav-browser.h>
-#include <e-util/e-web-extension-container.h>
 #ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 #include <e-util/e-web-view.h>
 #include <e-util/e-web-view-jsc-utils.h>
diff --git a/src/e-util/e-web-view.c b/src/e-util/e-web-view.c
index cef66792de..23b125aaee 100644
--- a/src/e-util/e-web-view.c
+++ b/src/e-util/e-web-view.c
@@ -38,7 +38,6 @@
 #include "e-popup-action.h"
 #include "e-selectable.h"
 #include "e-stock-request.h"
-#include "e-web-extension-container.h"
 #include "e-web-view-jsc-utils.h"
 
 #include "e-web-view.h"
diff --git a/src/e-util/test-html-editor-units-bugs.c b/src/e-util/test-html-editor-units-bugs.c
index ae6901e20b..7bebfb9276 100644
--- a/src/e-util/test-html-editor-units-bugs.c
+++ b/src/e-util/test-html-editor-units-bugs.c
@@ -22,51 +22,6 @@
 
 #include "test-html-editor-units-bugs.h"
 
-static void
-test_bug_726548 (TestFixture *fixture)
-{
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
-#if 0
-       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 "<div style=\"width: 71ch;\">aaa</div>"
-               "<ol style=\"width: 65ch;\">"
-               "<li>a</li><li>b</li><li>c</li></ol>"
-               "<div style=\"width: 71ch;\"><br></div>" 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);
-       }
-#endif
-}
-
 static void
 test_bug_750657 (TestFixture *fixture)
 {
@@ -77,7 +32,7 @@ test_bug_750657 (TestFixture *fixture)
        }
 
        test_utils_insert_content (fixture,
-               "<html><head></head><body>\n"
+               "<html><head></head><body>"
                "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">\n"
                "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>\n"
                "<div><br></div>\n"
@@ -88,12 +43,12 @@ test_bug_750657 (TestFixture *fixture)
                "</blockquote>\n"
                "<div>This is the fourth paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>\n"
                "</blockquote>\n"
-               "<div><br></div>\n"
+               "<div><br></div>"
                "</body></html>",
-               E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML | E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:uuuSuusD\n",
+               "seq:CecuuuSuusD\n",
                HTML_PREFIX
                "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">\n"
                "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>\n"
@@ -138,7 +93,7 @@ test_bug_760989 (TestFixture *fixture)
                "<div>Single line quoted.</div>\n"
                "</blockquote>" HTML_SUFFIX,
                "One line before quotation\n"
-               "> Single line quoted.")) {
+               "> Single line quoted.\n")) {
                g_test_fail ();
                return;
        }
@@ -150,7 +105,7 @@ test_bug_760989 (TestFixture *fixture)
                "<div>Single line quoted</div>\n"
                "</blockquote>" HTML_SUFFIX,
                "One line before quotation\n"
-               "> Single line quoted")) {
+               "> Single line quoted\n")) {
                g_test_fail ();
                return;
        }
@@ -166,11 +121,11 @@ test_bug_767903 (TestFixture *fixture)
                "type:First item\\n\n"
                "type:Second item\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">This is the first line:</div>"
-               "<ul style=\"width: 68ch;\">"
-               "<li>First item</li><li>Second item<br></li></ul>" HTML_SUFFIX,
+               "<ul>"
+               "<li>First item</li><li>Second item</li></ul>" HTML_SUFFIX,
                "This is the first line:\n"
                " * First item\n"
-               " * Second item")) {
+               " * Second item\n")) {
                g_test_fail ();
                return;
        }
@@ -179,11 +134,11 @@ test_bug_767903 (TestFixture *fixture)
                "seq:uhb\n"
                "undo:undo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">This is the first line:</div>"
-               "<ul style=\"width: 68ch;\">"
-               "<li>First item</li><li>Second item<br></li></ul>" HTML_SUFFIX,
+               "<ul>"
+               "<li>First item</li><li>Second item</li></ul>" HTML_SUFFIX,
                "This is the first line:\n"
                " * First item\n"
-               " * Second item")) {
+               " * Second item\n")) {
                g_test_fail ();
                return;
        }
@@ -210,12 +165,13 @@ test_bug_769708 (TestFixture *fixture)
 
        if (!test_utils_run_simple_test (fixture,
                "",
-               HTML_PREFIX "<div>aaa</div><div><span><pre>-- <br></pre>"
-               "<div>user &lt;user@no.where&gt;</div>"
+               HTML_PREFIX "<div style=\"width: 71ch;\">aaa</div>"
+               "<div class=\"-x-evo-signature-wrapper\" style=\"width: 71ch;\"><span 
class=\"-x-evo-signature\" id=\"autogenerated\"><pre>-- <br></pre>"
+               "<div>user &lt;<a href=\"mailto:user@no.where\";>user@no.where</a>&gt;</div>"
                "</span></div>" HTML_SUFFIX,
                "aaa\n"
                "-- \n"
-               "user <user@no.where>"))
+               "user <user@no.where>\n"))
                g_test_fail ();
 }
 
@@ -232,7 +188,7 @@ test_bug_769913 (TestFixture *fixture)
                "type:ab\n"
                "seq:ltlD\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -240,7 +196,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:ttllDD\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -248,7 +204,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:ttlDlD\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -256,7 +212,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:tttlllDDD\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -264,7 +220,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:tttlDlDlD\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -272,7 +228,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:tb\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -280,7 +236,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:ttbb\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -288,7 +244,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:ttlbrb\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -296,7 +252,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:tttbbb\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -304,7 +260,7 @@ test_bug_769913 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "seq:tttllbrbrb\n",
                HTML_PREFIX "<div>ab</div>" HTML_SUFFIX,
-               "ab")) {
+               "ab\n")) {
                g_test_fail ();
                return;
        }
@@ -326,7 +282,7 @@ test_bug_769955 (TestFixture *fixture)
                "<a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>"
                HTML_SUFFIX,
-               
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -342,7 +298,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[1] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[1] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[1] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -359,7 +315,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[2] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[2] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[2] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -376,7 +332,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[3] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[3] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[3] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -393,7 +349,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[4] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[4] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[4] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -412,7 +368,7 @@ test_bug_769955 (TestFixture *fixture)
                "<a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>"
                HTML_SUFFIX,
-               
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -428,7 +384,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[5] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[5] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[5] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -445,7 +401,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[6] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[6] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[6] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -462,7 +418,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[7] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[7] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[7] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -479,7 +435,7 @@ test_bug_769955 (TestFixture *fixture)
                HTML_PREFIX "<pre>"
                "[8] <a 
href=\"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\";>"
                
"http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines</a></pre>" 
HTML_SUFFIX,
-               "[8] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines";)) {
+               "[8] 
http://www.example.com/this-is-a-very-long-link-which-should-not-be-wrapped-into-multiple-lines\n";)) {
                g_test_fail ();
                return;
        }
@@ -503,15 +459,15 @@ test_bug_770073 (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:Chcddbb\n",
+               "seq:Chcddb\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; the 1st line text</div>"
-               "<div style=\"width: 71ch;\">&gt; the 3rd line text</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "the 1st line text</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "the 3rd line text</div>"
                "</blockquote>" HTML_SUFFIX,
                "On Today, User wrote:\n"
                "> the 1st line text\n"
-               "> the 3rd line text")) {
+               "> the 3rd line text\n")) {
                g_test_fail ();
                return;
        }
@@ -531,7 +487,7 @@ test_bug_770073 (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:Chcddbb\n",
+               "seq:Chcddb\n",
                HTML_PREFIX "<div>On Today, User wrote:</div>"
                "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
                "<div><span>the first line text</span></div>"
@@ -539,7 +495,7 @@ test_bug_770073 (TestFixture *fixture)
                "</blockquote>" HTML_SUFFIX,
                "On Today, User wrote:\n"
                "> the first line text\n"
-               "> the third line text"))
+               "> the third line text\n"))
                g_test_fail ();
 
 }
@@ -562,17 +518,17 @@ test_bug_770074 (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:Chcddbb\n"
+               "seq:Chcddb\n"
                "seq:n\n"
                "undo:undo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; the 1st line text</div>"
-               "<div style=\"width: 71ch;\">&gt; the 3rd line text</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "the 1st line text</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "the 3rd line text</div>"
                "</blockquote>" HTML_SUFFIX,
                "On Today, User wrote:\n"
                "> the 1st line text\n"
-               "> the 3rd line text"))
+               "> the 3rd line text\n"))
                g_test_fail ();
 }
 
@@ -586,13 +542,15 @@ test_bug_771044 (TestFixture *fixture)
                "<div>789 abc</div>"
                "<div><br></div>"
                HTML_SUFFIX,
-               "789 abc\n"))
+               "789 abc\n\n"))
                g_test_fail ();
 }
 
 static void
 test_bug_771131 (TestFixture *fixture)
 {
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-wrap-quoted-text-in-replies", TRUE);
+
        if (!test_utils_process_commands (fixture,
                "mode:plain\n")) {
                g_test_fail ();
@@ -601,7 +559,7 @@ test_bug_771131 (TestFixture *fixture)
 
        test_utils_insert_content (fixture,
                "<body><pre>On &lt;date1&gt;, &lt;name1&gt; wrote:\n"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">\n"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
                "Hello\n"
                "\n"
                "Goodbye</blockquote>"
@@ -612,16 +570,49 @@ test_bug_771131 (TestFixture *fixture)
 
        if (!test_utils_run_simple_test (fixture,
                "",
-               HTML_PREFIX "<div style=\"width: 71ch;\">On Sat, 2016-09-10 at 20:00 +0000, example example 
com wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; On &lt;date1&gt;, &lt;name1&gt; wrote:</div>"
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Sat, 2016-09-10 at 20:00 +0000, <a 
href=\"mailto:example example com\">example example com</a> wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "On &lt;date1&gt;, &lt;name1&gt; wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "Hello</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "Goodbye</div>"
+               "</blockquote>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "the 3rd line text</div>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "On Sat, 2016-09-10 at 20:00 +0000, example example com wrote:\n"
+               "> On <date1>, <name1> wrote:\n"
+               "> > Hello\n"
+               "> > \n"
+               "> > Goodbye\n"
+               "> the 3rd line text\n"))
+               g_test_fail ();
+
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-wrap-quoted-text-in-replies", FALSE);
+
+       test_utils_insert_content (fixture,
+               "<body><pre>On &lt;date1&gt;, &lt;name1&gt; wrote:\n"
                "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; &gt; Hello</div>"
-               "<div style=\"width: 71ch;\">&gt; &gt; <br></div>"
-               "<div style=\"width: 71ch;\">&gt; &gt; Goodbye</div>"
+               "Hello\n"
+               "\n"
+               "Goodbye</blockquote>"
+               "<div><span>the 3rd line text</span></div>"
+               "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Sat, 2016-09-10 at 20:00 +0000, 
example example com wrote:\"></span>"
+               "<span class=\"-x-evo-cite-body\"></span></body>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Sat, 2016-09-10 at 20:00 +0000, <a 
href=\"mailto:example example com\">example example com</a> wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "On &lt;date1&gt;, &lt;name1&gt; wrote:</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "Hello</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "<br></pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "Goodbye</pre>"
                "</blockquote>"
-               "<div style=\"width: 71ch;\">&gt; <br></div>"
-               "<div style=\"width: 71ch;\">&gt; the 3rd line text</div>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "the 3rd line text</pre>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Sat, 2016-09-10 at 20:00 +0000, example example com wrote:\n"
@@ -629,8 +620,7 @@ test_bug_771131 (TestFixture *fixture)
                "> > Hello\n"
                "> > \n"
                "> > Goodbye\n"
-               "> \n"
-               "> the 3rd line text"))
+               "> the 3rd line text\n"))
                g_test_fail ();
 }
 
@@ -646,7 +636,7 @@ test_bug_771493 (TestFixture *fixture)
        test_utils_insert_content (fixture,
                "<body><pre><br>"
                "----- Original Message -----\n"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">\n"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
                "This week summary:"
                "</blockquote>"
                "</pre><span class=\"-x-evo-to-body\" data-credits=\"On Thu, 2016-09-15 at 08:08 -0400, user 
wrote:\"></span>"
@@ -656,18 +646,18 @@ test_bug_771493 (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Thu, 2016-09-15 at 08:08 -0400, user wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; <br></div>"
-               "<div style=\"width: 71ch;\">&gt; ----- Original Message -----</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; &gt; This week summary:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "----- Original Message -----</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "This week summary:</div>"
                "</blockquote>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Thu, 2016-09-15 at 08:08 -0400, user wrote:\n"
                "> \n"
                "> ----- Original Message -----\n"
-               "> > This week summary:"))
+               "> > This week summary:\n"))
                g_test_fail ();
 }
 
@@ -689,16 +679,16 @@ test_bug_772171 (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:ddeb",
+               "seq:deb",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Thu, 2016-09-15 at 08:08 -0400, user wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; <br></div>"
-               "<div style=\"width: 71ch;\">&gt; b</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "b</div>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Thu, 2016-09-15 at 08:08 -0400, user wrote:\n"
                "> \n"
-               "> b"))
+               "> b\n"))
                g_test_fail ();
 }
 
@@ -729,7 +719,8 @@ test_bug_772513 (TestFixture *fixture)
 
        if (!test_utils_run_simple_test (fixture,
                "",
-               HTML_PREFIX "<div style=\"width: 71ch;\"><br></div>" HTML_SUFFIX,
+               HTML_PREFIX "<div style=\"width: 71ch;\"><br></div>"
+               "<div class=\"-x-evo-signature-wrapper\" style=\"width: 71ch;\"><span 
class=\"-x-evo-signature\" id=\"none\"></span></div>" HTML_SUFFIX,
                "\n"))
                g_test_fail ();
 }
@@ -745,7 +736,7 @@ test_bug_772918 (TestFixture *fixture)
                "undo:undo:6\n"
                "undo:redo:6\n",
                HTML_PREFIX "<div>a b 1 2 3 c d</div>" HTML_SUFFIX,
-               "a b 1 2 3 c d"))
+               "a b 1 2 3 c d\n"))
                g_test_fail ();
 }
 
@@ -761,31 +752,35 @@ test_bug_773164 (TestFixture *fixture)
                "undo:undo\n"
                "undo:test\n"
                "undo:redo\n"
-               "seq:huuuue\n" /* Go to the end of the first line */
-               "seq:Sdds\n"
+               "seq:huuue\n" /* Go to the end of the second line */
+               "seq:Sddes\n"
                "action:cut\n"
                "seq:dde\n" /* Go to the end of the last line */
                "action:paste\n"
-               "undo:undo:5\n"
+               "undo:undo:3\n"
                "undo:test\n"
-               "undo:redo:5\n",
+               "undo:redo:3\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">This is paragraph 1</div>"
                "<div style=\"width: 71ch;\"><br></div>"
                "<div style=\"width: 71ch;\">This is a longer paragraph 3</div>"
                "<div style=\"width: 71ch;\"><br></div>"
                "<div style=\"width: 71ch;\">This is paragraph 2</div>"
+               "<div style=\"width: 71ch;\"><br></div>"
                HTML_SUFFIX,
                "This is paragraph 1\n"
                "\n"
                "This is a longer paragraph 3\n"
                "\n"
-               "This is paragraph 2"))
+               "This is paragraph 2\n"
+               "\n"))
                g_test_fail ();
 }
 
 static void
 test_bug_775042 (TestFixture *fixture)
 {
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-wrap-quoted-text-in-replies", FALSE);
+
        test_utils_insert_content (fixture,
                "<body><pre>a\n"
                "b\n"
@@ -798,17 +793,69 @@ test_bug_775042 (TestFixture *fixture)
                "seq:rl\n"
                "mode:plain\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Fri, 2016-11-25 at 08:18 +0000, user wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; a</pre>"
-               "<pre>&gt; b</pre>"
-               "<pre>&gt; c</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "a</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "b</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "c</pre>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "On Fri, 2016-11-25 at 08:18 +0000, user wrote:\n"
+               "> a\n"
+               "> b\n"
+               "> c\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<body><pre>a\n"
+               "b\n"
+               "c"
+               "<span class=\"-x-evo-to-body\" data-credits=\"On Fri, 2016-11-25 at 08:18 +0000, user 
wrote:\"></span>"
+               "<span class=\"-x-evo-cite-body\"></span></body>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:rl\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Fri, 2016-11-25 at 08:18 +0000, user wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "a</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "b</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "c</pre>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "On Fri, 2016-11-25 at 08:18 +0000, user wrote:\n"
+               "> a\n"
+               "> b\n"
+               "> c\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<body><div>a</div>"
+               "<p>b</p>"
+               "<div>c</div>"
+               "<span class=\"-x-evo-to-body\" data-credits=\"On Fri, 2016-11-25 at 08:18 +0000, user 
wrote:\"></span>"
+               "<span class=\"-x-evo-cite-body\"></span></body>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:rl\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Fri, 2016-11-25 at 08:18 +0000, user wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "a</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "b</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "c</div>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Fri, 2016-11-25 at 08:18 +0000, user wrote:\n"
                "> a\n"
                "> b\n"
-               "> c"))
+               "> c\n")) {
                g_test_fail ();
+               return;
+       }
 }
 
 static void
@@ -825,7 +872,7 @@ test_bug_775691 (TestFixture *fixture)
                "<div style=\"width: 71ch;\">def</div>"
                HTML_SUFFIX,
                "abc def ghi\n"
-               "def"))
+               "def\n"))
                g_test_fail ();
 }
 
@@ -850,20 +897,53 @@ test_bug_779707 (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:uuuSesDbnnu\n"
+               "seq:ChcddhSesDbnn\n"
                "type:a very long text, which splits into multiple lines when this paragraph is not marked as 
preformatted, but as normal, as it should be\n"
-               "",
+               "seq:n\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">Credits:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; line 1</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "line 1</pre>"
                "</blockquote>"
                "<div style=\"width: 71ch;\"><br></div>"
                "<div style=\"width: 71ch;\">a very long text, which splits into multiple lines when this 
paragraph is not marked as preformatted, but as normal, as it should be</div>"
                "<div style=\"width: 71ch;\"><br></div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; line 3</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "line 3</pre>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "Credits:\n"
+               "> line 1\n"
+               "\n"
+               "a very long text, which splits into multiple lines when this paragraph\n"
+               "is not marked as preformatted, but as normal, as it should be\n"
+               "\n"
+               "> line 3\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<div>line 1</div>"
+               "<div>line 2</div>"
+               "<div>line 3</div>"
+               "<span class=\"-x-evo-to-body\" data-credits=\"Credits:\"></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,
+               "seq:ChcddhSesDbnn\n"
+               "type:a very long text, which splits into multiple lines when this paragraph is not marked as 
preformatted, but as normal, as it should be\n"
+               "seq:n\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">Credits:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 1</div>"
                "</blockquote>"
                "<div style=\"width: 71ch;\"><br></div>"
+               "<div style=\"width: 71ch;\">a very long text, which splits into multiple lines when this 
paragraph is not marked as preformatted, but as normal, as it should be</div>"
+               "<div style=\"width: 71ch;\"><br></div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 3</div>"
+               "</blockquote>"
                HTML_SUFFIX,
                "Credits:\n"
                "> line 1\n"
@@ -898,8 +978,7 @@ test_bug_780275_html (TestFixture *fixture)
                "seq:hSuusD\n"
                "undo:undo\n"
                "undo:test:1\n"
-               "undo:redo\n"
-               "",
+               "undo:redo\n",
                HTML_PREFIX "<div>line 0</div>"
                "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
                "<div>Xline 1</div>"
@@ -910,7 +989,7 @@ test_bug_780275_html (TestFixture *fixture)
                "line 0\n"
                "> Xline 1\n"
                "> line 2\n"
-               "line 4"))
+               "line 4\n"))
                g_test_fail ();
 }
 
@@ -939,16 +1018,16 @@ test_bug_780275_plain (TestFixture *fixture)
                "undo:test:1\n"
                "undo:redo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">line 0</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; Xline 1</div>"
-               "<div style=\"width: 71ch;\">&gt; line 2</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Xline 1</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
                "</blockquote>"
                "<div style=\"width: 71ch;\">line 4</div>"
                HTML_SUFFIX,
                "line 0\n"
                "> Xline 1\n"
                "> line 2\n"
-               "line 4"))
+               "line 4\n"))
                g_test_fail ();
 }
 
@@ -971,12 +1050,12 @@ test_bug_781722 (TestFixture *fixture)
                "seq:dd\n"
                "action:style-preformat\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">Credits:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; Signed-off-by: User &lt;<a 
href=\"mailto:user@no.where\";>user@no.where</a>&gt;</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "Signed-off-by: User &lt;<a 
href=\"mailto:user@no.where\";>user@no.where</a>&gt;</pre>"
                "</blockquote>"
                HTML_SUFFIX,
                "Credits:\n"
-               "> Signed-off-by: User <user@no.where>"))
+               "> Signed-off-by: User <user@no.where>\n"))
                g_test_fail ();
 }
 
@@ -993,7 +1072,7 @@ test_bug_781116 (TestFixture *fixture)
 
        test_utils_insert_content (fixture,
                "<pre>a very long text, which splits into multiple lines when this paragraph is not marked as 
preformatted, but as normal, as it should be</pre>\n"
-               "</pre><span class=\"-x-evo-to-body\" data-credits=\"Credits:\"></span>"
+               "<span class=\"-x-evo-to-body\" data-credits=\"Credits:\"></span>"
                "<span class=\"-x-evo-cite-body\"></span>",
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
@@ -1001,16 +1080,67 @@ test_bug_781116 (TestFixture *fixture)
                "seq:dd\n"
                "action:wrap-lines\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">Credits:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; a very long text, which splits into multiple lines when this<br>"
-               "&gt; paragraph is not marked as preformatted, but as normal, as it should<br>"
-               "&gt; be</pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "a very long text, which splits into multiple lines when 
this<br>"
+               QUOTE_SPAN (QUOTE_CHR) "paragraph is not marked as preformatted, but as normal, as it 
should<br>"
+               QUOTE_SPAN (QUOTE_CHR) "be</pre>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "Credits:\n"
+               "> a very long text, which splits into multiple lines when this\n"
+               "> paragraph is not marked as preformatted, but as normal, as it should\n"
+               "> be\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<blockquote type=\"cite\"><div>a very long text, which splits into multiple lines when this 
paragraph is not marked as preformatted, but as normal, as it should be</div></blockquote>"
+               "<span class=\"-x-evo-to-body\" data-credits=\"Credits:\"></span>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:dd\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">Credits:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "a very long text, which splits into multiple lines when 
this<br>"
+               QUOTE_SPAN (QUOTE_CHR) "paragraph is not marked as preformatted, but as normal, as it 
should<br>"
+               QUOTE_SPAN (QUOTE_CHR) "be</div>"
                "</blockquote>"
                HTML_SUFFIX,
                "Credits:\n"
                "> a very long text, which splits into multiple lines when this\n"
                "> paragraph is not marked as preformatted, but as normal, as it should\n"
-               "> be</pre>"))
+               "> be\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<blockquote type=\"cite\"><div>a very long text, which splits into multiple lines when this 
paragraph is not marked as preformatted, but as normal, as it should be</div></blockquote>"
+               "<span class=\"-x-evo-to-body\" data-credits=\"Credits:\"></span>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:dd\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<div>Credits:</div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>a very long text, which splits into multiple lines when this paragraph<br>"
+               "is not marked as preformatted, but as normal, as it should be</div>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "Credits:\n"
+               "> a very long text, which splits into multiple lines when this\n"
+               "> paragraph\n"
+               "> is not marked as preformatted, but as normal, as it should be\n"))
                g_test_fail ();
 }
 
@@ -1023,7 +1153,9 @@ test_bug_780088 (TestFixture *fixture)
                return;
        }
 
-       test_utils_set_clipboard_text ("Seeing @blah instead of @foo XX'ed on" UNICODE_NBSP 
"https://example.sub"; UNICODE_NBSP "domain.org/page I'd recommend to XX YY 
<https://example.subdomain.org/p/user/> , click fjwvne on the left, click skjd sjewncj on the right, and set 
wqje wjfdn Xs to something like wqjfnm www.example.com/~user wjfdncj or such.", FALSE);
+       test_utils_set_clipboard_text ("Seeing @blah instead of @foo XX'ed on" UNICODE_NBSP 
"https://example.sub"; UNICODE_NBSP "domain.org/page I'd"
+               " recommend to XX YY <https://example.subdomain.org/p/user/> , click fjwvne on the left, 
click skjd sjewncj on the right, and set"
+               " wqje wjfdn Xs to something like wqjfnm www.example.com/~user wjfdncj or such.", FALSE);
 
        if (!test_utils_run_simple_test (fixture,
                "action:paste\n"
@@ -1033,14 +1165,14 @@ test_bug_780088 (TestFixture *fixture)
                "&nbsp;domain.org/page I'd recommend to XX YY "
                "&lt;<a 
href=\"https://example.subdomain.org/p/user/\";>https://example.subdomain.org/p/user/</a>&gt; , "
                "click fjwvne on the left, click skjd sjewncj on the right, and set wqje wjfdn Xs to 
something like "
-               "wqjfnm <a href=\"www.example.com/~user\">www.example.com/~user</a> wjfdncj or such.</div>"
-               "</div><div style=\"width: 71ch;\"><br></div>"
+               "wqjfnm <a href=\"https://www.example.com/~user\";>www.example.com/~user</a> wjfdncj or 
such.</div>"
+               "<div style=\"width: 71ch;\"><br></div>"
                HTML_SUFFIX,
-               "Seeing @blah instead of @foo XX'ed on" UNICODE_NBSP "https://example.sub"; UNICODE_NBSP 
"domain.org/pa\n"
-               "ge I'd recommend to XX YY <https://example.subdomain.org/p/user/> ,\n"
-               "click fjwvne on the left, click skjd sjewncj on the right, and set wqje\n"
-               "wjfdn Xs to something like wqjfnm www.example.com/~user wjfdncj or\n"
-               "such.\n"))
+               "Seeing @blah instead of @foo XX'ed\n"
+               "on" UNICODE_NBSP "https://example.sub"; UNICODE_NBSP "domain.org/page I'd recommend to XX 
YY\n"
+               "<https://example.subdomain.org/p/user/> , click fjwvne on the left,\n"
+               "click skjd sjewncj on the right, and set wqje wjfdn Xs to something\n"
+               "like wqjfnm www.example.com/~user wjfdncj or such.\n\n"))
                g_test_fail ();
 }
 
@@ -1085,37 +1217,41 @@ test_bug_788829 (TestFixture *fixture)
 
        if (!test_utils_run_simple_test (fixture,
                "",
-               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div><blockquote type=\"cite\" 
" BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; Xxxxx xx xxxxxxxxx xx xxxxxxx xx xxxxx xxxx "
-               "xxxx xx xxx xxx xxxx xxx<br>&gt; xxxçx xôxé \"xxxxx xxxx xxxxxxx xxx\" xx xxxx "
-               "xxxxé xxx xxx xxxéx xxx<br>&gt; x'x xéxxxxé x'xxxxxxxxx xx xxx \"Xxxx XXX Xxxxx"
-               "x Xxx\". Xx xxxx<br>&gt; xxxxxxxx xxx xxxxxxxxxxxxxxxx.xx (xxxxxxx xxxxxxxxxx "
-               "xx .xxx). Xxxx<br>&gt; êxxx xxx xxxxxxxxxxx xxxéxxxxxxxx, xxxx xxxxx xx XXX xx "
-               "xéxxx à xx<br>&gt; xxx \"xxx xxxxxx xxxx xx xxxxxxx\" xx xxxx xx xxxxx xxxxxxxx "
-               "xxxxxxxx<br>&gt; xx $ xx xxxx x'xxxxxx.</div><div style=\"width: 71ch;\">"
-               "&gt; <br></div><div style=\"width: 71ch;\">&gt; Xxxx xx xéxxxxxxx, xxxxxxxx xxxxxxx "
-               "(!), xxxxxxx à xxx, xxxx ooo$ XXX<br>&gt; xxxxé: <br>&gt; <a href=\"https://xxxxx";
-               "xxxxxxxxxxx.xx/xxxxxxx/xxxxx-xxxx-xxxxxxxx-xxxxx-xxxx-xxx-xxxxxxxx-xxx/\">https://";
-               "xxxxxxxxxxxxxxxx.xx/xxxxxxx/xxxxx-xxxx-xxxxxxxx-xxxxx-xxxx-xxx-xxxxxxxx-xxx/</a><br>"
-               "&gt; xx xx xxxx xéxéxxxxxxx x'xxxxxx xxxx xx xxxxxx xx xxxxxxxxxxxx xx xxx<br>&gt; ("
-               "xxxxx Xxxxxx) xxxx xxxx x'xxxxxxx xx xxxxxx: <br>&gt; <a href=\"https://xxxxxxxxxxxxxx";
-               "xx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxxxxxxxxx-Xxxxx-Xxxx-XXX-Xxxxxx-Xxx.xxx\">https://xxxxxx";
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div><blockquote 
type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Xxxxx xx xxxxxxxxx xx xxxxxxx xx xxxxx xxxx xxxx xx xxx xxx 
xxxx xxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxçx xôxé \"xxxxx xxxx xxxxxxx xxx\" xx xxxx xxxxé xxx xxx xxxéx 
xxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "x'x xéxxxxé x'xxxxxxxxx xx xxx \"Xxxx XXX Xxxxxx Xxx\". Xx xxxx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxxxxxx xxx xxxxxxxxxxxxxxxx.xx (xxxxxxx xxxxxxxxxx xx .xxx). 
Xxxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "êxxx xxx xxxxxxxxxxx xxxéxxxxxxxx, xxxx xxxxx xx XXX xx xéxxx à xx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxx \"xxx xxxxxx xxxx xx xxxxxxx\" xx xxxx xx xxxxx xxxxxxxx 
xxxxxxxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xx $ xx xxxx x'xxxxxx.</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Xxxx xx xéxxxxxxx, xxxxxxxx xxxxxxx (!), xxxxxxx à xxx, xxxx 
ooo$ XXX<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxxé:<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://xxxxxxxxxxxxxxxx.xx/xxxxxxx/xxxxx-xxxx-xxxxxxxx-xxxxx-xxxx-xxx-xxxxxxxx-xxx/\";>https://";
+               "xxxxxxxxxxxxxxxx.xx/xxxxxxx/xxxxx-xxxx-xxxxxxxx-xxxxx-xxxx-xxx-xxxxxxxx-xxx/</a><br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xx xx xxxx xéxéxxxxxxx x'xxxxxx xxxx xx xxxxxx xx xxxxxxxxxxxx xx 
xxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "(xxxxx Xxxxxx) xxxx xxxx x'xxxxxxx xx xxxxxx:<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://xxxxxxxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxxxxxxxxx-Xxxxx-Xxxx-XXX-Xxxxxx-Xxx.xxx\";>https://xxxxxx";
                "xxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxxxxxxxxx-Xxxxx-Xxxx-XXX-Xxxxxx-Xxx.xxx</a></div>"
-               "<div style=\"width: 71ch;\">&gt; <br></div><div style=\"width: 71ch;\">&gt; Xxxx xxx x"
-               "xx xxxxxxx xxxxxxxéxx x'xxxêxxxx à xxxxx, xxx xx xxxxé xx<br>&gt; oooxooo xxxxx xxxxx "
-               "xxxx... xxxx x'xxx xxxxxxxxxxxx xxxxx xxx<br>&gt; xxxxxxxx xx \"xx xxxxx xxx xxx xxxxx"
-               "xx xxxxxxx xxxxxxxxxxxxxx xxxx<br>&gt; xxxxx xxxxxx xx xx xxxx xx x'xxxxxx\". Xx xxxx-"
-               "êxxx xxx xx xxxxxxxx xx<br>&gt; xxxx \"x'xxxêxx à xxxxx xx oooxooo xxxx xxx xéxxxxxxxx, "
-               "xxxx\"...</div><div style=\"width: 71ch;\">&gt; <br></div><div style=\"width: 71ch;\">"
-               "&gt; Xxxxx xxxxxx'xx xxx x xxxx xxxxxxx xxxxx xx xxèx xxxxxxxxx<br>&gt; <br>&gt; xxxxxx"
-               "xxxxxxxxxx à xx xxx x'xx xx xêxx (éxxxxxxxxx xxxx-xx-xxxxxxxx): <a href=\"https://xxxxx";
-               "xxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-xxxxxxxxxx-xxxxx.xxx\">https://xxxxxx";
-               "xxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-<br>&gt; xxxxxxxxxx-xxxxx.xxx</a> ;&nbsp;"
-               "</div><div style=\"width: 71ch;\">&gt; <br></div><div style=\"width: 71ch;\">&gt; ...x'"
-               "x xxxxx xx xxxxxx x'xxxxxx xéxxxxxxx, xx xxx xxxx xxxxxx<br>&gt; x'xxxxxxxxxxx xxxxxx, "
-               "xxxx <br>&gt; <a href=\"https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/\";>"
-               "https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/</a> xxxxx xxx <br>&gt; <a "
-               "href=\"https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/\";>https://xxxxxxxxx";
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Xxxx xxx xxx xxxxxxx xxxxxxxéxx x'xxxêxxxx à xxxxx, xxx xx 
xxxxé xx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "oooxooo xxxxx xxxxx xxxx... xxxx x'xxx xxxxxxxxxxxx xxxxx xxx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxxxxxx xx \"xx xxxxx xxx xxx xxxxxxx xxxxxxx xxxxxxxxxxxxxx xxxx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxxx xxxxxx xx xx xxxx xx x'xxxxxx\". Xx xxxx-êxxx xxx xx xxxxxxxx 
xx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxx \"x'xxxêxx à xxxxx xx oooxooo xxxx xxx xéxxxxxxxx, 
xxxx\"...</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Xxxxx xxxxxx'xx xxx x xxxx xxxxxxx xxxxx xx xxèx xxxxxxxxx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "xxxxxxxxxxxxxxxx à xx xxx x'xx xx xêxx (éxxxxxxxxx 
xxxx-xx-xxxxxxxx):<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://xxxxxxxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-xxxxxxxxxx-xxxxx.xxx\";>https://xxxxxx";
+               "xxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-xxxxxxxxxx-xxxxx.xxx</a><br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) ";&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "...x'x xxxxx xx xxxxxx x'xxxxxx xéxxxxxxx, xx xxx xxxx 
xxxxxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "x'xxxxxxxxxxx xxxxxx, xxxx<br class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/\";>"
+               "https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/</a> xxxxx xxx<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/\";>https://xxxxxxxxx";
                "xxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/</a> ...</div></blockquote>" HTML_SUFFIX,
                "On Today, User wrote:\n"
                "> Xxxxx xx xxxxxxxxx xx xxxxxxx xx xxxxx xxxx xxxx xx xxx xxx xxxx xxx\n"
@@ -1127,10 +1263,10 @@ test_bug_788829 (TestFixture *fixture)
                "> xx $ xx xxxx x'xxxxxx.\n"
                "> \n"
                "> Xxxx xx xéxxxxxxx, xxxxxxxx xxxxxxx (!), xxxxxxx à xxx, xxxx ooo$ XXX\n"
-               "> xxxxé: \n"
+               "> xxxxé:\n"
                "> https://xxxxxxxxxxxxxxxx.xx/xxxxxxx/xxxxx-xxxx-xxxxxxxx-xxxxx-xxxx-xxx-xxxxxxxx-xxx/\n";
                "> xx xx xxxx xéxéxxxxxxx x'xxxxxx xxxx xx xxxxxx xx xxxxxxxxxxxx xx xxx\n"
-               "> (xxxxx Xxxxxx) xxxx xxxx x'xxxxxxx xx xxxxxx: \n"
+               "> (xxxxx Xxxxxx) xxxx xxxx x'xxxxxxx xx xxxxxx:\n"
                "> 
https://xxxxxxxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxxxxxxxxx-Xxxxx-Xxxx-XXX-Xxxxxx-Xxx.xxx\n";
                "> \n"
                "> Xxxx xxx xxx xxxxxxx xxxxxxxéxx x'xxxêxxxx à xxxxx, xxx xx xxxxé xx\n"
@@ -1140,14 +1276,14 @@ test_bug_788829 (TestFixture *fixture)
                "> xxxx \"x'xxxêxx à xxxxx xx oooxooo xxxx xxx xéxxxxxxxx, xxxx\"...\n"
                "> \n"
                "> Xxxxx xxxxxx'xx xxx x xxxx xxxxxxx xxxxx xx xxèx xxxxxxxxx\n"
-               "> \n"
-               "> xxxxxxxxxxxxxxxx à xx xxx x'xx xx xêxx (éxxxxxxxxx xxxx-xx-xxxxxxxx): 
https://xxxxxxxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-\n";
-               "> xxxxxxxxxx-xxxxx.xxx ; \n"
+               "> xxxxxxxxxxxxxxxx à xx xxx x'xx xx xêxx (éxxxxxxxxx xxxx-xx-xxxxxxxx):\n"
+               "> https://xxxxxxxxxxxxxxxx.xxx/xx-xxxxxxx/xxxxxxx/Xxxxx-xxxx-xxx-xxxxxxxxxx-xxxxx.xxx\n";
+               "> ; \n"
                "> \n"
                "> ...x'x xxxxx xx xxxxxx x'xxxxxx xéxxxxxxx, xx xxx xxxx xxxxxx\n"
-               "> x'xxxxxxxxxxx xxxxxx, xxxx \n"
-               "> https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/ xxxxx xxx \n"
-               "> https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/ ..."))
+               "> x'xxxxxxxxxxx xxxxxx, xxxx\n"
+               "> https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/ xxxxx xxx\n"
+               "> https://xxxxxxxxxxxxxxxx.xxx/xxxxxxxx-xxxxxxx-xxxx-xxx-o/ ...\n"))
                g_test_fail ();
 }
 
@@ -1213,26 +1349,26 @@ test_bug_750636 (TestFixture *fixture)
                "B\n\n"
                "12345678901234567890123456789012345678901234567890123456789012345678901\n"
                "C\n\n"
-               "1234567890123456789012345678901234567890123456789012345678901234567890 \n"
+               "1234567890123456789012345678901234567890123456789012345678901234567890\n"
                "D\n\n"
                "12345678901234567890123456789012345678901234567890123456789012345678901\n"
-               "   E\n\n"
-               "1234567890123456789012345678901234567890123456789012345678901234567890 \n"
-               "  F\n\n"
+               UNICODE_NBSP UNICODE_NBSP UNICODE_NBSP "E\n\n"
+               "1234567890123456789012345678901234567890123456789012345678901234567890" UNICODE_NBSP "\n"
+               UNICODE_NBSP UNICODE_NBSP "F\n\n"
                " 1\n"
                "  2\n"
                "   3\n"
                "\n"
-               "prefix text \n"
+               "prefix text\n"
                
"https://www.gnome.org/1234567890123456789012345678901234567890123456789012345678901234567890\n";
                "after text\n"
                "prefix text https://www.gnome.org/123456789012345678901234567890123\n";
                "after text\n"
-               "prefix text https://www.gnome.org/12345678901234567890 \n"
+               "prefix text https://www.gnome.org/12345678901234567890\n";
                "https://www.gnome.org/12345678901234567890 after text\n"
-               "prefix text \n"
+               "prefix text\n"
                
"https://www.gnome.org/1234567890123456789012345678901234567890123456789012345678901234567890\n";
-               " next line text\n"))
+               " next line text\n\n"))
                g_test_fail ();
 }
 
@@ -1343,7 +1479,7 @@ test_issue_86 (TestFixture *fixture)
                "> > \n"
                "> > level 1\n"
                "> \n"
-               "> back normal text"))
+               "> back normal text\n"))
                g_test_fail ();
 
        g_free (to_insert);
@@ -1375,10 +1511,10 @@ test_issue_103 (TestFixture *fixture)
                "before\n"
                LONG_URL "\n"
                "after\n"
-               "prefix text \n"
+               "prefix text\n"
                SHORTER_URL " suffix\n"
                "prefix " SHORT_URL " suffix\n"
-               "end")) {
+               "end\n")) {
                g_test_fail ();
                return;
        }
@@ -1398,14 +1534,15 @@ test_issue_104 (TestFixture *fixture)
                "seq:h\n"
                "action:show-replace\n"
                "type:e\t\n"
-               "type:\t\t\t\t\t\t\t\n" /* Jump to 'Replace All' */
-               "seq:n\n" /* Press it */
+               "seq:A\n" /* Press 'Alt+A' to press 'Replace All' button */
+               "type:a\n"
+               "seq:a\n"
                "seq:^\n" /* Close the dialog */
                "undo:undo\n"
                "undo:test:1\n"
                "undo:redo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">txt to rplac</div>" HTML_SUFFIX,
-               "txt to rplac"))
+               "txt to rplac\n"))
                g_test_fail ();
 }
 
@@ -1443,39 +1580,39 @@ test_issue_107 (TestFixture *fixture)
                "",
                HTML_PREFIX
                "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; text</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.01.org/\";>https://www.01.org/</a>&nbsp;?</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.02.org/\";>https://www.02.org/</a>&nbsp;A</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.03.org/\";>https://www.03.org/</a>&nbsp;ěšč</div>"
-               "<div style=\"width: 71ch;\">&gt; <a href=\"https://www.04.org/\";>https://www.04.org/</a> 
?</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.05.org/\";>https://www.05.org/</a></div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.06.org/\";>https://www.06.org/</a>&nbsp;</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.07.org/\";>https://www.07.org/</a>&nbsp;&nbsp;</div>"
-               "<div style=\"width: 71ch;\">&gt; <a 
href=\"https://www.08.org/\";>https://www.08.org/</a>&nbsp;&gt;&nbsp;&lt;&nbsp;</div>"
-               "<div style=\"width: 71ch;\">&gt; &lt;<a 
href=\"https://www.09.org/\";>https://www.09.org/</a>&gt;</div>"
-               "<div style=\"width: 71ch;\">&gt; &lt;<a 
href=\"https://www.10.org/\";>https://www.10.org/</a>&nbsp;?&gt;</div>"
-               "<div style=\"width: 71ch;\">&gt; &nbsp;<a 
href=\"https://www.11.org/\";>https://www.11.org/</a>&nbsp;</div>"
-               "<div style=\"width: 71ch;\">&gt; &lt;&nbsp;<a 
href=\"https://www.12.org/\";>https://www.12.org/</a>&nbsp;&gt;</div>"
-               "<div style=\"width: 71ch;\">&gt; &nbsp;&lt;<a 
href=\"https://www.13.org/\";>https://www.13.org/</a>&gt;&nbsp;</div>"
-               "<div style=\"width: 71ch;\">&gt; Text <a 
href=\"https://www.14.org/\";>https://www.14.org/</a>\temail: user@no.where</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "text</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.01.org/\";>https://www.01.org/</a>&nbsp;?</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.02.org/\";>https://www.02.org/</a>&nbsp;A</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.03.org/\";>https://www.03.org/</a>&nbsp;ěšč</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a href=\"https://www.04.org/\";>https://www.04.org/</a> 
?</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a href=\"https://www.05.org/\";>https://www.05.org/</a></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.06.org/\";>https://www.06.org/</a>&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.07.org/\";>https://www.07.org/</a>&nbsp;&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.08.org/\";>https://www.08.org/</a>&nbsp;&gt;&nbsp;&lt;&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "&lt;<a 
href=\"https://www.09.org/\";>https://www.09.org/</a>&gt;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "&lt;<a 
href=\"https://www.10.org/\";>https://www.10.org/</a>&nbsp;?&gt;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "&nbsp;<a 
href=\"https://www.11.org/\";>https://www.11.org/</a>&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "&lt;&nbsp;<a 
href=\"https://www.12.org/\";>https://www.12.org/</a>&nbsp;&gt;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "&nbsp;&lt;<a 
href=\"https://www.13.org/\";>https://www.13.org/</a>&gt;&nbsp;</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "Text <a 
href=\"https://www.14.org/\";>https://www.14.org/</a>\temail: <a 
href=\"mailto:user@no.where\";>user@no.where</a></div>"
                "</blockquote>" HTML_SUFFIX,
                "On Today, User wrote:\n"
                "> text\n"
-               "> https://www.01.org/ ?\n"
-               "> https://www.02.org/ A\n"
-               "> https://www.03.org/ ěšč\n"
+               "> https://www.01.org/"; UNICODE_NBSP "?\n"
+               "> https://www.02.org/"; UNICODE_NBSP "A\n"
+               "> https://www.03.org/"; UNICODE_NBSP "ěšč\n"
                "> https://www.04.org/ ?\n"
                "> https://www.05.org/\n";
-               "> https://www.06.org/ \n"
-               "> https://www.07.org/  \n"
-               "> https://www.08.org/ > < \n"
+               "> https://www.06.org/"; UNICODE_NBSP "\n"
+               "> https://www.07.org/"; UNICODE_NBSP UNICODE_NBSP "\n"
+               "> https://www.08.org/"; UNICODE_NBSP ">" UNICODE_NBSP "<" UNICODE_NBSP "\n"
                "> <https://www.09.org/>\n"
-               "> <https://www.10.org/ ?>\n"
-               ">  https://www.11.org/ \n"
-               "> < https://www.12.org/ >\n"
-               ">  <https://www.13.org/> \n"
-               "> Text https://www.14.org/\temail: user@no.where")) {
+               "> <https://www.10.org/"; UNICODE_NBSP "?>\n"
+               "> " UNICODE_NBSP "https://www.11.org/"; UNICODE_NBSP "\n"
+               "> <" UNICODE_NBSP "https://www.12.org/"; UNICODE_NBSP ">\n"
+               "> " UNICODE_NBSP "<https://www.13.org/>" UNICODE_NBSP "\n"
+               "> Text https://www.14.org/\temail: user@no.where\n")) {
                g_test_fail ();
        }
 }
@@ -1483,7 +1620,6 @@ test_issue_107 (TestFixture *fixture)
 void
 test_add_html_editor_bug_tests (void)
 {
-       test_utils_add_test ("/bug/726548", test_bug_726548);
        test_utils_add_test ("/bug/750657", test_bug_750657);
        test_utils_add_test ("/bug/760989", test_bug_760989);
        test_utils_add_test ("/bug/767903", test_bug_767903);
@@ -1502,8 +1638,8 @@ test_add_html_editor_bug_tests (void)
        test_utils_add_test ("/bug/775042", test_bug_775042);
        test_utils_add_test ("/bug/775691", test_bug_775691);
        test_utils_add_test ("/bug/779707", test_bug_779707);
-       test_utils_add_test ("/bug/780275/html", test_bug_780275_html);
-       test_utils_add_test ("/bug/780275/plain", test_bug_780275_plain);
+       test_utils_add_test ("/bug/780275-html", test_bug_780275_html);
+       test_utils_add_test ("/bug/780275-plain", test_bug_780275_plain);
        test_utils_add_test ("/bug/781722", test_bug_781722);
        test_utils_add_test ("/bug/781116", test_bug_781116);
        test_utils_add_test ("/bug/780088", test_bug_780088);
diff --git a/src/e-util/test-html-editor-units-bugs.h b/src/e-util/test-html-editor-units-bugs.h
index c5e78fef34..2a77efd6a3 100644
--- a/src/e-util/test-html-editor-units-bugs.h
+++ b/src/e-util/test-html-editor-units-bugs.h
@@ -20,6 +20,8 @@
 #define HTML_PREFIX "<html><head></head><body>"
 #define HTML_SUFFIX "</body></html>"
 #define BLOCKQUOTE_STYLE "style=\"margin:0 0 0 .8ex; border-left:2px #729fcf solid;padding-left:1ex\""
+#define QUOTE_SPAN(x) "<span class='-x-evo-quoted'>" x "</span>"
+#define QUOTE_CHR "<span class='-x-evo-quote-character'>&gt; </span>"
 
 G_BEGIN_DECLS
 
diff --git a/src/e-util/test-html-editor-units-utils.c b/src/e-util/test-html-editor-units-utils.c
index daedae625f..51ecd453d8 100644
--- a/src/e-util/test-html-editor-units-utils.c
+++ b/src/e-util/test-html-editor-units-utils.c
@@ -83,6 +83,50 @@ test_utils_free_global_memory (void)
        g_clear_object (&global_web_context);
 }
 
+typedef struct _GetContentData {
+       EContentEditorContentHash *content_hash;
+       gpointer async_data;
+} GetContentData;
+
+static void
+get_editor_content_hash_ready_cb (GObject *source_object,
+                                 GAsyncResult *result,
+                                 gpointer user_data)
+{
+       GetContentData *gcd = user_data;
+       GError *error = NULL;
+
+       g_assert_nonnull (gcd);
+       g_assert (E_IS_CONTENT_EDITOR (source_object));
+
+       gcd->content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, 
&error);
+
+       g_assert_no_error (error);
+
+       g_clear_error (&error);
+
+       test_utils_async_call_finish (gcd->async_data);
+}
+
+static EContentEditorContentHash *
+test_utils_get_editor_content_hash_sync (EContentEditor *cnt_editor,
+                                        guint32 flags)
+{
+       GetContentData gcd;
+
+       g_assert (E_IS_CONTENT_EDITOR (cnt_editor));
+
+       gcd.content_hash = NULL;
+       gcd.async_data = test_utils_async_call_prepare ();
+
+       e_content_editor_get_content (cnt_editor, flags, "test-domain", NULL, 
get_editor_content_hash_ready_cb, &gcd);
+
+       g_assert (test_utils_async_call_wait (gcd.async_data, MAX (event_processing_delay_ms / 25, 1) + 1));
+       g_assert_nonnull (gcd.content_hash);
+
+       return gcd.content_hash;
+}
+
 typedef struct _UndoContent {
        gchar *html;
        gchar *plain;
@@ -92,16 +136,20 @@ static UndoContent *
 undo_content_new (TestFixture *fixture)
 {
        EContentEditor *cnt_editor;
+       EContentEditorContentHash *content_hash;
        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);
+       content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML 
| E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
 
        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);
+       uc->html = e_content_editor_util_steal_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_HTML, 
NULL);
+       uc->plain = e_content_editor_util_steal_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, NULL);
+
+       e_content_editor_util_free_content_hash (content_hash);
 
        g_warn_if_fail (uc->html != NULL);
        g_warn_if_fail (uc->plain != NULL);
@@ -127,15 +175,17 @@ undo_content_test (TestFixture *fixture,
                   gint cmd_index)
 {
        EContentEditor *cnt_editor;
-       gchar *text;
+       EContentEditorContentHash *content_hash;
+       const 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);
+       content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML 
| E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
 
-       text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL);
+       text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_HTML);
        g_return_val_if_fail (text != NULL, FALSE);
 
        if (!test_utils_html_equal (fixture, text, uc->html)) {
@@ -143,13 +193,13 @@ undo_content_test (TestFixture *fixture,
                        g_printerr ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do not match 
at command %d\n", G_STRFUNC, text, uc->html, cmd_index);
                else
                        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);
+
+               e_content_editor_util_free_content_hash (content_hash);
+
                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);
+       text = e_content_editor_util_get_content_data (content_hash, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
        g_return_val_if_fail (text != NULL, FALSE);
 
        if (!test_utils_html_equal (fixture, text, uc->plain)) {
@@ -157,11 +207,13 @@ undo_content_test (TestFixture *fixture,
                        g_printerr ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do not 
match at command %d\n", G_STRFUNC, text, uc->plain, cmd_index);
                else
                        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);
+
+               e_content_editor_util_free_content_hash (content_hash);
+
                return FALSE;
        }
 
-       g_free (text);
+       e_content_editor_util_free_content_hash (content_hash);
 
        return TRUE;
 }
@@ -175,9 +227,32 @@ test_utils_web_process_crashed_cb (WebKitWebView *web_view,
        return FALSE;
 }
 
+/* <Control>+<Shift>+I */
+#define WEBKIT_INSPECTOR_MOD  (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
+#define WEBKIT_INSPECTOR_KEY  (GDK_KEY_I)
+
+static gboolean
+wk_editor_key_press_event_cb (WebKitWebView *web_view,
+                             GdkEventKey *event)
+{
+       WebKitWebInspector *inspector;
+       gboolean handled = FALSE;
+
+       inspector = webkit_web_view_get_inspector (web_view);
+
+       if ((event->state & WEBKIT_INSPECTOR_MOD) == WEBKIT_INSPECTOR_MOD &&
+           event->keyval == WEBKIT_INSPECTOR_KEY) {
+               webkit_web_inspector_show (inspector);
+               handled = TRUE;
+       }
+
+       return handled;
+}
+
 typedef struct _CreateData {
        gpointer async_data;
        TestFixture *fixture;
+       gboolean created;
 } CreateData;
 
 static void
@@ -193,6 +268,8 @@ test_utils_html_editor_created_cb (GObject *source_object,
 
        g_return_if_fail (create_data != NULL);
 
+       create_data->created = TRUE;
+
        fixture = create_data->fixture;
 
        html_editor = e_html_editor_new_finish (result, &error);
@@ -213,6 +290,26 @@ test_utils_html_editor_created_cb (GObject *source_object,
        gtk_widget_show (GTK_WIDGET (fixture->editor));
        gtk_container_add (GTK_CONTAINER (fixture->window), GTK_WIDGET (fixture->editor));
 
+       fixture->focus_tracker = e_focus_tracker_new (GTK_WINDOW (fixture->window));
+
+       e_focus_tracker_set_cut_clipboard_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "cut"));
+
+       e_focus_tracker_set_copy_clipboard_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "copy"));
+
+       e_focus_tracker_set_paste_clipboard_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "paste"));
+
+       e_focus_tracker_set_select_all_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "select-all"));
+
+       e_focus_tracker_set_undo_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "undo"));
+
+       e_focus_tracker_set_redo_action (fixture->focus_tracker,
+               e_html_editor_get_action (fixture->editor, "redo"));
+
        /* Make sure this is off */
        test_utils_fixture_change_setting_boolean (fixture,
                "org.gnome.evolution.mail", "prompt-on-composer-mode-switch", FALSE);
@@ -229,12 +326,23 @@ test_utils_html_editor_created_cb (GObject *source_object,
        g_signal_connect (cnt_editor, "web-process-crashed",
                G_CALLBACK (test_utils_web_process_crashed_cb), NULL);
 
-       if (!test_utils_get_multiple_web_processes () && !global_web_context &&
-           WEBKIT_IS_WEB_VIEW (cnt_editor)) {
-               WebKitWebContext *web_context;
+       if (WEBKIT_IS_WEB_VIEW (cnt_editor)) {
+               WebKitSettings *web_settings;
+
+               web_settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (cnt_editor));
+               webkit_settings_set_enable_developer_extras (web_settings, TRUE);
+               webkit_settings_set_enable_write_console_messages_to_stdout (web_settings, TRUE);
+
+               g_signal_connect (
+                       cnt_editor, "key-press-event",
+                       G_CALLBACK (wk_editor_key_press_event_cb), NULL);
 
-               web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (cnt_editor));
-               global_web_context = g_object_ref (web_context);
+               if (!test_utils_get_multiple_web_processes () && !global_web_context) {
+                       WebKitWebContext *web_context;
+
+                       web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (cnt_editor));
+                       global_web_context = g_object_ref (web_context);
+               }
        }
 
        gtk_window_set_focus (GTK_WINDOW (fixture->window), GTK_WIDGET (cnt_editor));
@@ -254,6 +362,8 @@ test_utils_add_test (const gchar *name,
                test_utils_fixture_set_up, (ETestFixtureFunc) func, test_utils_fixture_tear_down);
 }
 
+static void test_utils_async_call_free (gpointer async_data);
+
 void
 test_utils_fixture_set_up (TestFixture *fixture,
                           gconstpointer user_data)
@@ -271,10 +381,14 @@ test_utils_fixture_set_up (TestFixture *fixture,
 
        create_data.async_data = test_utils_async_call_prepare ();
        create_data.fixture = fixture;
+       create_data.created = FALSE;
 
        e_html_editor_new (test_utils_html_editor_created_cb, &create_data);
 
-       test_utils_async_call_wait (create_data.async_data, 60);
+       if (create_data.created)
+               test_utils_async_call_free (create_data.async_data);
+       else
+               test_utils_async_call_wait (create_data.async_data, 60);
 
        g_warn_if_fail (fixture->editor != NULL);
        g_warn_if_fail (E_IS_HTML_EDITOR (fixture->editor));
@@ -303,6 +417,8 @@ void
 test_utils_fixture_tear_down (TestFixture *fixture,
                              gconstpointer user_data)
 {
+       g_clear_object (&fixture->focus_tracker);
+
        gtk_widget_destroy (GTK_WIDGET (fixture->window));
        fixture->editor = NULL;
 
@@ -385,6 +501,16 @@ test_utils_flush_main_context (void)
        }
 }
 
+static void
+test_utils_async_call_free (gpointer async_data)
+{
+       GMainLoop *loop = async_data;
+
+       test_utils_flush_main_context ();
+
+       g_main_loop_unref (loop);
+}
+
 gpointer
 test_utils_async_call_prepare (void)
 {
@@ -440,9 +566,7 @@ test_utils_async_call_wait (gpointer async_data,
                g_source_unref (source);
        }
 
-       test_utils_flush_main_context ();
-
-       g_main_loop_unref (loop);
+       test_utils_async_call_free (async_data);
 
        return !async_call_data.timeout_reached;
 }
@@ -566,16 +690,57 @@ test_utils_type_text (TestFixture *fixture,
        return TRUE;
 }
 
+typedef struct _HTMLEqualData {
+       gpointer async_data;
+       gboolean equal;
+} HTMLEqualData;
+
+static void
+test_html_equal_done_cb (GObject *source_object,
+                        GAsyncResult *result,
+                        gpointer user_data)
+{
+       HTMLEqualData *hed = user_data;
+       WebKitJavascriptResult *js_result;
+       JSCException *exception;
+       JSCValue *js_value;
+       GError *error = NULL;
+
+       g_return_if_fail (hed != NULL);
+
+       js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source_object), result, &error);
+
+       g_assert_no_error (error);
+       g_clear_error (&error);
+
+       g_assert_nonnull (js_result);
+
+       js_value = webkit_javascript_result_get_js_value (js_result);
+       g_assert_nonnull (js_value);
+       g_assert (jsc_value_is_boolean (js_value));
+
+       hed->equal = jsc_value_to_boolean (js_value);
+
+       exception = jsc_context_get_exception (jsc_value_get_context (js_value));
+
+       if (exception) {
+               g_warning ("Failed to call EvoEditorTest.isHTMLEqual: %s", jsc_exception_get_message 
(exception));
+               jsc_context_clear_exception (jsc_value_get_context (js_value));
+       }
+
+       webkit_javascript_result_unref (js_result);
+
+       test_utils_async_call_finish (hed->async_data);
+}
+
 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;
+       gchar *script;
+       HTMLEqualData hed;
 
        g_return_val_if_fail (fixture != NULL, FALSE);
        g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
@@ -584,29 +749,33 @@ test_utils_html_equal (TestFixture *fixture,
 
        cnt_editor = e_html_editor_get_content_editor (fixture->editor);
        g_return_val_if_fail (cnt_editor != NULL, FALSE);
+       g_return_val_if_fail (WEBKIT_IS_WEB_VIEW (cnt_editor), FALSE);
 
-       g_object_get (cnt_editor, "web-extension", &web_extension, NULL);
-
-       g_return_val_if_fail (G_IS_DBUS_PROXY (web_extension), FALSE);
+       script = e_web_view_jsc_printf_script (
+               "var EvoEditorTest = {};\n"
+               "EvoEditorTest.isHTMLEqual = function(html1, html2) {\n"
+               "       var elem1, elem2;\n"
+               "       elem1 = document.createElement(\"testHtmlEqual\");\n"
+               "       elem2 = document.createElement(\"testHtmlEqual\");\n"
+               "       elem1.innerHTML = html1.replace(/&nbsp;/g, \" \").replace(/ /g, \" \");\n"
+               "       elem2.innerHTML = html2.replace(/&nbsp;/g, \" \").replace(/ /g, \" \");\n"
+               "       elem1.normalize();\n"
+               "       elem2.normalize();\n"
+               "       return elem1.isEqualNode(elem2);\n"
+               "}\n"
+               "EvoEditorTest.isHTMLEqual(%s, %s);", html1, html2);
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_full (
-               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);
+       hed.async_data = test_utils_async_call_prepare();
+       hed.equal = FALSE;
 
-       g_clear_error (&error);
+       webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (cnt_editor), script, NULL,
+               test_html_equal_done_cb, &hed);
 
-       g_return_val_if_fail (result != NULL, FALSE);
+       test_utils_async_call_wait (hed.async_data, 10);
 
-       g_variant_get (result, "(b)", &html_equal);
-       g_variant_unref (result);
+       g_free (script);
 
-       return html_equal;
+       return hed.equal;
 }
 
 static gboolean
@@ -781,6 +950,21 @@ test_utils_execute_action (TestFixture *fixture,
        return TRUE;
 }
 
+static gboolean
+test_utils_set_font_name (TestFixture *fixture,
+                         const gchar *font_name)
+{
+       EContentEditor *cnt_editor;
+
+       g_return_val_if_fail (fixture != NULL, FALSE);
+       g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
+
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       e_content_editor_set_font_name (cnt_editor, font_name);
+
+       return TRUE;
+}
+
 /* Expects only the part like "undo" [ ":" number ] */
 static gint
 test_utils_maybe_extract_undo_number (const gchar *command)
@@ -825,6 +1009,7 @@ test_utils_pick_undo_content (const GSList *undo_stack,
 
    command   = actioncmd ; Execute an action
              / modecmd   ; Change editor mode to HTML or Plain Text
+             / fnmcmd    ; Set font name
              / seqcmd    ; Sequence of special key strokes
              / typecmd   ; Type a text
              / undocmd   ; Undo/redo commands
@@ -832,7 +1017,9 @@ test_utils_pick_undo_content (const GSList *undo_stack,
 
    actioncmd = "action:" name
 
-   actioncmd = "mode:" ("html" / "plain")
+   modecmd   = "mode:" ("html" / "plain")
+
+   fnmcmd    = "font-name:" name
 
    seqcmd    = "seq:" sequence
 
@@ -899,6 +1086,8 @@ test_utils_process_commands (TestFixture *fixture,
                                success = FALSE;
                                g_warning ("%s: Unknown mode '%s'", G_STRFUNC, mode_change);
                        }
+               } else if (g_str_has_prefix (command, "font-name:")) {
+                       success = test_utils_set_font_name (fixture, command + 10);
                } else if (g_str_has_prefix (command, "seq:")) {
                        success = test_utils_process_sequence (fixture, command + 4);
                } else if (g_str_has_prefix (command, "type:")) {
@@ -983,7 +1172,8 @@ test_utils_run_simple_test (TestFixture *fixture,
                            const gchar *expected_plain)
 {
        EContentEditor *cnt_editor;
-       gchar *text;
+       EContentEditorContentHash *content_hash;
+       const gchar *text;
 
        g_return_val_if_fail (fixture != NULL, FALSE);
        g_return_val_if_fail (E_IS_HTML_EDITOR (fixture->editor), FALSE);
@@ -994,8 +1184,10 @@ test_utils_run_simple_test (TestFixture *fixture,
        if (!test_utils_process_commands (fixture, commands))
                return FALSE;
 
+       content_hash = test_utils_get_editor_content_hash_sync (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_HTML 
| E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
+
        if (expected_html) {
-               text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_HTML, NULL, NULL);
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_HTML);
                g_return_val_if_fail (text != NULL, FALSE);
 
                if (!test_utils_html_equal (fixture, text, expected_html)) {
@@ -1003,15 +1195,15 @@ test_utils_run_simple_test (TestFixture *fixture,
                                g_printerr ("%s: returned HTML\n---%s---\n and expected HTML\n---%s---\n do 
not match\n", G_STRFUNC, text, expected_html);
                        else
                                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);
+
+                       e_content_editor_util_free_content_hash (content_hash);
+
                        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);
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
                g_return_val_if_fail (text != NULL, FALSE);
 
                if (!test_utils_html_equal (fixture, text, expected_plain)) {
@@ -1019,13 +1211,15 @@ test_utils_run_simple_test (TestFixture *fixture,
                                g_printerr ("%s: returned Plain\n---%s---\n and expected Plain\n---%s---\n do 
not match\n", G_STRFUNC, text, expected_plain);
                        else
                                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);
+
+                       e_content_editor_util_free_content_hash (content_hash);
+
                        return FALSE;
                }
-
-               g_free (text);
        }
 
+       e_content_editor_util_free_content_hash (content_hash);
+
        return TRUE;
 }
 
@@ -1099,35 +1293,35 @@ test_utils_get_content_editor (TestFixture *fixture)
 }
 
 gchar *
-test_utils_get_base64_data_for_image (const gchar *path)
+test_utils_dup_image_uri (const gchar *path)
 {
-       gchar *image_data = NULL;
-       gchar *image_data_base64;
-       gsize image_data_length = 0;
-       gboolean success;
+       gchar *image_uri = NULL;
        GError *error = NULL;
 
        if (path && strchr (path, G_DIR_SEPARATOR)) {
-               success = g_file_get_contents (path, &image_data, &image_data_length, &error);
+               image_uri = g_filename_to_uri (path, NULL, &error);
        } else {
                gchar *filename;
 
                filename = e_icon_factory_get_icon_filename (path, GTK_ICON_SIZE_MENU);
                if (filename) {
-                       success = g_file_get_contents (filename, &image_data, &image_data_length, &error);
+                       image_uri = g_filename_to_uri (filename, NULL, &error);
                        g_free (filename);
                } else {
                        g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Icon '%s' not found", path);
-                       success = FALSE;
                }
        }
 
-       g_assert_no_error (error);
-       g_assert (success);
+       if (image_uri) {
+               gchar *tmp;
 
-       image_data_base64 = g_base64_encode ((const guchar *) image_data, image_data_length);
+               tmp = g_strconcat ("evo-", image_uri, NULL);
+               g_free (image_uri);
+               image_uri = tmp;
+       }
 
-       g_free (image_data);
+       g_assert_no_error (error);
+       g_assert_nonnull (image_uri);
 
-       return image_data_base64;
+       return image_uri;
 }
diff --git a/src/e-util/test-html-editor-units-utils.h b/src/e-util/test-html-editor-units-utils.h
index ee79ac4e02..a24ba56033 100644
--- a/src/e-util/test-html-editor-units-utils.h
+++ b/src/e-util/test-html-editor-units-utils.h
@@ -31,6 +31,7 @@ typedef struct _TestSettings {
 typedef struct _TestFixture {
        GtkWidget *window;
        EHTMLEditor *editor;
+       EFocusTracker *focus_tracker;
        GSList *settings; /* TestSettings * */
        guint key_state;
 
@@ -104,8 +105,7 @@ void                test_utils_set_clipboard_text   (const gchar *text,
 gchar *                test_utils_get_clipboard_text   (gboolean request_html);
 EContentEditor *
                test_utils_get_content_editor   (TestFixture *fixture);
-gchar *                test_utils_get_base64_data_for_image
-                                               (const gchar *path);
+gchar *                test_utils_dup_image_uri        (const gchar *path);
 
 G_END_DECLS
 
diff --git a/src/e-util/test-html-editor-units.c b/src/e-util/test-html-editor-units.c
index 9d3035f9bd..ddbf0470e9 100644
--- a/src/e-util/test-html-editor-units.c
+++ b/src/e-util/test-html-editor-units.c
@@ -47,7 +47,7 @@ test_style_bold_selection (TestFixture *fixture)
                "seq:hCrcrCSrsc\n"
                "action:bold\n",
                HTML_PREFIX "<div>some <b>bold</b> text</div>" HTML_SUFFIX,
-               "some bold text"))
+               "some bold text\n"))
                g_test_fail ();
 }
 
@@ -62,7 +62,7 @@ test_style_bold_typed (TestFixture *fixture)
                "action:bold\n"
                "type: text\n",
                HTML_PREFIX "<div>some <b>bold</b> text</div>" HTML_SUFFIX,
-               "some bold text"))
+               "some bold text\n"))
                g_test_fail ();
 }
 
@@ -75,7 +75,7 @@ test_style_italic_selection (TestFixture *fixture)
                "seq:hCrcrCSrsc\n"
                "action:italic\n",
                HTML_PREFIX "<div>some <i>italic</i> text</div>" HTML_SUFFIX,
-               "some italic text"))
+               "some italic text\n"))
                g_test_fail ();
 }
 
@@ -90,7 +90,7 @@ test_style_italic_typed (TestFixture *fixture)
                "action:italic\n"
                "type: text\n",
                HTML_PREFIX "<div>some <i>italic</i> text</div>" HTML_SUFFIX,
-               "some italic text"))
+               "some italic text\n"))
                g_test_fail ();
 }
 
@@ -103,7 +103,7 @@ test_style_underline_selection (TestFixture *fixture)
                "seq:hCrcrCSrsc\n"
                "action:underline\n",
                HTML_PREFIX "<div>some <u>underline</u> text</div>" HTML_SUFFIX,
-               "some underline text"))
+               "some underline text\n"))
                g_test_fail ();
 }
 
@@ -118,7 +118,7 @@ test_style_underline_typed (TestFixture *fixture)
                "action:underline\n"
                "type: text\n",
                HTML_PREFIX "<div>some <u>underline</u> text</div>" HTML_SUFFIX,
-               "some underline text"))
+               "some underline text\n"))
                g_test_fail ();
 }
 
@@ -131,7 +131,7 @@ test_style_strikethrough_selection (TestFixture *fixture)
                "seq:hCrcrCSrsc\n"
                "action:strikethrough\n",
                HTML_PREFIX "<div>some <strike>strikethrough</strike> text</div>" HTML_SUFFIX,
-               "some strikethrough text"))
+               "some strikethrough text\n"))
                g_test_fail ();
 }
 
@@ -146,7 +146,7 @@ test_style_strikethrough_typed (TestFixture *fixture)
                "action:strikethrough\n"
                "type: text\n",
                HTML_PREFIX "<div>some <strike>strikethrough</strike> text</div>" HTML_SUFFIX,
-               "some strikethrough text"))
+               "some strikethrough text\n"))
                g_test_fail ();
 }
 
@@ -157,9 +157,9 @@ test_style_monospace_selection (TestFixture *fixture)
                "mode:html\n"
                "type:some monospace text\n"
                "seq:hCrcrCSrsc\n"
-               "action:monospaced\n",
-               HTML_PREFIX "<div>some <font face=\"monospace\" size=\"3\">monospace</font> text</div>" 
HTML_SUFFIX,
-               "some monospace text"))
+               "font-name:monospace\n",
+               HTML_PREFIX "<div>some <font face=\"monospace\">monospace</font> text</div>" HTML_SUFFIX,
+               "some monospace text\n"))
                g_test_fail ();
 }
 
@@ -169,12 +169,12 @@ test_style_monospace_typed (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture,
                "mode:html\n"
                "type:some \n"
-               "action:monospaced\n"
+               "font-name:monospace\n"
                "type:monospace\n"
-               "action:monospaced\n"
+               "font-name:\n"
                "type: text\n",
-               HTML_PREFIX "<div>some <font face=\"monospace\" size=\"3\">monospace</font> text</div>" 
HTML_SUFFIX,
-               "some monospace text"))
+               HTML_PREFIX "<div>some <font face=\"monospace\">monospace</font> text</div>" HTML_SUFFIX,
+               "some monospace text\n"))
                g_test_fail ();
 }
 
@@ -193,13 +193,14 @@ test_justify_selection (TestFixture *fixture)
                "seq:d\n"
                "action:justify-left\n",
                HTML_PREFIX
-                       "<div style=\"text-align: center\">center</div>"
-                       "<div style=\"text-align: right\">right</div>"
+                       "<div style=\"text-align: center;\">center</div>"
+                       "<div style=\"text-align: right;\">right</div>"
                        "<div>left</div><div><br></div>"
                HTML_SUFFIX,
                "                                center\n"
                "                                                                  right\n"
-               "left\n"))
+               "left\n"
+               "\n"))
                g_test_fail ();
 }
 
@@ -215,13 +216,14 @@ test_justify_typed (TestFixture *fixture)
                "action:justify-left\n"
                "type:left\\n\n",
                HTML_PREFIX
-                       "<div style=\"text-align: center\">center</div>"
-                       "<div style=\"text-align: right\">right</div>"
+                       "<div style=\"text-align: center;\">center</div>"
+                       "<div style=\"text-align: right;\">right</div>"
                        "<div>left</div><div><br></div>"
                HTML_SUFFIX,
                "                                center\n"
                "                                                                  right\n"
-               "left\n"))
+               "left\n"
+               "\n"))
                g_test_fail ();
 }
 
@@ -245,16 +247,16 @@ test_indent_selection (TestFixture *fixture)
                "action:unindent\n",
                HTML_PREFIX
                        "<div>level 0</div>"
-                       "<div style=\"margin-left: 3ch;\">"
-                               "<div>level 1</div>"
-                               "<div style=\"margin-left: 3ch;\"><div>level 2</div></div>"
-                               "<div>level 1</div>"
-                       "</div><div><br></div>"
+                       "<div style=\"margin-left: 3ch;\">level 1</div>"
+                       "<div style=\"margin-left: 6ch;\">level 2</div>"
+                       "<div style=\"margin-left: 3ch;\">level 1</div>"
+                       "<div><br></div>"
                HTML_SUFFIX,
                "level 0\n"
                "   level 1\n"
                "      level 2\n"
-               "   level 1\n"))
+               "   level 1\n"
+               "\n"))
                g_test_fail ();
 }
 
@@ -273,16 +275,16 @@ test_indent_typed (TestFixture *fixture)
                "action:unindent\n",
                HTML_PREFIX
                        "<div>level 0</div>"
-                       "<div style=\"margin-left: 3ch;\">"
-                               "<div>level 1</div>"
-                               "<div style=\"margin-left: 3ch;\"><div>level 2</div></div>"
-                               "<div>level 1</div>"
-                       "</div><div><br></div>"
+                       "<div style=\"margin-left: 3ch;\">level 1</div>"
+                       "<div style=\"margin-left: 6ch;\">level 2</div>"
+                       "<div style=\"margin-left: 3ch;\">level 1</div>"
+                       "<div><br></div>"
                HTML_SUFFIX,
                "level 0\n"
                "   level 1\n"
                "      level 2\n"
-               "   level 1\n"))
+               "   level 1\n"
+               "\n"))
                g_test_fail ();
 }
 
@@ -308,7 +310,7 @@ test_font_size_selection (TestFixture *fixture)
                "action:size-plus-four\n",
                HTML_PREFIX "<div><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></div>" HTML_SUFFIX,
-               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4"))
+               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4\n"))
                g_test_fail ();
 }
 
@@ -343,9 +345,10 @@ test_font_size_typed (TestFixture *fixture)
                "action:size-plus-four\n"
                "type:FontP4\n"
                "action:size-plus-zero\n",
-               HTML_PREFIX "<div><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><br></div>" HTML_SUFFIX,
-               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4"))
+               HTML_PREFIX "<div><font size=\"1\">FontM2</font><font size=\"3\"> </font><font 
size=\"2\">FontM1</font><font size=\"3\"> Font0 </font>"
+               "<font size=\"4\">FontP1</font><font size=\"3\"> </font><font size=\"5\">FontP2</font><font 
size=\"3\"> </font>"
+               "<font size=\"6\">FontP3</font><font size=\"3\"> </font><font size=\"7\">FontP4</font></div>" 
HTML_SUFFIX,
+               "FontM2 FontM1 Font0 FontP1 FontP2 FontP3 FontP4\n"))
                g_test_fail ();
 }
 
@@ -405,7 +408,7 @@ test_font_color_selection (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture, "",
                HTML_PREFIX "<div>default <font color=\"#ff0000\">red</font> <font 
color=\"#00ff00\">green</font> "
                "<font color=\"#0000ff\">blue</font></div>" HTML_SUFFIX,
-               "default red green blue"))
+               "default red green blue\n"))
                g_test_fail ();
 }
 
@@ -470,7 +473,7 @@ test_font_color_typed (TestFixture *fixture)
        if (!test_utils_run_simple_test (fixture, "",
                HTML_PREFIX "<div>default <font color=\"#ff0000\">red </font><font color=\"#00ff00\">green 
</font>"
                "<font color=\"#0000ff\">blue</font></div>" HTML_SUFFIX,
-               "default red green blue"))
+               "default red green blue\n"))
                g_test_fail ();
 }
 
@@ -489,7 +492,7 @@ test_list_bullet_plain (TestFixture *fixture)
                " * item 1\n"
                " * item 2\n"
                " * item 3\n"
-               "text"))
+               "text\n"))
                g_test_fail ();
 }
 
@@ -517,9 +520,9 @@ test_list_bullet_html (TestFixture *fixture)
                        "<div>text</div>"
                HTML_SUFFIX,
                " * item 1\n"
-               "    * item 2\n"
+               "    - item 2\n"
                " * item 3\n"
-               "text"))
+               "text\n"))
                g_test_fail ();
 }
 
@@ -531,7 +534,7 @@ test_list_bullet_change (TestFixture *fixture)
                "action:style-list-bullet\n"
                "action:style-list-number\n",
                NULL,
-               "   1. "))
+               "   1. \n"))
                g_test_fail ();
 }
 
@@ -559,7 +562,7 @@ test_list_bullet_html_from_block (TestFixture *fixture)
                " * item 1\n"
                " * item 2\n"
                " * item 3\n"
-               " * "))
+               " * \n"))
                g_test_fail ();
 }
 
@@ -587,9 +590,9 @@ test_list_alpha_html (TestFixture *fixture)
                        "<div>text</div>"
                HTML_SUFFIX,
                "   A. item 1\n"
-               "      A. item 2\n"
+               "         A. item 2\n"
                "   B. item 3\n"
-               "text"))
+               "text\n"))
                g_test_fail ();
 }
 
@@ -608,9 +611,60 @@ test_list_alpha_plain (TestFixture *fixture)
                "type:text\n",
                NULL,
                "   A. item 1\n"
-               "      A. item 2\n"
+               "         A. item 2\n"
                "   B. item 3\n"
-               "text"))
+               "text\n"))
+               g_test_fail ();
+}
+
+static void
+test_list_number_html (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:style-list-number\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>"
+                               "<li>item 1</li>"
+                               "<ol>"
+                                       "<li>item 2</li>"
+                               "</ol>"
+                               "<li>item 3</li>"
+                       "</ol>"
+                       "<div>text</div>"
+               HTML_SUFFIX,
+               "   1. item 1\n"
+               "         1. item 2\n"
+               "   2. item 3\n"
+               "text\n"))
+               g_test_fail ();
+}
+
+static void
+test_list_number_plain (TestFixture *fixture)
+{
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:style-list-number\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,
+               "   1. item 1\n"
+               "         1. item 2\n"
+               "   2. item 3\n"
+               "text\n"))
                g_test_fail ();
 }
 
@@ -643,24 +697,24 @@ test_list_roman_html (TestFixture *fixture)
                "<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"))
+               "    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\n"))
                g_test_fail ();
 }
 
@@ -689,24 +743,24 @@ test_list_roman_plain (TestFixture *fixture)
                "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"))
+               "    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\n"))
                g_test_fail ();
 }
 
@@ -737,7 +791,7 @@ test_list_multi_html (TestFixture *fixture)
                " * item 2\n"
                "   I. item 3\n"
                "  II. item 4\n"
-               " III. "))
+               " III. \n"))
                g_test_fail ();
 }
 
@@ -758,7 +812,7 @@ test_list_multi_plain (TestFixture *fixture)
                " * item 2\n"
                "   I. item 3\n"
                "  II. item 4\n"
-               " III. "))
+               " III. \n"))
                g_test_fail ();
 }
 
@@ -789,7 +843,7 @@ test_list_multi_change_html (TestFixture *fixture)
                "   2. item 2\n"
                "   3. item 3\n"
                "   4. item 4\n"
-               "   5. "))
+               "   5. \n"))
                g_test_fail ();
 }
 
@@ -812,410 +866,3046 @@ test_list_multi_change_plain (TestFixture *fixture)
                "   2. item 2\n"
                "   3. item 3\n"
                "   4. item 4\n"
-               "   5. "))
+               "   5. \n"))
                g_test_fail ();
 }
 
 static void
-test_link_insert_dialog (TestFixture *fixture)
+test_list_indent_same (TestFixture *fixture,
+                      gboolean is_html)
 {
-       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 "<div>a link example: <a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></div>" HTML_SUFFIX,
-               "a link example: http://www.gnome.org";))
-               g_test_fail ();
-}
+       const gchar *unindented_html, *unindented_plain;
+
+       unindented_html =
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n";
 
-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 "<div>a link example: <a href=\"http://www.gnome.org\";>GNOME</a></div>" 
HTML_SUFFIX,
-               "a link example: GNOME"))
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "type:b\\n\n"
+               "type:c\\n\n"
+               "type:d\\n\n"
+               "seq:nb\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
-}
+               return;
+       }
+
+       /* Changing all items */
 
-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 "<div><a href=\"http://www.gnome.org\";>www.gnome.org</a> </div>" HTML_SUFFIX,
-               "www.gnome.org "))
+               "undo:save\n" /* 1 */
+               "seq:uuuSddds\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>a</pre>"
+                       "<pre>b</pre>"
+                       "<pre>c</pre>"
+                       "<pre>d</pre>"
+               HTML_SUFFIX,
+               "a\n"
+               "b\n"
+               "c\n"
+               "d\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-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:A\n" /* Alt+D to jump to the Description */
-               "type:D\n"
-               "seq:a\n"
-               "type:GNOME\n"
-               "seq:n\n",
-               HTML_PREFIX "<div><a href=\"http://www.gnome.org\";>GNOME</a> </div>" HTML_SUFFIX,
-               "GNOME "))
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<ul>"
+                                       "<li>a</li>"
+                                       "<li>b</li>"
+                                       "<li>c</li>"
+                                       "<li>d</li>"
+                               "</ul>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "    - a\n"
+               "    - b\n"
+               "    - c\n"
+               "    - d\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-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:A\n" /* Alt+R to press the 'Remove Link' */
-               "type:R\n"
-               "seq:a\n",
-               HTML_PREFIX "<div>www.gnome.org </div>" HTML_SUFFIX,
-               "www.gnome.org "))
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
-}
+               return;
+       }
 
-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 "<div><a href=\"http://www.gnome.org/\";>www.gnome.org/about</a> </div>" 
HTML_SUFFIX,
-               "www.gnome.org/about "))
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
-}
+               return;
+       }
+
+       /* Changing first item, single select */
 
-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 "<div><a href=\"http://www.gnome.org\";>www.gnome.o</a></div>" HTML_SUFFIX,
-               "www.gnome.o"))
+               "undo:save\n" /* 1 */
+               "seq:uuu\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>a</pre>"
+                       "<ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-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:^\n",  /* Escape key press to close the dialog */
-               HTML_PREFIX "<div>text</div><hr align=\"left\" size=\"2\" noshade=\"\">" HTML_SUFFIX,
-               "text"))
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<ul><li>a</li></ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "    - a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-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:drn\n" /* Press the right key instead of End key as the End key won't move caret after 
the HR element */
-               "type:below\n",
-               HTML_PREFIX "<div>above</div><hr align=\"left\" size=\"2\" noshade=\"\"><div>below</div>" 
HTML_SUFFIX,
-               "above\nbelow"))
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
-}
-
-static void
-test_image_insert (TestFixture *fixture)
-{
-       EContentEditor *cnt_editor;
-       gchar *expected_html;
-       gchar *filename;
-       gchar *image_data_base64;
-       gchar *uri;
-       GError *error = NULL;
+               return;
+       }
 
-       if (!test_utils_process_commands (fixture,
-               "mode:html\n"
-               "type:before*\n")) {
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
                return;
        }
 
-       filename = g_build_filename (EVOLUTION_TESTTOPSRCDIR, "data", "icons", 
"hicolor_actions_24x24_stock_people.png", NULL);
-       uri = g_filename_to_uri (filename, NULL, &error);
-       g_assert_no_error (error);
-
-       /* Mimic what the action:insert-image does, without invoking the image chooser dialog */
-       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
-       e_content_editor_insert_image (cnt_editor, uri);
-       /* Wait some time until the operation is finished */
-       test_utils_wait_milliseconds (500);
-
-       image_data_base64 = test_utils_get_base64_data_for_image (filename);
-
-       g_free (uri);
-       g_free (filename);
-
-       g_return_if_fail (image_data_base64 != NULL);
-
-       expected_html = g_strconcat (HTML_PREFIX "<div>before*<img src=\"data:image/png;base64,",
-               image_data_base64, "\">+after</div>" HTML_SUFFIX, NULL);
-
-       g_free (image_data_base64);
+       /* Changing mid item, single select */
 
        if (!test_utils_run_simple_test (fixture,
                "undo:save\n" /* 1 */
-               "undo:undo\n"
-               "undo:redo\n"
-               "undo:test:1\n"
-               "type:+after\n",
-               expected_html,
-               "before*+after"))
+               "seq:dh\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<pre>b</pre>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "b\n"
+               " * c\n"
+               " * d\n")) {
                g_test_fail ();
-
-       g_free (expected_html);
-}
-
-static void
-test_emoticon_insert_typed (TestFixture *fixture)
-{
-       gchar *image_data_base64;
-       gchar *expected_html;
-
-       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-magic-smileys", TRUE);
-       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-unicode-smileys", FALSE);
-
-       image_data_base64 = test_utils_get_base64_data_for_image ("face-smile");
-
-       expected_html = g_strconcat (HTML_PREFIX "<div>before <img src=\"data:image/png;base64,",
-               image_data_base64, "\" alt=\":-)\">after</div>" HTML_SUFFIX, NULL);
-
-       g_free (image_data_base64);
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n"
-               "type:before :)after\n",
-               expected_html,
-               "before :-)after"))
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ul><li>b</li></ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "    - b\n"
+               " * c\n"
+               " * d\n")) {
                g_test_fail ();
+               return;
+       }
 
-       g_free (expected_html);
-}
-
-static void
-test_emoticon_insert_typed_dash (TestFixture *fixture)
-{
-       gchar *image_data_base64;
-       gchar *expected_html;
-
-       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-magic-smileys", TRUE);
-       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-unicode-smileys", FALSE);
-
-       image_data_base64 = test_utils_get_base64_data_for_image ("face-smile");
-
-       expected_html = g_strconcat (HTML_PREFIX "<div>before <img src=\"data:image/png;base64,",
-               image_data_base64, "\" alt=\":-)\">after</div>" HTML_SUFFIX, NULL);
-
-       g_free (image_data_base64);
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n"
-               "type:before :-)after\n",
-               expected_html,
-               "before :-)after"))
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
+               return;
+       }
 
-       g_free (expected_html);
-}
+       /* Changing last item, single select */
 
-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 "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
-               "<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.")) {
+               "undo:save\n" /* 1 */
+               "seq:dd\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ul>"
+                       "<pre>d</pre>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "d\n")) {
                g_test_fail ();
                return;
        }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing 
elit. Integer nec odio. Praesent libero.</div>"
-               "<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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<ul><li>d</li></ul>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "    - d\n")) {
                g_test_fail ();
                return;
        }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n",
-               HTML_PREFIX "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
-               "<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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                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 "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
-               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.")) {
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
                return;
        }
 
+       /* Changing first items, multi-select */
+
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing 
elit. Integer nec odio. Praesent libero.</div>"
-               "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</div>" 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.")) {
+               "undo:save\n" /* 1 */
+               "seq:uuuhSdes\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>a</pre>"
+                       "<pre>b</pre>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "a\n"
+               "b\n"
+               " * c\n"
+               " * d\n")) {
                g_test_fail ();
                return;
        }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n",
-               HTML_PREFIX "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
-               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<ul><li>a</li>"
+                               "<li>b</li></ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "    - a\n"
+               "    - b\n"
+               " * c\n"
+               " * d\n")) {
                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>"
-               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
                return;
        }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n",
-               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</pre>"
-               "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</div>" 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.")) {
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
                return;
        }
 
+       /* Changing mid items, multi-select */
+
        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>"
-               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.")) {
+               "undo:save\n" /* 1 */
+               "seq:duhSdes\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<pre>b</pre>"
+                       "<pre>c</pre>"
+                       "<ul>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "b\n"
+               "c\n"
+               " * d\n")) {
                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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ul><li>b</li>"
+                               "<li>c</li></ul>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "    - b\n"
+               "    - c\n"
+               " * d\n")) {
                g_test_fail ();
                return;
        }
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\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.")) {
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                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.")) {
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
                g_test_fail ();
                return;
        }
-}
 
-static void
-test_paragraph_address_selection (TestFixture *fixture)
-{
+       /* Changing last items, multi-select */
+
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n"
+               "undo:save\n" /* 1 */
+               "seq:dhSues\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ul>"
+                       "<pre>c</pre>"
+                       "<pre>d</pre>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               "c\n"
+               "d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<ul><li>c</li>"
+                               "<li>d</li></ul>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               "    - c\n"
+               "    - d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* The same tests as above, only with added text above and below the list */
+
+       unindented_html =
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:select-all\n"
+               "seq:D\n"
+               "action:style-preformat\n"
+               "type:prefix\\n\n"
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "type:b\\n\n"
+               "type:c\\n\n"
+               "type:d\\n\n"
+               "seq:n\n"
+               "action:style-preformat\n"
+               "type:suffix\n"
+               "seq:u\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing all items */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuuhSdddes\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<pre>a</pre>"
+                       "<pre>b</pre>"
+                       "<pre>c</pre>"
+                       "<pre>d</pre>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "a\n"
+               "b\n"
+               "c\n"
+               "d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<ul>"
+                                       "<li>a</li>"
+                                       "<li>b</li>"
+                                       "<li>c</li>"
+                                       "<li>d</li>"
+                               "</ul>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "    - a\n"
+               "    - b\n"
+               "    - c\n"
+               "    - d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuu\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<pre>a</pre>"
+                       "<ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<ul><li>a</li></ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "    - a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:dh\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<pre>b</pre>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               "b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ul><li>b</li></ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               "    - b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:ddh\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ul>"
+                       "<pre>d</pre>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<ul><li>d</li></ul>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "    - d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuuhSdes\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<pre>a</pre>"
+                       "<pre>b</pre>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "a\n"
+               "b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<ul><li>a</li>"
+                               "<li>b</li></ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "    - a\n"
+               "    - b\n"
+               " * c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:duhSdes\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<pre>b</pre>"
+                       "<pre>c</pre>"
+                       "<ul>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               "b\n"
+               "c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ul><li>b</li>"
+                               "<li>c</li></ul>"
+                               "<li>d</li>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               "    - b\n"
+               "    - c\n"
+               " * d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n"
+               "undo:drop:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:deSuhs\n"
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ul>"
+                       "<pre>c</pre>"
+                       "<pre>d</pre>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               "c\n"
+               "d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:drop:1\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<ul><li>c</li>"
+                               "<li>d</li></ul>"
+                       "</ul>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               "    - c\n"
+               "    - d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:test:2\n"
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:test:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain))
+               g_test_fail ();
+}
+
+static void
+test_list_indent_same_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_same (fixture, TRUE);
+}
+
+static void
+test_list_indent_same_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_same (fixture, FALSE);
+}
+
+static void
+test_list_indent_different (TestFixture *fixture,
+                           gboolean is_html)
+{
+       const gchar *unindented_html, *unindented_plain;
+
+       unindented_html =
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "type:b\\n\n"
+               "type:c\\n\n"
+               "type:d\\n\n"
+               "seq:nb\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuu\n"
+               "action:indent\n"
+               "action:style-list-number\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<ol><li>a</li></ol>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "      1. a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ol><li>a</li></ol>"
+                       "<ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "   1. a\n"
+               " * b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:d\n"
+               "action:indent\n"
+               "action:style-list-alpha\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ol type=\"A\"><li>b</li></ol>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "      A. b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<ol type=\"A\"><li>b</li></ol>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "   A. b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:dd\n"
+               "action:indent\n"
+               "action:style-list-roman\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<ol type=\"I\"><li>d</li></ol>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "      I. d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ul>"
+                       "<ol type=\"I\"><li>d</li></ol>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "   I. d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuuhSdes\n"
+               "action:indent\n"
+               "action:style-list-number\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<ol>"
+                                       "<li>a</li>"
+                                       "<li>b</li>"
+                               "</ol>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "      1. a\n"
+               "      2. b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ol>"
+                       "<ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               "   1. a\n"
+               "   2. b\n"
+               " * c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:duhSdes\n"
+               "action:indent\n"
+               "action:style-list-alpha\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ol type=\"A\">"
+                                       "<li>b</li>"
+                                       "<li>c</li>"
+                               "</ol>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "      A. b\n"
+               "      B. c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                       "</ul>"
+                       "<ol type=\"A\">"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ol>"
+                       "<ul>"
+                               "<li>d</li>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               "   A. b\n"
+               "   B. c\n"
+               " * d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:deSuhs\n"
+               "action:indent\n"
+               "action:style-list-roman\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<ol type=\"I\">"
+                                       "<li>c</li>"
+                                       "<li>d</li>"
+                               "</ol>"
+                       "</ul>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               "      I. c\n"
+               "     II. d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ul>"
+                       "<ol type=\"I\">"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+               HTML_SUFFIX,
+               " * a\n"
+               " * b\n"
+               "   I. c\n"
+               "  II. d\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* The same tests as above, only with added text above and below the list */
+
+       unindented_html =
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               "prefix\n"
+               "   1. a\n"
+               "   2. b\n"
+               "   3. c\n"
+               "   4. d\n"
+               "suffix\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:select-all\n"
+               "seq:D\n"
+               "action:style-preformat\n"
+               "type:prefix\\n\n"
+               "action:style-list-number\n"
+               "type:a\\n\n"
+               "type:b\\n\n"
+               "type:c\\n\n"
+               "type:d\\n\n"
+               "seq:n\n"
+               "action:style-preformat\n"
+               "type:suffix\n"
+               "seq:ur\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuu\n"
+               "action:indent\n"
+               "action:style-list-bullet\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<ul><li>a</li></ul>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "       - a\n"
+               "   1. b\n"
+               "   2. c\n"
+               "   3. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul><li>a</li></ul>"
+                       "<ol>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               "   1. b\n"
+               "   2. c\n"
+               "   3. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:d\n"
+               "action:indent\n"
+               "action:style-list-alpha\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<ol type=\"A\"><li>b</li></ol>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "         A. b\n"
+               "   2. c\n"
+               "   3. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                       "</ol>"
+                       "<ol type=\"A\"><li>b</li></ol>"
+                       "<ol>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   A. b\n"
+               "   1. c\n"
+               "   2. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last item, single select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:dd\n"
+               "action:indent\n"
+               "action:style-list-roman\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                               "<ol type=\"I\"><li>d</li></ol>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   2. b\n"
+               "   3. c\n"
+               "         I. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ol>"
+                       "<ol type=\"I\"><li>d</li></ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   2. b\n"
+               "   3. c\n"
+               "   I. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing first items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:uuuhSdes\n"
+               "action:indent\n"
+               "action:style-list-bullet\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<ul>"
+                                       "<li>a</li>"
+                                       "<li>b</li>"
+                               "</ul>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "       - a\n"
+               "       - b\n"
+               "   1. c\n"
+               "   2. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ul>"
+                       "<ol>"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               " * a\n"
+               " * b\n"
+               "   1. c\n"
+               "   2. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing mid items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:duhSdes\n"
+               "action:indent\n"
+               "action:style-list-alpha\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<ol type=\"A\">"
+                                       "<li>b</li>"
+                                       "<li>c</li>"
+                               "</ol>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "         A. b\n"
+               "         B. c\n"
+               "   2. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                       "</ol>"
+                       "<ol type=\"A\">"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ol>"
+                       "<ol>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   A. b\n"
+               "   B. c\n"
+               "   1. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       /* Changing last items, multi-select */
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "seq:deSuhs\n"
+               "action:indent\n"
+               "action:style-list-roman\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<ol type=\"I\">"
+                                       "<li>c</li>"
+                                       "<li>d</li>"
+                               "</ol>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   2. b\n"
+               "         I. c\n"
+               "        II. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:2\n"
+               "undo:test:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>prefix</pre>"
+                       "<ol>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                       "</ol>"
+                       "<ol type=\"I\">"
+                               "<li>c</li>"
+                               "<li>d</li>"
+                       "</ol>"
+                       "<pre>suffix</pre>"
+               HTML_SUFFIX,
+               "prefix\n"
+               "   1. a\n"
+               "   2. b\n"
+               "   I. c\n"
+               "  II. d\n"
+               "suffix\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:3\n"
+               "undo:undo:3\n",
+               !is_html ? NULL : unindented_html, unindented_plain))
+               g_test_fail ();
+}
+
+static void
+test_list_indent_different_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_different (fixture, TRUE);
+}
+
+static void
+test_list_indent_different_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_different (fixture, FALSE);
+}
+
+static void
+test_list_indent_multi (TestFixture *fixture,
+                       gboolean is_html)
+{
+       const gchar *unindented_html, *unindented_plain;
+
+       unindented_html =
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<pre>line 2</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ul>"
+                       "<pre>line 3</pre>"
+                       "<ol>"
+                               "<li>1</li>"
+                               "<li>2</li>"
+                       "</ol>"
+                       "<pre>line 4</pre>"
+                       "<ol type=\"A\">"
+                               "<li>A</li>"
+                               "<li>B</li>"
+                       "</ol>"
+                       "<pre>line 5</pre>"
+                       "<ol type=\"I\">"
+                               "<li>i</li>"
+                               "<li>ii</li>"
+                       "</ol>"
+                       "<pre>line 6</pre>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               "line 1\n"
+               "line 2\n"
+               " * a\n"
+               " * b\n"
+               " * c\n"
+               "line 3\n"
+               "   1. 1\n"
+               "   2. 2\n"
+               "line 4\n"
+               "   A. A\n"
+               "   B. B\n"
+               "line 5\n"
+               "   I. i\n"
+               "  II. ii\n"
+               "line 6\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:style-preformat\n"
+               "type:line 1\\n\n"
+               "type:line 2\\n\n"
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "type:b\\n\n"
+               "type:c\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 3\\n\n"
+               "action:style-list-number\n"
+               "type:1\\n\n"
+               "type:2\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 4\\n\n"
+               "action:style-list-alpha\n"
+               "type:A\\n\n"
+               "type:B\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 5\\n\n"
+               "action:style-list-roman\n"
+               "type:i\\n\n"
+               "type:ii\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 6\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:select-all\n"
+               "undo:save\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre style=\"margin-left: 3ch;\">line 1</pre>"
+                       "<pre style=\"margin-left: 3ch;\">line 2</pre>"
+                       "<ul><ul>"
+                               "<li>a</li>"
+                               "<li>b</li>"
+                               "<li>c</li>"
+                       "</ul></ul>"
+                       "<pre style=\"margin-left: 3ch;\">line 3</pre>"
+                       "<ol><ol>"
+                               "<li>1</li>"
+                               "<li>2</li>"
+                       "</ol></ol>"
+                       "<pre style=\"margin-left: 3ch;\">line 4</pre>"
+                       "<ol type=\"A\"><ol type=\"A\">"
+                               "<li>A</li>"
+                               "<li>B</li>"
+                       "</ol></ol>"
+                       "<pre style=\"margin-left: 3ch;\">line 5</pre>"
+                       "<ol type=\"I\"><ol type=\"I\">"
+                               "<li>i</li>"
+                               "<li>ii</li>"
+                       "</ol></ol>"
+                       "<pre style=\"margin-left: 3ch;\">line 6</pre>"
+               HTML_SUFFIX,
+               "   line 1\n"
+               "   line 2\n"
+               "    - a\n"
+               "    - b\n"
+               "    - c\n"
+               "   line 3\n"
+               "         1. 1\n"
+               "         2. 2\n"
+               "   line 4\n"
+               "         A. A\n"
+               "         B. B\n"
+               "   line 5\n"
+               "         I. i\n"
+               "        II. ii\n"
+               "   line 6\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo\n"
+               "undo:test:1\n"
+               "undo:redo\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:Shur\n" /* To be able to unindent the selection should end inside the list */
+               "action:unindent\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<pre>line 2</pre>"
+                       "<pre>a</pre>"
+                       "<pre>b</pre>"
+                       "<pre>c</pre>"
+                       "<pre>line 3</pre>"
+                       "<pre>1</pre>"
+                       "<pre>2</pre>"
+                       "<pre>line 4</pre>"
+                       "<pre>A</pre>"
+                       "<pre>B</pre>"
+                       "<pre>line 5</pre>"
+                       "<pre>i</pre>"
+                       "<pre>ii</pre>"
+                       "<pre>line 6</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "line 2\n"
+               "a\n"
+               "b\n"
+               "c\n"
+               "line 3\n"
+               "1\n"
+               "2\n"
+               "line 4\n"
+               "A\n"
+               "B\n"
+               "line 5\n"
+               "i\n"
+               "ii\n"
+               "line 6\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 3 */
+               "undo:undo:2\n"
+               "undo:redo:2\n"
+               "undo:test\n"
+               "undo:undo:2\n",
+               !is_html ? NULL : unindented_html, unindented_plain))
+               g_test_fail ();
+}
+
+static void
+test_list_indent_multi_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_multi (fixture, TRUE);
+}
+
+static void
+test_list_indent_multi_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_multi (fixture, FALSE);
+}
+
+static void
+test_list_indent_nested (TestFixture *fixture,
+                        gboolean is_html)
+{
+       const gchar *unindented_html, *unindented_plain;
+
+       unindented_html =
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<ul>"
+                               "<li>a</li>"
+                               "<ol>"
+                                       "<li>123456789 123456789 123456789 123456789 123456789 123456789 
123456789 123456789</li>"
+                                       "<li>2</li>"
+                                               "<ul>"
+                                                       "<li>x</li>"
+                                                       "<li>y</li>"
+                                               "</ul>"
+                                       "<li>3</li>"
+                               "</ol>"
+                               "<li>b</li>"
+                       "</ul>"
+                       "<pre>line 2</pre>"
+                       "<ol>"
+                               "<li>1</li>"
+                                       "<ul>"
+                                               "<li>a</li>"
+                                               "<ol type=\"A\">"
+                                                       "<li>A</li>"
+                                                       "<li>B</li>"
+                                               "</ol>"
+                                               "<li>b</li>"
+                                       "</ul>"
+                               "<li>2</li>"
+                       "</ol>"
+                       "<pre>line 3</pre>"
+               HTML_SUFFIX;
+
+       unindented_plain =
+               "line 1\n"
+               " * a\n"
+               "      1. 123456789 123456789 123456789 123456789 123456789 123456789 123456789\n"
+               "         123456789\n"
+               "      2. 2\n"
+               "          + x\n"
+               "          + y\n"
+               "      3. 3\n"
+               " * b\n"
+               "line 2\n"
+               "   1. 1\n"
+               "       - a\n"
+               "            A. A\n"
+               "            B. B\n"
+               "       - b\n"
+               "   2. 2\n"
+               "line 3\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:style-preformat\n"
+               "type:line 1\\n\n"
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "action:indent\n"
+               "action:style-list-number\n"
+               "type:123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789\\n\n"
+               "type:2\\n\n"
+               "action:indent\n"
+               "action:style-list-bullet\n"
+               "type:x\\n\n"
+               "type:y\\n\\n\n"
+               "type:3\\n\\n\n"
+               "type:b\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 2\\n\n"
+               "action:style-list-number\n"
+               "type:1\\n\n"
+               "action:indent\n"
+               "action:style-list-bullet\n"
+               "type:a\\n\n"
+               "action:indent\n"
+               "action:style-list-alpha\n"
+               "type:A\\n\n"
+               "type:B\\n\\n\n"
+               "type:b\\n\\n\n"
+               "type:2\\n\\n\n"
+               "action:style-preformat\n"
+               "type:line 3\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:hurSChcds\n" /* selects all but the "line 1" and "line 3" */
+               "undo:save\n" /* 1 */
+               "action:indent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<ul><ul>"
+                               "<li>a</li>"
+                               "<ol>"
+                                       "<li>123456789 123456789 123456789 123456789 123456789 123456789 
123456789 123456789</li>"
+                                       "<li>2</li>"
+                                               "<ul>"
+                                                       "<li>x</li>"
+                                                       "<li>y</li>"
+                                               "</ul>"
+                                       "<li>3</li>"
+                               "</ol>"
+                               "<li>b</li>"
+                       "</ul></ul>"
+                       "<pre style=\"margin-left: 3ch;\">line 2</pre>"
+                       "<ol><ol>"
+                               "<li>1</li>"
+                                       "<ul>"
+                                               "<li>a</li>"
+                                               "<ol type=\"A\">"
+                                                       "<li>A</li>"
+                                                       "<li>B</li>"
+                                               "</ol>"
+                                               "<li>b</li>"
+                                       "</ul>"
+                               "<li>2</li>"
+                       "</ol></ol>"
+                       "<pre>line 3</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "    - a\n"
+               "         1. 123456789 123456789 123456789 123456789 123456789 123456789 123456789\n"
+               "            123456789\n"
+               "         2. 2\n"
+               "             * x\n"
+               "             * y\n"
+               "         3. 3\n"
+               "    - b\n"
+               "   line 2\n"
+               "         1. 1\n"
+               "             + a\n"
+               "                  A. A\n"
+               "                  B. B\n"
+               "             + b\n"
+               "         2. 2\n"
+               "line 3\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* 0 */
+               "undo:save\n" /* 1 */
+               "action:unindent\n",
+               !is_html ? NULL : unindented_html, unindented_plain)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* 0 */
+               "undo:save\n" /* 1 */
+               "action:unindent\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<div>a</div>"
+                       "<ol>"
+                               "<li>123456789 123456789 123456789 123456789 123456789 123456789 123456789 
123456789</li>"
+                               "<li>2</li>"
+                                       "<ul>"
+                                               "<li>x</li>"
+                                               "<li>y</li>"
+                                       "</ul>"
+                               "<li>3</li>"
+                       "</ol>"
+                       "<div>b</div>"
+                       "<pre>line 2</pre>"
+                       "<div>1</div>"
+                               "<ul>"
+                                       "<li>a</li>"
+                                       "<ol type=\"A\">"
+                                               "<li>A</li>"
+                                               "<li>B</li>"
+                                       "</ol>"
+                                       "<li>b</li>"
+                               "</ul>"
+                       "<div>2</div>"
+                       "<pre>line 3</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "a\n"
+               "   1. 123456789 123456789 123456789 123456789 123456789 123456789 123456789\n"
+               "      123456789\n"
+               "   2. 2\n"
+               "       - x\n"
+               "       - y\n"
+               "   3. 3\n"
+               "b\n"
+               "line 2\n"
+               "1\n"
+               " * a\n"
+               "      A. A\n"
+               "      B. B\n"
+               " * b\n"
+               "2\n"
+               "line 3\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo\n"
+               "undo:test:2\n"
+               "undo:redo\n"
+               "undo:test\n"
+               "undo:drop:2\n" /* 0 */
+               "undo:save\n" /* 1 */
+               "seq:CecuueSChcdds\n"
+               "action:unindent\n"
+               "seq:CecuuueSChcddds\n"
+               "action:unindent\n"
+               "action:select-all\n"
+               "action:style-normal\n"
+               "action:style-preformat\n",
+               !is_html ? NULL :
+               HTML_PREFIX
+                       "<pre>line 1</pre>"
+                       "<pre>a</pre>"
+                       "<pre>123456789 123456789 123456789 123456789 123456789 123456789 123456789 
123456789</pre>"
+                       "<pre>2</pre>"
+                       "<pre>x</pre>"
+                       "<pre>y</pre>"
+                       "<pre>3</pre>"
+                       "<pre>b</pre>"
+                       "<pre>line 2</pre>"
+                       "<pre>1</pre>"
+                       "<pre>a</pre>"
+                       "<pre>A</pre>"
+                       "<pre>B</pre>"
+                       "<pre>b</pre>"
+                       "<pre>2</pre>"
+                       "<pre>line 3</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "a\n"
+               "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789\n"
+               "2\n"
+               "x\n"
+               "y\n"
+               "3\n"
+               "b\n"
+               "line 2\n"
+               "1\n"
+               "a\n"
+               "A\n"
+               "B\n"
+               "b\n"
+               "2\n"
+               "line 3\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_process_commands (fixture,
+               "undo:save\n" /* 2 */
+               "undo:undo:4\n"
+               "undo:test:2\n"
+               "undo:redo:4\n"
+               "undo:test\n"))
+               g_test_fail ();
+}
+
+static void
+test_list_indent_nested_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_nested (fixture, TRUE);
+}
+
+static void
+test_list_indent_nested_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture, "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_list_indent_nested (fixture, FALSE);
+}
+
+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 "<div>a link example: <a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></div>" HTML_SUFFIX,
+               "a link example: http://www.gnome.org\n";))
+               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 "<div>a link example: <a href=\"http://www.gnome.org\";>GNOME</a></div>" 
HTML_SUFFIX,
+               "a link example: GNOME\n"))
+               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 "<div><a href=\"https://www.gnome.org\";>www.gnome.org</a> </div>" HTML_SUFFIX,
+               "www.gnome.org \n"))
+               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:A\n" /* Alt+D to jump to the Description */
+               "type:D\n"
+               "seq:a\n"
+               "type:GNOME\n"
+               "seq:n\n",
+               HTML_PREFIX "<div><a href=\"https://www.gnome.org\";>GNOME</a> </div>" HTML_SUFFIX,
+               "GNOME \n"))
+               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:A\n" /* Alt+R to press the 'Remove Link' */
+               "type:R\n"
+               "seq:a\n",
+               HTML_PREFIX "<div>www.gnome.org </div>" HTML_SUFFIX,
+               "www.gnome.org \n"))
+               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 "<div><a href=\"https://www.gnome.org\";>www.gnome.org/about</a> </div>" 
HTML_SUFFIX,
+               "www.gnome.org/about \n"))
+               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 "<div><a href=\"https://www.gnome.org\";>www.gnome.o</a></div>" HTML_SUFFIX,
+               "www.gnome.o\n"))
+               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:^\n", /* Escape key press to close the dialog */
+               HTML_PREFIX "<div>text</div><hr align=\"center\">" HTML_SUFFIX,
+               "text\n\n"))
+               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:^\n" /* Escape key press to close the dialog */
+               "seq:drn\n" /* Press the right key instead of End key as the End key won't move caret after 
the HR element */
+               "type:below\n",
+               HTML_PREFIX "<div>above</div><hr align=\"center\"><div>below</div>" HTML_SUFFIX,
+               "above\n"
+               "\n"
+               "below\n"))
+               g_test_fail ();
+}
+
+static void
+test_image_insert (TestFixture *fixture)
+{
+       EContentEditor *cnt_editor;
+       gchar *expected_html;
+       gchar *filename;
+       gchar *uri;
+       GError *error = NULL;
+
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n"
+               "type:before*\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       filename = g_build_filename (EVOLUTION_TESTTOPSRCDIR, "data", "icons", 
"hicolor_actions_24x24_stock_people.png", NULL);
+       uri = g_filename_to_uri (filename, NULL, &error);
+       g_assert_no_error (error);
+
+       /* Mimic what the action:insert-image does, without invoking the image chooser dialog */
+       cnt_editor = e_html_editor_get_content_editor (fixture->editor);
+       e_content_editor_insert_image (cnt_editor, uri);
+       /* Wait some time until the operation is finished */
+       test_utils_wait_milliseconds (500);
+
+       expected_html = g_strconcat (HTML_PREFIX "<div>before*<img src=\"evo-", uri, "\" width=\"24px\" 
height=\"24px\">+after</div>" HTML_SUFFIX, NULL);
+
+       g_free (uri);
+       g_free (filename);
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:save\n" /* 1 */
+               "undo:undo\n"
+               "undo:redo\n"
+               "undo:test:1\n"
+               "type:+after\n",
+               expected_html,
+               "before*+after\n"))
+               g_test_fail ();
+
+       g_free (expected_html);
+}
+
+static void
+test_emoticon_insert_typed (TestFixture *fixture)
+{
+       gchar *image_uri;
+       gchar *expected_html;
+
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-magic-smileys", TRUE);
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-unicode-smileys", FALSE);
+
+       image_uri = test_utils_dup_image_uri ("face-smile");
+
+       expected_html = g_strconcat (HTML_PREFIX "<div>before <img src=\"", image_uri, "\" alt=\":-)\" 
width=\"16px\" height=\"16px\">after</div>" HTML_SUFFIX, NULL);
+
+       g_free (image_uri);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:before :)after\n",
+               expected_html,
+               "before :-)after\n"))
+               g_test_fail ();
+
+       g_free (expected_html);
+}
+
+static void
+test_emoticon_insert_typed_dash (TestFixture *fixture)
+{
+       gchar *image_uri;
+       gchar *expected_html;
+
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-magic-smileys", TRUE);
+       test_utils_fixture_change_setting_boolean (fixture, "org.gnome.evolution.mail", 
"composer-unicode-smileys", FALSE);
+
+       image_uri = test_utils_dup_image_uri ("face-smile");
+
+       expected_html = g_strconcat (HTML_PREFIX "<div>before <img src=\"", image_uri, "\" alt=\":-)\" 
width=\"16px\" height=\"16px\">after</div>" HTML_SUFFIX, NULL);
+
+       g_free (image_uri);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:before :-)after\n",
+               expected_html,
+               "before :-)after\n"))
+               g_test_fail ();
+
+       g_free (expected_html);
+}
+
+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 "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
+               "<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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing 
elit. Integer nec odio. Praesent libero.</div>"
+               "<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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
+               "<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.\n")) {
+               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 "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
+               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing 
elit. Integer nec odio. Praesent libero.</div>"
+               "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</div>" 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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n",
+               HTML_PREFIX "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</div>"
+               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.\n")) {
+               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>"
+               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               HTML_PREFIX "<pre>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. 
Praesent libero.</pre>"
+               "<div style=\"width: 71ch;\">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec odio. Praesent libero.</div>" 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.\n")) {
+               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>"
+               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent 
libero.</div>" 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.\n")) {
+               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.\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\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.\n")) {
+               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.\n")) {
+               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"
@@ -1236,7 +3926,7 @@ test_paragraph_address_selection (TestFixture *fixture)
                "address line 2\n"
                "address line 3\n"
                "\n"
-               "normal text"))
+               "normal text\n"))
                g_test_fail ();
 }
 
@@ -1265,7 +3955,7 @@ test_paragraph_address_typed (TestFixture *fixture)
                "address line 2\n"
                "address line 3\n"
                "\n"
-               "normal text"))
+               "normal text\n"))
                g_test_fail ();
 }
 
@@ -1295,7 +3985,7 @@ test_paragraph_header_n_selection (TestFixture *fixture,
        expected_plain = g_strdup_printf (
                "normal text\n"
                "header %d\n"
-               "normal text",
+               "normal text\n",
                header_n);
 
        success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain);
@@ -1318,7 +4008,7 @@ test_paragraph_header_n_selection (TestFixture *fixture,
                "normal text\n"
                "\n"
                "header %d\n"
-               "normal text",
+               "normal text\n",
                header_n);
 
        success = test_utils_run_simple_test (fixture,
@@ -1358,7 +4048,7 @@ test_paragraph_header_n_typed (TestFixture *fixture,
        expected_plain = g_strdup_printf (
                "normal text\n"
                "header %d\n"
-               "normal text",
+               "normal text\n",
                header_n);
 
        success = test_utils_run_simple_test (fixture, actions, expected_html, expected_plain);
@@ -1381,7 +4071,7 @@ test_paragraph_header_n_typed (TestFixture *fixture,
                "normal text\n"
                "header %d\n"
                "\n"
-               "normal text",
+               "normal text\n",
                header_n);
 
        success = test_utils_run_simple_test (fixture,
@@ -1488,10 +4178,12 @@ test_paragraph_wrap_lines (TestFixture *fixture)
                "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 "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer 
nec<br>odio. Praesent libero.</div>"
-               "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>odio. Praesent 
libero.</div>" 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."))
+               HTML_PREFIX "<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec<br>"
+               "odio. Praesent libero. Lorem ipsum dolor sit amet, consectetur<br>"
+               "adipiscing elit. Integer nec odio. Praesent libero.</div>" HTML_SUFFIX,
+               "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec\n"
+               "odio. Praesent libero. Lorem ipsum dolor sit amet, consectetur\n"
+               "adipiscing elit. Integer nec odio. Praesent libero.\n"))
                g_test_fail ();
 }
 
@@ -1506,391 +4198,775 @@ test_paste_singleline_html2html (TestFixture *fixture)
                "action:paste\n"
                "type: text after\n",
                HTML_PREFIX "<div>text before some <b>bold</b> text text after</div>" HTML_SUFFIX,
-               "text before some bold text text after"))
+               "text before some bold text text after\n"))
+               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 "<div style=\"width: 71ch;\">text before some bold text text after</div>" 
HTML_SUFFIX,
+               "text before some bold text text after\n"))
+               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 "<div>text before some plain text text after</div>" HTML_SUFFIX,
+               "text before some plain text text after\n"))
+               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 "<div style=\"width: 71ch;\">text before some plain text text after</div>" 
HTML_SUFFIX,
+               "text before some plain text text after\n"))
+               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 "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text<br><u>underline</u> text<br>.text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\n.text after\n"))
+               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 "<div style=\"width: 71ch;\">text before bold text</div>"
+               "<div style=\"width: 71ch;\">italic text<br>underline text</div>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after\n"))
+               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 "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div>.text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\n.text after\n"))
+               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 "<div style=\"width: 71ch;\">text before bold text</div>"
+               "<div style=\"width: 71ch;\">italic text<br>underline text<br></div>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after\n"))
+               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><br></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 "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div>text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after\n"))
+               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 "<div style=\"width: 71ch;\">text before bold text</div>"
+               "<div style=\"width: 71ch;\">italic text<br>underline text<br></div>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before bold text\nitalic text\nunderline text\ntext after\n"))
+               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 "<div>text before line 1</div><div>line 2</div><div>line 3</div><div>text 
after</div>" HTML_SUFFIX,
+               "text before line 1\nline 2\nline 3\ntext after\n"))
+               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 "<div style=\"width: 71ch;\">text before line 1</div>"
+               "<div style=\"width: 71ch;\">line 2</div>"
+               "<div style=\"width: 71ch;\">line 3</div>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before line 1\nline 2\nline 3\ntext after\n"))
+               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 "<div>text before </div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>some <b>bold</b> text</div></blockquote>"
+               "<div>text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> some bold text\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_singleline_html2plain (TestFixture *fixture)
+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\n"
-               "type: text after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before some bold text text after</div>" 
HTML_SUFFIX,
-               "text before some bold text text after"))
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
+               "<blockquote type=\"cite\">"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "some bold text</div>"
+               "</blockquote>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> some bold text\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_singleline_plain2html (TestFixture *fixture)
+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\n"
-               "type: text after\n",
-               HTML_PREFIX "<div>text before some plain text text after</div>" HTML_SUFFIX,
-               "text before some plain text text after"))
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<div>text before </div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>some plain text</div></blockquote>"
+               "<div>text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> some plain text\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_singleline_plain2plain (TestFixture *fixture)
+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\n"
-               "type: text after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before some plain text text after</div>" 
HTML_SUFFIX,
-               "text before some plain text text after"))
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
+               "<blockquote type=\"cite\"><div>" QUOTE_SPAN (QUOTE_CHR) "some plain text</div></blockquote>"
+               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> some plain text\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_multiline_html2html (TestFixture *fixture)
+test_paste_quoted_multiline_html2html (TestFixture *fixture)
 {
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
-
-#if 0
        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"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
                "type:text after\n",
-               HTML_PREFIX "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div>text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               HTML_PREFIX "<div>text before </div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div><b>bold</b> text<br>"
+               "<i>italic</i> text<br>"
+               "<u>underline</u> text<br></div></blockquote>"
+               "<div>text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> bold text\n"
+               "> italic text\n"
+               "> underline text\n"
+               "text after\n"))
                g_test_fail ();
-#endif
 }
 
 static void
-test_paste_multiline_html2plain (TestFixture *fixture)
+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\n"
-               "type:\\ntext after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before bold text</div>"
-               "<div style=\"width: 71ch;\">italic text</div>"
-               "<div style=\"width: 71ch;\">underline text</div>"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
+               "<blockquote type=\"cite\"><div>" QUOTE_SPAN (QUOTE_CHR) "bold text<br>"
+               QUOTE_SPAN (QUOTE_CHR) "italic text<br>"
+               QUOTE_SPAN (QUOTE_CHR) "underline text</div></blockquote>"
                "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               "text before \n"
+               "> bold text\n"
+               "> italic text\n"
+               "> underline text\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_multiline_div_html2html (TestFixture *fixture)
+test_paste_quoted_multiline_plain2html (TestFixture *fixture)
 {
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
-
-#if 0
-       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);
+       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"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
                "type:text after\n",
-               HTML_PREFIX "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div>text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               HTML_PREFIX "<div>text before </div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>line 1</div>"
+               "<div>line 2</div>"
+               "<div>line 3</div>"
+               "<div><br></div></blockquote>"
+               "<div>text after</div>" HTML_SUFFIX,
+               "text before \n"
+               "> line 1\n"
+               "> line 2\n"
+               "> line 3\n"
+               "> \n"
+               "text after\n"))
                g_test_fail ();
-#endif
 }
 
 static void
-test_paste_multiline_div_html2plain (TestFixture *fixture)
+test_paste_quoted_multiline_plain2plain (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);
+       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 "<div style=\"width: 71ch;\">text before bold text</div>"
-               "<div style=\"width: 71ch;\">italic text</div>"
-               "<div style=\"width: 71ch;\">underline text</div>"
+               "action:paste-quote\n"
+               "type:\\n\n" /* stop quotting */
+               "type:text after\n",
+               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
+               "<blockquote type=\"cite\"><div>" QUOTE_SPAN (QUOTE_CHR) "line 1</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 3</div></blockquote>"
                "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               "text before \n"
+               "> line 1\n"
+               "> line 2\n"
+               "> line 3\n"
+               "text after\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_multiline_p_html2html (TestFixture *fixture)
+test_cite_html2plain (TestFixture *fixture)
 {
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
 
-#if 0
-       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);
+       test_utils_insert_content (fixture,
+               "<html><head></head><body>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>level 1</div>"
+               "<div><br></div>"
+               "<div>level 1</div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>level 2</div>"
+               "</blockquote>"
+               "<div>back in level 1</div>"
+               "</blockquote>"
+               "<div><br></div>"
+               "<div>out of the citation</div>"
+               "</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,
-               "mode:html\n"
-               "type:text before \n"
-               "action:paste\n"
-               "type:text after\n",
-               HTML_PREFIX "<div>text before <b>bold</b> text</div><div><i>italic</i> 
text</div><div><u>underline</u> text</div><div>text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>level 
1</div><div><br></div><div>level 1</div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>level 2</div></blockquote><div>back in 
level 1</div></blockquote>"
+               "<div><br></div><div>out of the citation</div>" HTML_SUFFIX,
+               "> level 1\n"
+               "> \n"
+               "> level 1\n"
+               "> > level 2\n"
+               "> back in level 1\n"
+               "\n"
+               "out of the citation\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n",
+               NULL,
+               "> level 1\n"
+               "> \n"
+               "> level 1\n"
+               "> > level 2\n"
+               "> back in level 1\n"
+               "\n"
+               "out of the citation\n"))
                g_test_fail ();
-#endif
 }
 
 static void
-test_paste_multiline_p_html2plain (TestFixture *fixture)
+test_cite_shortline (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_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>Just one short line.</div>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n"
-               "type:text before \n"
-               "action:paste\n"
-               "type:\\ntext after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before bold text</div>"
-               "<div style=\"width: 71ch;\">italic text</div>"
-               "<div style=\"width: 71ch;\">underline text</div>"
-               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before bold text\nitalic text\nunderline text\ntext after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>Just one short line.</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> Just one short line.\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-static void
-test_paste_multiline_plain2html (TestFixture *fixture)
-{
-       test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE);
+       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\" " BLOCKQUOTE_STYLE ">"
+               "<div>Just one short line.</div>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:html\n"
-               "type:text before \n"
-               "action:paste\n"
-               "type:text after\n",
-               HTML_PREFIX "<div>text before line 1</div><div>line 2</div><div>line 3</div><div>text 
after</div>" HTML_SUFFIX,
-               "text before line 1\nline 2\nline 3\ntext after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>Just one short line.</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> Just one short line.\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-static void
-test_paste_multiline_plain2plain (TestFixture *fixture)
-{
-       test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE);
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>short line 1</div>"
+               "<div>short line 2</div>"
+               "<div>short line 3</div>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n"
-               "type:text before \n"
-               "action:paste\n"
-               "type:\\ntext after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before line 1</div>"
-               "<div style=\"width: 71ch;\">line 2</div>"
-               "<div style=\"width: 71ch;\">line 3</div>"
-               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before line 1\nline 2\nline 3\ntext after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>short line 1</div>"
+               "<div>short line 2</div>"
+               "<div>short line 3</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> short line 1\n"
+               "> short line 2\n"
+               "> short line 3\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\" " BLOCKQUOTE_STYLE ">"
+               "<div>short line 1</div>"
+               "<div>short line 2</div>"
+               "<div>short line 3</div>"
+               "</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\" " BLOCKQUOTE_STYLE ">"
+               "<div>short line 1</div>"
+               "<div>short line 2</div>"
+               "<div>short line 3</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> short line 1\n"
+               "> short line 2\n"
+               "> short line 3\n")) {
                g_test_fail ();
+               return;
+       }
 }
 
 static void
-test_paste_quoted_singleline_html2html (TestFixture *fixture)
+test_cite_longline (TestFixture *fixture)
 {
-       test_utils_set_clipboard_text ("<html><body>some <b>bold</b> text</body></html>", TRUE);
+       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\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        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 "<div>text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>some <b>bold</b> text</div></blockquote>"
-               "<div>text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> some bold text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long text\n"
+               "> to test. It has the second sentence as well.\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-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_process_commands (fixture,
+               "seq:C\n"
+               "type:a\n"
+               "seq:cDb\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</blockquote></body></html>",
+               E_CONTENT_EDITOR_INSERT_TEXT_HTML | E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
 
        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 "<div style=\"width: 71ch;\">text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div style=\"width: 71ch;\">&gt; some 
<b>bold</b> text</div></blockquote>"
-               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> some bold text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long text\n"
+               "> to test. It has the second sentence as well.\n")) {
                g_test_fail ();
-}
+               return;
+       }
 
-static void
-test_paste_quoted_singleline_plain2html (TestFixture *fixture)
-{
-       test_utils_set_clipboard_text ("some plain text", FALSE);
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "<div>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "<div>This is the third paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</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,
-               "mode:html\n"
-               "type:text before \n"
-               "action:paste-quote\n"
-               "type:\\n\n" /* stop quotting */
-               "type:text after\n",
-               HTML_PREFIX "<div>text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>some plain text</div></blockquote>"
-               "<div>text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> some plain text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "<div>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "<div>This is the third paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
+               "</blockquote><br>after quote" HTML_SUFFIX,
+               "> This is the first paragraph of a quoted text which has some long text\n"
+               "> to test. It has the second sentence as well.\n"
+               "> This is the second 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 text\n"
+               "> to test. It has the second sentence as well.\n"
+               "\nafter quote")) {
                g_test_fail ();
+               return;
+       }
 }
 
 static void
-test_paste_quoted_singleline_plain2plain (TestFixture *fixture)
+test_cite_reply_html (TestFixture *fixture)
 {
-       test_utils_set_clipboard_text ("some plain text", FALSE);
+       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,
-               "mode:plain\n"
-               "type:text before \n"
-               "action:paste-quote\n"
-               "type:\\n\n" /* stop quotting */
-               "type:text after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div style=\"width: 71ch;\">&gt; some plain 
text</div></blockquote>"
-               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> some plain text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<div>On Today, User wrote:</div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><pre>line 1</pre>"
+               "<pre>line 2</pre></blockquote>" HTML_SUFFIX,
+               "On Today, User wrote:\n"
+               "> line 1\n"
+               "> line 2\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_quoted_multiline_html2html (TestFixture *fixture)
+test_cite_reply_html_to_plain (TestFixture *fixture)
 {
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
 
-#if 0
-       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);
+       test_utils_insert_content (fixture,
+               "<pre>line 1\n"
+               "line 2\n\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_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
 
        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 "<div>text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">&gt; <b>bold</b> text</div>"
-               "<div>&gt; <i>italic</i> text</div>"
-               "<div>&gt; <u>underline</u> text</div></blockquote>"
-               "<div>text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> bold text\n"
-               "> italic text\n"
-               "> underline text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "line 1</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "line 2</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "<br></pre></blockquote>" HTML_SUFFIX,
+               "On Today, User wrote:\n"
+               "> line 1\n"
+               "> line 2\n"
+               "> \n")) {
                g_test_fail ();
-#endif
-}
+               return;
+       }
 
-static void
-test_paste_quoted_multiline_html2plain (TestFixture *fixture)
-{
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
 
-#if 0
-       test_utils_set_clipboard_text ("<html><body><b>bold</b> text<br><i>italic</i> 
text<br><u>underline</u> text</body></html>", TRUE);
+       test_utils_insert_content (fixture,
+               "<div>line 1</div>"
+               "<div>line 2</div><br>"
+               "<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_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
 
        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 "<div style=\"width: 71ch;\">text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>&gt; bold text</div>"
-               "<div style=\"width: 71ch;\">&gt; italic text</div>"
-               "<div style=\"width: 71ch;\">&gt; underline text</div></blockquote>"
-               "<div style=\"width: 71ch;\">&gt; text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> bold text\n"
-               "> italic text\n"
-               "> underline text\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 1</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div></blockquote>" HTML_SUFFIX,
+               "On Today, User wrote:\n"
+               "> line 1\n"
+               "> line 2\n"
+               "> \n"))
                g_test_fail ();
-#endif
 }
 
 static void
-test_paste_quoted_multiline_plain2html (TestFixture *fixture)
+test_cite_reply_plain (TestFixture *fixture)
 {
-       test_utils_set_clipboard_text ("line 1\nline 2\nline 3\n", FALSE);
+       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,
-               "mode:html\n"
-               "type:text before \n"
-               "action:paste-quote\n"
-               "seq:b\n" /* stop quotting */
-               "type:text after\n",
-               HTML_PREFIX "<div>text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>line 1</div>"
-               "<div>line 2</div>"
-               "<div>line 3</div></blockquote>"
-               "<div>text after</div>" HTML_SUFFIX,
-               "text before \n"
+               "",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 1</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div></blockquote>" HTML_SUFFIX,
+               "On Today, User wrote:\n"
                "> line 1\n"
-               "> line 2\n"
-               "> line 3\n"
-               "text after"))
+               "> line 2\n"))
                g_test_fail ();
 }
 
 static void
-test_paste_quoted_multiline_plain2plain (TestFixture *fixture)
+test_cite_reply_link (TestFixture *fixture)
 {
-       test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE);
+       if (!test_utils_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<html><head></head><body><div><span>123 (here <a href=\"https://www.example.com\";>\n"
+               "https://www.example.com/1234567890/1234567890/1234567890/1234567890/1234567890/";
+               ") and </span>here ěščřžýáíé <a href=\"https://www.example.com\";>www.example.com</a>"
+               " with closing text after.</div>"
+               "<div>www.example1.com</div>"
+               "<div>before www.example2.com</div>"
+               "<div>www.example3.com after</div>"
+               "<div>😏😉🙂 user@no.where line with Emoji</div></body></html>"
+               "<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,
-               "mode:plain\n"
-               "type:text before \n"
-               "action:paste-quote\n"
-               "type:\\n\n" /* stop quotting */
-               "type:text after\n",
-               HTML_PREFIX "<div style=\"width: 71ch;\">text before </div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div style=\"width: 71ch;\">&gt; line 1</div>"
-               "<div style=\"width: 71ch;\">&gt; line 2</div>"
-               "<div style=\"width: 71ch;\">&gt; line 3</div></blockquote>"
-               "<div style=\"width: 71ch;\">text after</div>" HTML_SUFFIX,
-               "text before \n"
-               "> line 1\n"
-               "> line 2\n"
-               "> line 3\n"
-               "text after"))
+               "",
+               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "123 (here </div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.example.com/1234567890/1234567890/1234567890/1234567890/1234567890/\";>"
+                       
"https://www.example.com/1234567890/1234567890/1234567890/1234567890/1234567890/</a>)<br 
class=\"-x-evo-wrap-br\">"
+               QUOTE_SPAN (QUOTE_CHR) "and here ěščřžýáíé <a 
href=\"https://www.example.com\";>www.example.com</a> with closing text after.</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a 
href=\"https://www.example1.com\";>www.example1.com</a></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "before <a 
href=\"https://www.example2.com\";>www.example2.com</a></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<a href=\"https://www.example3.com\";>www.example3.com</a> 
after</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "😏😉🙂 <a href=\"mailto:user@no.where\";>user@no.where</a> line 
with Emoji</div>"
+               "</blockquote>" HTML_SUFFIX,
+               "On Today, User wrote:\n"
+               "> 123 (here \n"
+               "> https://www.example.com/1234567890/1234567890/1234567890/1234567890/1234567890/\n";
+               "> ) and here ěščřžýáíé www.example.com with closing text after.\n"
+               "> www.example1.com\n"
+               "> before www.example2.com\n"
+               "> www.example3.com after\n"
+               "> 😏😉🙂 user@no.where line with Emoji\n"))
                g_test_fail ();
 }
 
 static void
-test_cite_html2plain (TestFixture *fixture)
+test_cite_editing_html (TestFixture *fixture)
 {
+       const gchar *plain0, *html0, *plain1, *html1, *plain2, *html2, *plain3, *html3, *plain4, *html4;
+
        if (!test_utils_process_commands (fixture,
                "mode:html\n")) {
                g_test_fail ();
@@ -1898,157 +4974,234 @@ test_cite_html2plain (TestFixture *fixture)
        }
 
        test_utils_insert_content (fixture,
-               "<html><head></head><body>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div data-evo-paragraph=\"\">level 1</div>"
-               "<div data-evo-paragraph=\"\"><br></div>"
-               "<div data-evo-paragraph=\"\">level 1</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div data-evo-paragraph=\"\">level 2</div>"
-               "</blockquote>"
-               "<div data-evo-paragraph=\"\">back in level 1</div>"
+               "<div>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>cite level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
                "</blockquote>"
-               "<div data-evo-paragraph=\"\"><br></div>"
-               "<div data-evo-paragraph=\"\">out of the citation</div>"
-               "</body></html>",
+               "<div>after citation</div>",
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
-       /* Just check the content was read properly */
+       html0 = HTML_PREFIX "<div>before citation</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>cite level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div>after citation</div>" HTML_SUFFIX;
+
+       plain0 = "before citation\n"
+               "> cite level 1a\n"
+               "> > cite level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
+       if (!test_utils_run_simple_test (fixture, "", html0, plain0)) {
+               g_test_fail ();
+               return;
+       }
+
+       html1 = HTML_PREFIX "<div>before citation</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>ciXte level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div>after citation</div>" HTML_SUFFIX;
+
+       plain1 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciXte level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>level 
1</div><div><br></div><div>level 1</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div>level 2</div></blockquote><div>back in 
level 1</div></blockquote>"
-               "<div><br></div><div>out of the citation</div>" HTML_SUFFIX,
-               "> level 1\n"
-               "> \n"
-               "> level 1\n"
-               "> > level 2\n"
-               "> back in level 1\n"
-               "\n"
-               "out of the citation")) {
+               "seq:Chc\n" /* Ctrl+Home to get to the beginning of the document */
+               "seq:ddrr\n" /* on the third line, after the second character */
+               "type:X\n",
+               html1, plain1)) {
                g_test_fail ();
                return;
        }
 
+       html2 = HTML_PREFIX "<div>before citation</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div>Y</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>te level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div>after citation</div>" HTML_SUFFIX;
+
+       plain2 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > te level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
        if (!test_utils_run_simple_test (fixture,
-               "mode:plain\n",
-               NULL,
-               "> level 1\n"
-               "> \n"
-               "> level 1\n"
-               "> > level 2\n"
-               "> back in level 1\n"
-               "\n"
-               "out of the citation")) {
+               "type:\\nY\n",
+               html2, plain2)) {
                g_test_fail ();
+               return;
        }
-}
 
-static void
-test_cite_shortline (TestFixture *fixture)
-{
-       if (!test_utils_process_commands (fixture,
-               "mode:html\n")) {
+       html3 = HTML_PREFIX "<div>before citation</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div>Y</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>tZ<br>e level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div>after citation</div>" HTML_SUFFIX;
+
+       plain3 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > tZ\n"
+               "> > e level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "seq:dr\n"
+               "type:Z\n"
+               "seq:Sns\n", /* soft Enter */
+               html3, plain3)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>Just one short line.</div>"
-               "</blockquote></body></html>",
-               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+       html4 = HTML_PREFIX "<div>before citation</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div>Y</div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                               "<div>tZ<br>e level 2</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div><br></div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div><br></div>"
+               "</blockquote>"
+               "<div><br></div>"
+               "<blockquote type='cite' " BLOCKQUOTE_STYLE ">"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div><br></div>"
+               "<div>after citation</div>" HTML_SUFFIX;
+
+       plain4 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > tZ\n"
+               "> > e level 2\n"
+               "\n"
+               "> \n"
+               "\n"
+               "> cite level 1b\n"
+               "\n"
+               "after citation\n";
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>Just one short line.</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> Just one short line.")) {
+               "seq:endhnden\n",
+               html4, plain4)) {
                g_test_fail ();
                return;
        }
 
-       if (!test_utils_process_commands (fixture,
-               "seq:C\n"
-               "type:a\n"
-               "seq:cD\n")) {
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo:3\n",
+               html3, plain3)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>Just one short line.</div>"
-               "</blockquote></body></html>",
-               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo:2\n",
+               html2, plain2)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>Just one short line.</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> Just one short line.")) {
+               "undo:undo:2\n",
+               html1, plain1)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>short line 1</div>"
-               "<div>short line 2</div>"
-               "<div>short line 3</div>"
-               "</blockquote></body></html>",
-               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo:1\n",
+               html0, plain0)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>short line 1</div>"
-               "<div>short line 2</div>"
-               "<div>short line 3</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> short line 1\n"
-               "> short line 2\n"
-               "> short line 3")) {
+               "undo:redo:1\n",
+               html1, plain1)) {
                g_test_fail ();
                return;
        }
 
-       if (!test_utils_process_commands (fixture,
-               "seq:C\n"
-               "type:a\n"
-               "seq:cD\n")) {
+       if (!test_utils_run_simple_test (fixture,
+               "undo:redo:2\n",
+               html2, plain2)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>short line 1</div>"
-               "<div>short line 2</div>"
-               "<div>short line 3</div>"
-               "</blockquote></body></html>",
-               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+       if (!test_utils_run_simple_test (fixture,
+               "undo:redo:2\n",
+               html3, plain3)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>short line 1</div>"
-               "<div>short line 2</div>"
-               "<div>short line 3</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> short line 1\n"
-               "> short line 2\n"
-               "> short line 3")) {
+               "undo:redo:3\n",
+               html4, plain4)) {
                g_test_fail ();
                return;
        }
 }
 
 static void
-test_cite_longline (TestFixture *fixture)
+test_cite_editing_plain (TestFixture *fixture)
 {
+       const gchar *plain0, *html0, *plain1, *html1, *plain2, *html2, *plain3, *html3, *plain4, *html4;
+
        if (!test_utils_process_commands (fixture,
                "mode:html\n")) {
                g_test_fail ();
@@ -2056,135 +5209,229 @@ test_cite_longline (TestFixture *fixture)
        }
 
        test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote></body></html>",
+               "<div>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>cite level 2</div>"
+                       "</blockquote>"
+                       "<div>cite level 1b</div>"
+               "</blockquote>"
+               "<div>after citation</div>",
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
+       html0 = HTML_PREFIX "<div style='width: 71ch;'>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "cite level 2</div>"
+                       "</blockquote>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1b</div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>after citation</div>" HTML_SUFFIX;
+
+       plain0 = "before citation\n"
+               "> cite level 1a\n"
+               "> > cite level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
+       if (!test_utils_run_simple_test (fixture, "mode:plain\n", html0, plain0)) {
+               g_test_fail ();
+               return;
+       }
+
+       html1 = HTML_PREFIX "<div style='width: 71ch;'>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "ciXte level 2</div>"
+                       "</blockquote>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1b</div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>after citation</div>" HTML_SUFFIX;
+
+       plain1 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciXte level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> This is the first paragraph of a quoted text which has some long text\n"
-               "> to test. It has the second sentence as well.")) {
+               "seq:Chc\n" /* Ctrl+Home to get to the beginning of the document */
+               "seq:ddrr\n" /* on the third line, after the second character */
+               "type:X\n",
+               html1, plain1)) {
                g_test_fail ();
                return;
        }
 
-       if (!test_utils_process_commands (fixture,
-               "seq:C\n"
-               "type:a\n"
-               "seq:cDb\n")) {
+       html2 = HTML_PREFIX "<div style='width: 71ch;'>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>Y</div>"
+               "<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "te level 2</div>"
+                       "</blockquote>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1b</div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>after citation</div>" HTML_SUFFIX;
+
+       plain2 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > te level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
+
+       if (!test_utils_run_simple_test (fixture,
+               "type:\\nY\n",
+               html2, plain2)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote></body></html>",
-               E_CONTENT_EDITOR_INSERT_TEXT_HTML | E_CONTENT_EDITOR_INSERT_REPLACE_ALL);
+       html3 = HTML_PREFIX "<div style='width: 71ch;'>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>Y</div>"
+               "<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "tZ</div>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "e level 2</div>"
+                       "</blockquote>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1b</div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>after citation</div>" HTML_SUFFIX;
+
+       plain3 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > tZ\n"
+               "> > e level 2\n"
+               "> cite level 1b\n"
+               "after citation\n";
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote>" HTML_SUFFIX,
-               "> This is the first paragraph of a quoted text which has some long text\n"
-               "> to test. It has the second sentence as well.")) {
+               "seq:dr\n"
+               "type:Z\n"
+               "seq:Sns\n", /* soft Enter */
+               html3, plain3)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<html><head></head><body><blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "<div>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "<div>This is the third paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote><br>after quote</body></html>",
-               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+       html4 = HTML_PREFIX "<div style='width: 71ch;'>before citation</div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1a</div>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "ciX</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div style='width: 71ch;'>Y</div>"
+               "<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "tZ</div>"
+                               "<div>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "e level 2</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div style='width: 71ch;'><br></div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'><br></div>"
+               "<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "cite level 1b</div>"
+               "</blockquote>"
+               "<div style='width: 71ch;'><br></div>"
+               "<div style='width: 71ch;'>after citation</div>" HTML_SUFFIX;
+
+       plain4 = "before citation\n"
+               "> cite level 1a\n"
+               "> > ciX\n"
+               "Y\n"
+               "> > tZ\n"
+               "> > e level 2\n"
+               "\n"
+               "> \n"
+               "\n"
+               "> cite level 1b\n"
+               "\n"
+               "after citation\n";
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div>This is the first paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "<div>This is the second paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "<div>This is the third paragraph of a quoted text which has some long text to test. It has 
the second sentence as well.</div>"
-               "</blockquote><br>after quote" HTML_SUFFIX,
-               "> This is the first paragraph of a quoted text which has some long text\n"
-               "> to test. It has the second sentence as well.\n"
-               "> This is the second 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 text\n"
-               "> to test. It has the second sentence as well.\n"
-               "\nafter quote")) {
+               "seq:endhnden\n",
+               html4, plain4)) {
                g_test_fail ();
                return;
        }
-}
 
-static void
-test_cite_reply_html (TestFixture *fixture)
-{
-       /* This test is known to fail, skip it. */
-       printf ("SKIPPED ");
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo:3\n",
+               html3, plain3)) {
+               g_test_fail ();
+               return;
+       }
 
-#if 0
-       if (!test_utils_process_commands (fixture,
-               "mode:html\n")) {
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo:2\n",
+               html2, plain2)) {
                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,
+               "undo:undo:2\n",
+               html1, plain1)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<div>On Today, User wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><pre>line 1\n"
-               "line 2\n"
-               "</pre></blockquote>" HTML_SUFFIX,
-               "On Today, User wrote:\n"
-               "> line 1\n"
-               "> line 2\n"
-               "> "))
+               "undo:undo:1\n",
+               html0, plain0)) {
                g_test_fail ();
-#endif
-}
+               return;
+       }
 
-static void
-test_cite_reply_plain (TestFixture *fixture)
-{
-       if (!test_utils_process_commands (fixture,
-               "mode:plain\n")) {
+       if (!test_utils_run_simple_test (fixture,
+               "undo:redo:1\n",
+               html1, plain1)) {
                g_test_fail ();
                return;
        }
 
-       test_utils_insert_content (fixture,
-               "<pre>line 1\n"
-               "line 2\n\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,
+               "undo:redo:2\n",
+               html2, plain2)) {
+               g_test_fail ();
+               return;
+       }
 
        if (!test_utils_run_simple_test (fixture,
-               "",
-               HTML_PREFIX "<div style=\"width: 71ch;\">On Today, User wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE "><div style=\"width: 71ch;\">&gt; line 1</div>"
-               "<div style=\"width: 71ch;\">&gt; line 2</div>"
-               "<div style=\"width: 71ch;\">&gt; <br></div></blockquote>" HTML_SUFFIX,
-               "On Today, User wrote:\n"
-               "> line 1\n"
-               "> line 2\n"
-               "> "))
+               "undo:redo:2\n",
+               html3, plain3)) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:redo:3\n",
+               html4, plain4)) {
                g_test_fail ();
+               return;
+       }
 }
 
 static void
@@ -2206,7 +5453,7 @@ test_undo_text_typed (TestFixture *fixture)
                "undo:drop\n"
                "type:xt\n",
                HTML_PREFIX "<div>some text</div>" HTML_SUFFIX,
-               "some text"))
+               "some text\n"))
                g_test_fail ();
 }
 
@@ -2221,7 +5468,7 @@ test_undo_text_forward_delete (TestFixture *fixture)
                "undo:redo\n"
                "undo:undo\n",
                HTML_PREFIX "<div>some text to delete</div>" HTML_SUFFIX,
-               "some text to delete"))
+               "some text to delete\n"))
                g_test_fail ();
 }
 
@@ -2236,7 +5483,7 @@ test_undo_text_backward_delete (TestFixture *fixture)
                "undo:redo\n"
                "undo:undo\n",
                HTML_PREFIX "<div>some text to delete</div>" HTML_SUFFIX,
-               "some text to delete"))
+               "some text to delete\n"))
                g_test_fail ();
 }
 
@@ -2250,7 +5497,7 @@ test_undo_text_cut (TestFixture *fixture)
                "action:cut\n"
                "undo:undo\n",
                NULL,
-               "some text to delete"))
+               "some text to delete\n"))
                g_test_fail ();
 }
 
@@ -2265,104 +5512,104 @@ test_undo_style (TestFixture *fixture)
                "action:bold\n"
                "type:bold\n"
                "undo:save\n" /* 2 */
-               "undo:undo:5\n"
+               "undo:undo:4\n"
                "undo:test:2\n"
-               "undo:redo:5\n"
+               "undo:redo:4\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:5\n"
+               "undo:undo:4\n"
                "type:bold\n"
                "seq:CSlsc\n"
                "action:bold\n"
                "undo:save\n" /* 2 */
-               "undo:undo:5\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:5\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:5\n"
+               "undo:undo:2\n"
 
                "action:italic\n"
                "type:italic\n"
                "undo:save\n" /* 2 */
-               "undo:undo:7\n"
+               "undo:undo:6\n"
                "undo:test:2\n"
-               "undo:redo:7\n"
+               "undo:redo:6\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:7\n"
+               "undo:undo:6\n"
                "type:italic\n"
                "seq:CSlsc\n"
                "action:italic\n"
                "undo:save\n" /* 2 */
-               "undo:undo:7\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:7\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:7\n"
+               "undo:undo:2\n"
 
                "action:underline\n"
                "type:underline\n"
                "undo:save\n" /* 2 */
-               "undo:undo:10\n"
+               "undo:undo:9\n"
                "undo:test:2\n"
-               "undo:redo:10\n"
+               "undo:redo:9\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:10\n"
+               "undo:undo:9\n"
                "type:underline\n"
                "seq:CSlsc\n"
                "action:underline\n"
                "undo:save\n" /* 2 */
-               "undo:undo:10\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:10\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:10\n"
+               "undo:undo:2\n"
 
                "action:strikethrough\n"
                "type:strikethrough\n"
                "undo:save\n" /* 2 */
-               "undo:undo:14\n"
+               "undo:undo:13\n"
                "undo:test:2\n"
-               "undo:redo:14\n"
+               "undo:redo:13\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:14\n"
+               "undo:undo:13\n"
                "type:strikethrough\n"
                "seq:CSlsc\n"
                "action:strikethrough\n"
                "undo:save\n" /* 2 */
-               "undo:undo:14\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:14\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:14\n"
+               "undo:undo:2\n"
 
-               "action:monospaced\n"
+               "font-name:monospace\n"
                "type:monospaced\n"
                "undo:save\n" /* 2 */
-               "undo:undo:11\n"
+               "undo:undo:10\n"
                "undo:test:2\n"
-               "undo:redo:11\n"
+               "undo:redo:10\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:11\n"
+               "undo:undo:10\n"
                "type:monospaced\n"
                "seq:CSlsc\n"
-               "action:monospaced\n"
+               "font-name:monospace\n"
                "undo:save\n" /* 2 */
-               "undo:undo:11\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:11\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:11\n",
+               "undo:undo:2\n",
                HTML_PREFIX "<div>The first paragraph text</div><div><br></div>" HTML_SUFFIX,
-               "The first paragraph text\n"))
+               "The first paragraph text\n\n"))
                g_test_fail ();
 }
 
@@ -2427,15 +5674,15 @@ test_undo_justify (TestFixture *fixture)
                "seq:CSlsc\n"
                "action:justify-right\n"
                "undo:save\n" /* 2 */
-               "undo:undo:6\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:6\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:6\n",
+               "undo:undo:2\n",
 
                HTML_PREFIX "<div>The first paragraph text</div><div><br></div>" HTML_SUFFIX,
-               "The first paragraph text\n"))
+               "The first paragraph text\n\n"))
                g_test_fail ();
 }
 
@@ -2460,12 +5707,12 @@ test_undo_indent (TestFixture *fixture)
                "seq:CSlsc\n"
                "action:indent\n"
                "undo:save\n" /* 2 */
-               "undo:undo:5\n"
+               "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:redo:5\n"
+               "undo:redo:2\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:5\n"
+               "undo:undo:2\n"
 
                "type:text\n"
                "undo:save\n" /* 2 */
@@ -2484,7 +5731,7 @@ test_undo_indent (TestFixture *fixture)
                "undo:undo:3\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:4\n"
+               "undo:undo:1\n"
                "undo:test\n"
 
                "type:level 1\\n\n"
@@ -2516,15 +5763,15 @@ test_undo_indent (TestFixture *fixture)
                "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:undo:18\n" /* 6x action:indent, 3x type "level X\\n" (= 4 undo steps) */
                "undo:test:2\n"
-               "undo:redo:30\n"
+               "undo:redo:18\n"
                "undo:test\n"
                "undo:drop\n" /* drop the save 2 */
-               "undo:undo:30\n",
+               "undo:undo:18\n",
 
                HTML_PREFIX "<div>The first paragraph text</div><div><br></div>" HTML_SUFFIX,
-               "The first paragraph text\n"))
+               "The first paragraph text\n\n"))
                g_test_fail ();
 }
 
@@ -2542,11 +5789,11 @@ test_undo_link_paste_html (TestFixture *fixture)
                "undo:save\n" /* 2 */
                "undo:undo:2\n"
                "undo:test:2\n"
-               "undo:undo:5\n"
-               "undo:redo:7\n"
+               "undo:undo:2\n"
+               "undo:redo:4\n"
                "undo:test\n",
                HTML_PREFIX "<div>URL:</div><div><a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></div><div><br></div>" HTML_SUFFIX,
-               "URL:\nhttp://www.gnome.org\n";))
+               "URL:\nhttp://www.gnome.org\n\n";))
                g_test_fail ();
 }
 
@@ -2570,7 +5817,7 @@ test_undo_link_paste_plain (TestFixture *fixture)
                HTML_PREFIX "<div style=\"width: 71ch;\">URL:</div>"
                "<div style=\"width: 71ch;\"><a href=\"http://www.gnome.org\";>http://www.gnome.org</a></div>"
                "<div style=\"width: 71ch;\"><br></div>" HTML_SUFFIX,
-               "URL:\nhttp://www.gnome.org\n";))
+               "URL:\nhttp://www.gnome.org\n\n";))
                g_test_fail ();
 }
 
@@ -2601,14 +5848,14 @@ test_delete_quoted (TestFixture *fixture)
                "undo:test\n"
                "undo:redo:2",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Thu, 2016-09-15 at 08:08 -0400, user wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; a</div>"
-               "<div style=\"width: 71ch;\">&gt; b</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "a</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "b</div>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Thu, 2016-09-15 at 08:08 -0400, user wrote:\n"
                "> a\n"
-               "> b"))
+               "> b\n"))
                g_test_fail ();
 }
 
@@ -2632,16 +5879,16 @@ test_delete_after_quoted (TestFixture *fixture)
                E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
 
        if (!test_utils_run_simple_test (fixture,
-               "seq:ddddbb\n",
+               "seq:dddb\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">On Thu, 2016-09-15 at 08:08 -0400, user wrote:</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<pre>&gt; a</pre>"
-               "<pre>&gt; b<br></pre>"
+               "<blockquote type=\"cite\">"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "a</pre>"
+               "<pre>" QUOTE_SPAN (QUOTE_CHR) "b</pre>"
                "</blockquote>"
                HTML_SUFFIX,
                "On Thu, 2016-09-15 at 08:08 -0400, user wrote:\n"
                "> a\n"
-               "> b"))
+               "> b\n"))
                g_test_fail ();
 }
 
@@ -2661,21 +5908,100 @@ test_delete_quoted_selection (TestFixture *fixture)
                "undo:test\n"
                "undo:redo\n"
                "undo:undo\n"
-               "seq:d\n"
+               "seq:r\n"
                "type:X\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">line 0</div>"
-               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
-               "<div style=\"width: 71ch;\">&gt; line 1</div>"
-               "<div style=\"width: 71ch;\">&gt; <br></div>"
-               "<div style=\"width: 71ch;\">&gt; line 2</div>"
-               "<div style=\"width: 71ch;\">&gt; X</div>"
+               "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 1</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "<br></div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "X</div>"
                "</blockquote>"
                HTML_SUFFIX,
                "line 0\n"
                "> line 1\n"
                "> \n"
                "> line 2\n"
-               "> X"))
+               "> X\n"))
+               g_test_fail ();
+}
+
+static void
+test_delete_quoted_multiselect (TestFixture *fixture)
+{
+       test_utils_set_clipboard_text ("line 1\nline 2\nline 3", FALSE);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "action:paste-quote\n"
+               "type:X\n"
+               "undo:save\n" /* 1 */
+               "seq:ChcrrSdsD\n",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>line 2</div>"
+               "<div>line 3X</div>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "> line 2\n"
+               "> line 3X\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "undo:drop:1\n"
+               "seq:Cec\n" /* Go to the end of the document (Ctrl+End) */
+               "type:\\nY\n",
+               HTML_PREFIX "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+               "<div>line 2</div>"
+               "<div>line 3X</div>"
+               "</blockquote>"
+               "<div>Y</div>"
+               HTML_SUFFIX,
+               "> line 2\n"
+               "> line 3X\n"
+               "Y\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture, "<body></body>", E_CONTENT_EDITOR_INSERT_REPLACE_ALL | 
E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:plain\n"
+               "action:paste-quote\n"
+               "type:X\n"
+               "undo:save\n" /* 1 */
+               "seq:ChcrrSdsD\n",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 3X</div>"
+               "</blockquote>"
+               HTML_SUFFIX,
+               "> line 2\n"
+               "> line 3X\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "undo:undo\n"
+               "undo:test\n"
+               "undo:redo\n"
+               "seq:Cec\n" /* Go to the end of the document (Ctrl+End) */
+               "type:\\nY\n",
+               HTML_PREFIX "<blockquote type=\"cite\">"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 2</div>"
+               "<div>" QUOTE_SPAN (QUOTE_CHR) "line 3X</div>"
+               "</blockquote>"
+               "<div style=\"width: 71ch;\">Y</div>"
+               HTML_SUFFIX,
+               "> line 2\n"
+               "> line 3X\n"
+               "Y\n"))
                g_test_fail ();
 }
 
@@ -2689,14 +6015,15 @@ test_replace_dialog (TestFixture *fixture)
                "seq:h\n"
                "action:show-replace\n"
                "type:to\t2\n"
-               "type:\t\t\t\t\t\t\n" /* Jump to 'Replace' */
-               "seq:n\n" /* Press it */
+               "seq:A\n" /* Press 'Alt+R' to press 'Replace' button */
+               "type:r\n"
+               "seq:a\n"
                "seq:^\n" /* Close the dialog */
                "undo:undo\n"
                "undo:test:1\n"
                "undo:redo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">text 2 replace</div>" HTML_SUFFIX,
-               "text 2 replace"))
+               "text 2 replace\n"))
                g_test_fail ();
 }
 
@@ -2710,14 +6037,375 @@ test_replace_dialog_all (TestFixture *fixture)
                "seq:h\n"
                "action:show-replace\n"
                "type:e\t3\n"
-               "type:\t\t\t\t\t\t\t\n" /* Jump to 'Replace All' */
-               "seq:n\n" /* Press it */
+               "seq:A\n" /* Press 'Alt+A' to press 'Replace All' button */
+               "type:a\n"
+               "seq:a\n"
                "seq:^\n" /* Close the dialog */
                "undo:undo\n"
                "undo:test:1\n"
                "undo:redo\n",
                HTML_PREFIX "<div style=\"width: 71ch;\">t3xt to r3plac3</div>" HTML_SUFFIX,
-               "t3xt to r3plac3"))
+               "t3xt to r3plac3\n"))
+               g_test_fail ();
+}
+
+static void
+test_wrap_basic (TestFixture *fixture)
+{
+       test_utils_fixture_change_setting_int32 (fixture, "org.gnome.evolution.mail", 
"composer-word-wrap-length", 10);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:123 456 789 123 456\\n\n"
+               "type:a b\\n\n"
+               "type:c \\n\n"
+               "type:d\\n\n"
+               "type: e f\\n\n"
+               "type:\\n\n"
+               "type:123 456 7 8 9 12345 1 2 3 456 789\n"
+               "action:select-all\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<div>123 456<br>"
+               "789 123<br>"
+               "456 a b c <br>"
+               "d  e f</div>"
+               "<div><br></div>"
+               "<div>123 456 7<br>"
+               "8 9 12345<br>"
+               "1 2 3 456<br>"
+               "789</div>" HTML_SUFFIX,
+               "123 456\n"
+               "789 123\n"
+               "456 a b c \n"
+               "d  e f\n"
+               "\n"
+               "123 456 7\n"
+               "8 9 12345\n"
+               "1 2 3 456\n"
+               "789\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       if (!test_utils_run_simple_test (fixture,
+               "action:select-all\n"
+               "seq:D\n"
+               "type:123 456 7 8 901234567890123456 1 2 3 4 5 6 7\\n\n"
+               "type:1234567890123456 12345678901234567890 1 2 3 4 5 6 7 8\\n\n"
+               "type:12345678 123456789 1234567890 123 456 78\n"
+               "action:select-all\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<div>123 456 7<br>"
+               "8<br>"
+               "901234567890123456<br>"
+               "1 2 3 4 5<br>"
+               "6 7<br>"
+               "1234567890123456<br>"
+               "12345678901234567890<br>"
+               "1 2 3 4 5<br>"
+               "6 7 8<br>"
+               "12345678<br>"
+               "123456789<br>"
+               "1234567890<br>"
+               "123 456 78</div>" HTML_SUFFIX,
+               "123 456 7\n"
+               "8\n"
+               "9012345678\n"
+               "90123456\n"
+               "1 2 3 4 5\n"
+               "6 7\n"
+               "1234567890\n"
+               "123456\n"
+               "1234567890\n"
+               "1234567890\n"
+               "1 2 3 4 5\n"
+               "6 7 8\n"
+               "12345678\n"
+               "123456789\n"
+               "1234567890\n"
+               "123 456 78\n")) {
+               g_test_fail ();
+               return;
+       }
+}
+
+static void
+test_wrap_nested (TestFixture *fixture)
+{
+       test_utils_fixture_change_setting_int32 (fixture, "org.gnome.evolution.mail", 
"composer-word-wrap-length", 10);
+
+       if (!test_utils_run_simple_test (fixture,
+               "mode:html\n"
+               "type:123 4 \n"
+               "action:bold\n"
+               "type:b\n"
+               "action:bold\n"
+               "type: 5 67 89 \n"
+               "action:bold\n"
+               "type:bold text\n"
+               "action:bold\n"
+               "type: 123 456 \n"
+               "action:italic\n"
+               "type:italic text \n"
+               "action:underline\n"
+               "type:and underline text\n"
+               "action:underline\n"
+               "type: xyz\n"
+               "action:italic\n"
+               "type: 7 8 9 1 2 3 4 5\n"
+               "action:select-all\n"
+               "action:wrap-lines\n",
+               HTML_PREFIX "<div>123 4 <b>b</b> 5<br>"
+               "67 89 <b>bold<br>"
+               "text</b> 123<br>"
+               "456 <i>italic<br>"
+               "text <u>and<br>"
+               "underline<br>"
+               "text</u> xyz</i> 7<br>"
+               "8 9 1 2 3<br>"
+               "4 5</div>" HTML_SUFFIX,
+               "123 4 b 5\n"
+               "67 89 bold\n"
+               "text 123\n"
+               "456 italic\n"
+               "text and\n"
+               "underline\n"
+               "text xyz 7\n"
+               "8 9 1 2 3\n"
+               "4 5\n"))
+               g_test_fail ();
+}
+
+static void
+test_pre_split_simple_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</pre>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<pre>line 1</pre>"
+               "<pre>line 2</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "line 2\n"))
+               g_test_fail ();
+}
+
+static void
+test_pre_split_simple_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</pre>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<pre>line 1</pre>"
+               "<pre>line 2</pre>"
+               HTML_SUFFIX,
+               "line 1\n"
+               "line 2\n"))
+               g_test_fail ();
+}
+
+static void
+test_pre_split_complex_html (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:html\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<div>leading text</div>"
+               "<pre><b>bold1</b><blockquote type=\"cite\">text 1\n"
+               "text 2\n"
+               "text 3</blockquote>"
+               "text A<i>italic</i>text B\n"
+               "<b>bold2</b>"
+               "</pre>"
+               "<div>mid text</div>"
+               "<pre><blockquote type=\"cite\">level 1\n"
+               "E-mail: &lt;<a href=\"mailto:user@no.where\";>user@no.where</a>&gt; line\n"
+               "Phone: 1234567890\n"
+               "<div>div in\npre</div>"
+               "<blockquote type=\"cite\">level 2\n"
+               "\n"
+               "level 2\n</blockquote>"
+               "</blockquote></pre>"
+               "<pre>text\n"
+               "text 2<i>italic 1</i>\n"
+               "<i>italic 2</i> text 3\n"
+               "pre <i>imid</i> pos\n"
+               "<i>ipre</i> mid <i>ipos</i>\n"
+               "<i>ipre2</i> mid2 <i>i<b>pos</b>2</i> pos2\n</pre>"
+               "<div>closing text</div>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<div>leading text</div>"
+               "<pre><b>bold1</b></pre>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+                       "<pre>text 1</pre>"
+                       "<pre>text 2</pre>"
+                       "<pre>text 3</pre>"
+               "</blockquote>"
+               "<pre>text A<i>italic</i>text B</pre>"
+               "<pre><b>bold2</b></pre>"
+               "<div>mid text</div>"
+               "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+                       "<pre>level 1</pre>"
+                       "<pre>E-mail: &lt;<a href=\"mailto:user@no.where\";>user@no.where</a>&gt; line</pre>"
+                       "<pre>Phone: 1234567890</pre>"
+                       "<pre><div>div in\npre</div></pre>"
+                       "<blockquote type=\"cite\" " BLOCKQUOTE_STYLE ">"
+                               "<pre>level 2</pre>"
+                               "<pre><br></pre>"
+                               "<pre>level 2</pre>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<pre>text</pre>"
+               "<pre>text 2<i>italic 1</i></pre>"
+               "<pre><i>italic 2</i> text 3</pre>"
+               "<pre>pre <i>imid</i> pos</pre>"
+               "<pre><i>ipre</i> mid <i>ipos</i></pre>"
+               "<pre><i>ipre2</i> mid2 <i>i<b>pos</b>2</i> pos2</pre>"
+               "<div>closing text</div>"
+               HTML_SUFFIX,
+               "leading text\n"
+               "bold1\n"
+               "> text 1\n"
+               "> text 2\n"
+               "> text 3\n"
+               "text Aitalictext B\n"
+               "bold2\n"
+               "mid text\n"
+               "> level 1\n"
+               "> E-mail: <user@no.where> line\n"
+               "> Phone: 1234567890\n"
+               "> > div in\n" /* this and th ebelow line is level 1 in quotation, but due to nested <div> in 
<pre> the EvoConvert */
+               "> > pre\n"    /* doubles quotation marks. It's not ideal, but it's a broken HTML anyway 
(broken for the HTML editor). */
+               "> > level 2\n"
+               "> > \n"
+               "> > level 2\n"
+               "text\n"
+               "text 2italic 1\n"
+               "italic 2 text 3\n"
+               "pre imid pos\n"
+               "ipre mid ipos\n"
+               "ipre2 mid2 ipos2 pos2\n"
+               "closing text\n"))
+               g_test_fail ();
+}
+
+static void
+test_pre_split_complex_plain (TestFixture *fixture)
+{
+       if (!test_utils_process_commands (fixture,
+               "mode:plain\n")) {
+               g_test_fail ();
+               return;
+       }
+
+       test_utils_insert_content (fixture,
+               "<div>leading text</div>"
+               "<pre><b>bold1</b><blockquote type=\"cite\">text 1\n"
+               "text 2\n"
+               "text 3</blockquote>"
+               "text A<i>italic</i>text B\n"
+               "<b>bold2</b>"
+               "</pre>"
+               "<div>mid text</div>"
+               "<pre><blockquote type=\"cite\">level 1\n"
+               "E-mail: &lt;<a href=\"mailto:user@no.where\";>user@no.where</a>&gt; line\n"
+               "Phone: 1234567890\n"
+               "<div>div in\npre</div>"
+               "<blockquote type=\"cite\">level 2\n"
+               "\n"
+               "level 2\n</blockquote>"
+               "</blockquote></pre>"
+               "<pre>text\n"
+               "text 2<i>italic 1</i>\n"
+               "<i>italic 2</i> text 3\n"
+               "pre <i>imid</i> pos\n"
+               "<i>ipre</i> mid <i>ipos</i>\n"
+               "<i>ipre2</i> mid2 <i>i<b>pos</b>2</i> pos2\n</pre>"
+               "<div>closing text</div>",
+               E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_HTML);
+
+       if (!test_utils_run_simple_test (fixture,
+               "",
+               HTML_PREFIX "<div style='width: 71ch;'>leading text</div>"
+               "<pre><b>bold1</b></pre>"
+               "<blockquote type=\"cite\">"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "text 1</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "text 2</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "text 3</pre>"
+               "</blockquote>"
+               "<pre>text A<i>italic</i>text B</pre>"
+               "<pre><b>bold2</b></pre>"
+               "<div style='width: 71ch;'>mid text</div>"
+               "<blockquote type=\"cite\">"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "level 1</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "E-mail: &lt;<a 
href=\"mailto:user@no.where\";>user@no.where</a>&gt; line</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "Phone: 1234567890</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "div in</pre>"
+                       "<pre>" QUOTE_SPAN (QUOTE_CHR) "pre</pre>"
+                       "<blockquote type=\"cite\">"
+                               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "level 2</pre>"
+                               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "<br></pre>"
+                               "<pre>" QUOTE_SPAN (QUOTE_CHR QUOTE_CHR) "level 2</pre>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<pre>text</pre>"
+               "<pre>text 2<i>italic 1</i></pre>"
+               "<pre><i>italic 2</i> text 3</pre>"
+               "<pre>pre <i>imid</i> pos</pre>"
+               "<pre><i>ipre</i> mid <i>ipos</i></pre>"
+               "<pre><i>ipre2</i> mid2 <i>i<b>pos</b>2</i> pos2</pre>"
+               "<div style='width: 71ch;'>closing text</div>"
+               HTML_SUFFIX,
+               "leading text\n"
+               "bold1\n"
+               "> text 1\n"
+               "> text 2\n"
+               "> text 3\n"
+               "text Aitalictext B\n"
+               "bold2\n"
+               "mid text\n"
+               "> level 1\n"
+               "> E-mail: <user@no.where> line\n"
+               "> Phone: 1234567890\n"
+               "> div in\n"
+               "> pre\n"
+               "> > level 2\n"
+               "> > \n"
+               "> > level 2\n"
+               "text\n"
+               "text 2italic 1\n"
+               "italic 2 text 3\n"
+               "pre imid pos\n"
+               "ipre mid ipos\n"
+               "ipre2 mid2 ipos2 pos2\n"
+               "closing text\n"))
                g_test_fail ();
 }
 
@@ -2767,6 +6455,7 @@ main (gint argc,
           user settings when playing with them. */
        g_setenv ("GIO_EXTRA_MODULES", EVOLUTION_TESTGIOMODULESDIR, TRUE);
        g_setenv ("GSETTINGS_BACKEND", TEST_KEYFILE_SETTINGS_BACKEND_NAME, TRUE);
+       g_setenv ("E_HTML_EDITOR_TEST_SOURCES", "1", FALSE);
        g_setenv (TEST_KEYFILE_SETTINGS_FILENAME_ENVVAR, test_keyfile_filename, TRUE);
 
        g_test_init (&argc, &argv, NULL);
@@ -2797,110 +6486,133 @@ main (gint argc,
        e_util_init_main_thread (NULL);
        e_passwords_init ();
 
+       gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), EVOLUTION_ICONDIR);
+
        modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
        g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
 
        test_utils_add_test ("/create/editor", test_create_editor);
-       test_utils_add_test ("/style/bold/selection", test_style_bold_selection);
-       test_utils_add_test ("/style/bold/typed", test_style_bold_typed);
-       test_utils_add_test ("/style/italic/selection", test_style_italic_selection);
-       test_utils_add_test ("/style/italic/typed", test_style_italic_typed);
-       test_utils_add_test ("/style/underline/selection", test_style_underline_selection);
-       test_utils_add_test ("/style/underline/typed", test_style_underline_typed);
-       test_utils_add_test ("/style/strikethrough/selection", test_style_strikethrough_selection);
-       test_utils_add_test ("/style/strikethrough/typed", test_style_strikethrough_typed);
-       test_utils_add_test ("/style/monospace/selection", test_style_monospace_selection);
-       test_utils_add_test ("/style/monospace/typed", test_style_monospace_typed);
+       test_utils_add_test ("/style/bold-selection", test_style_bold_selection);
+       test_utils_add_test ("/style/bold-typed", test_style_bold_typed);
+       test_utils_add_test ("/style/italic-selection", test_style_italic_selection);
+       test_utils_add_test ("/style/italic-typed", test_style_italic_typed);
+       test_utils_add_test ("/style/underline-selection", test_style_underline_selection);
+       test_utils_add_test ("/style/underline-typed", test_style_underline_typed);
+       test_utils_add_test ("/style/strikethrough-selection", test_style_strikethrough_selection);
+       test_utils_add_test ("/style/strikethrough-typed", test_style_strikethrough_typed);
+       test_utils_add_test ("/style/monospace-selection", test_style_monospace_selection);
+       test_utils_add_test ("/style/monospace-typed", test_style_monospace_typed);
        test_utils_add_test ("/justify/selection", test_justify_selection);
        test_utils_add_test ("/justify/typed", test_justify_typed);
        test_utils_add_test ("/indent/selection", test_indent_selection);
        test_utils_add_test ("/indent/typed", test_indent_typed);
-       test_utils_add_test ("/font/size/selection", test_font_size_selection);
-       test_utils_add_test ("/font/size/typed", test_font_size_typed);
-       test_utils_add_test ("/font/color/selection", test_font_color_selection);
-       test_utils_add_test ("/font/color/typed", test_font_color_typed);
-       test_utils_add_test ("/list/bullet/plain", test_list_bullet_plain);
-       test_utils_add_test ("/list/bullet/html", test_list_bullet_html);
-       test_utils_add_test ("/list/bullet/change", test_list_bullet_change);
-       test_utils_add_test ("/list/bullet/html/from-block", test_list_bullet_html_from_block);
-       test_utils_add_test ("/list/alpha/html", test_list_alpha_html);
-       test_utils_add_test ("/list/alpha/plain", test_list_alpha_plain);
-       test_utils_add_test ("/list/roman/html", test_list_roman_html);
-       test_utils_add_test ("/list/roman/plain", test_list_roman_plain);
-       test_utils_add_test ("/list/multi/html", test_list_multi_html);
-       test_utils_add_test ("/list/multi/plain", test_list_multi_plain);
-       test_utils_add_test ("/list/multi/change/html", test_list_multi_change_html);
-       test_utils_add_test ("/list/multi/change/plain", test_list_multi_change_plain);
-       test_utils_add_test ("/link/insert/dialog", test_link_insert_dialog);
-       test_utils_add_test ("/link/insert/dialog/selection", test_link_insert_dialog_selection);
-       test_utils_add_test ("/link/insert/dialog/remove-link", test_link_insert_dialog_remove_link);
-       test_utils_add_test ("/link/insert/typed", test_link_insert_typed);
-       test_utils_add_test ("/link/insert/typed/change-description", 
test_link_insert_typed_change_description);
-       test_utils_add_test ("/link/insert/typed/append", test_link_insert_typed_append);
-       test_utils_add_test ("/link/insert/typed/remove", test_link_insert_typed_remove);
+       test_utils_add_test ("/font/size-selection", test_font_size_selection);
+       test_utils_add_test ("/font/size-typed", test_font_size_typed);
+       test_utils_add_test ("/font/color-selection", test_font_color_selection);
+       test_utils_add_test ("/font/color-typed", test_font_color_typed);
+       test_utils_add_test ("/list/bullet-plain", test_list_bullet_plain);
+       test_utils_add_test ("/list/bullet-html", test_list_bullet_html);
+       test_utils_add_test ("/list/bullet-change", test_list_bullet_change);
+       test_utils_add_test ("/list/bullet-html-from-block", test_list_bullet_html_from_block);
+       test_utils_add_test ("/list/alpha-html", test_list_alpha_html);
+       test_utils_add_test ("/list/alpha-plain", test_list_alpha_plain);
+       test_utils_add_test ("/list/number-html", test_list_number_html);
+       test_utils_add_test ("/list/number-plain", test_list_number_plain);
+       test_utils_add_test ("/list/roman-html", test_list_roman_html);
+       test_utils_add_test ("/list/roman-plain", test_list_roman_plain);
+       test_utils_add_test ("/list/multi-html", test_list_multi_html);
+       test_utils_add_test ("/list/multi-plain", test_list_multi_plain);
+       test_utils_add_test ("/list/multi-change-html", test_list_multi_change_html);
+       test_utils_add_test ("/list/multi-change-plain", test_list_multi_change_plain);
+       test_utils_add_test ("/list/indent-same-html", test_list_indent_same_html);
+       test_utils_add_test ("/list/indent-same-plain", test_list_indent_same_plain);
+       test_utils_add_test ("/list/indent-different-html", test_list_indent_different_html);
+       test_utils_add_test ("/list/indent-different-plain", test_list_indent_different_plain);
+       test_utils_add_test ("/list/indent-multi-html", test_list_indent_multi_html);
+       test_utils_add_test ("/list/indent-multi-plain", test_list_indent_multi_plain);
+       test_utils_add_test ("/list/indent-nested-html", test_list_indent_nested_html);
+       test_utils_add_test ("/list/indent-nested-plain", test_list_indent_nested_plain);
+       test_utils_add_test ("/link/insert-dialog", test_link_insert_dialog);
+       test_utils_add_test ("/link/insert-dialog-selection", test_link_insert_dialog_selection);
+       test_utils_add_test ("/link/insert-dialog-remove-link", test_link_insert_dialog_remove_link);
+       test_utils_add_test ("/link/insert-typed", test_link_insert_typed);
+       test_utils_add_test ("/link/insert-typed-change-description", 
test_link_insert_typed_change_description);
+       test_utils_add_test ("/link/insert-typed-append", test_link_insert_typed_append);
+       test_utils_add_test ("/link/insert-typed-remove", test_link_insert_typed_remove);
        test_utils_add_test ("/h-rule/insert", test_h_rule_insert);
        test_utils_add_test ("/h-rule/insert-text-after", test_h_rule_insert_text_after);
        test_utils_add_test ("/image/insert", test_image_insert);
-       test_utils_add_test ("/emoticon/insert/typed", test_emoticon_insert_typed);
-       test_utils_add_test ("/emoticon/insert/typed-dash", test_emoticon_insert_typed_dash);
-       test_utils_add_test ("/paragraph/normal/selection", test_paragraph_normal_selection);
-       test_utils_add_test ("/paragraph/normal/typed", test_paragraph_normal_typed);
-       test_utils_add_test ("/paragraph/preformatted/selection", test_paragraph_preformatted_selection);
-       test_utils_add_test ("/paragraph/preformatted/typed", test_paragraph_preformatted_typed);
-       test_utils_add_test ("/paragraph/address/selection", test_paragraph_address_selection);
-       test_utils_add_test ("/paragraph/address/typed", test_paragraph_address_typed);
-       test_utils_add_test ("/paragraph/header1/selection", test_paragraph_header1_selection);
-       test_utils_add_test ("/paragraph/header1/typed", test_paragraph_header1_typed);
-       test_utils_add_test ("/paragraph/header2/selection", test_paragraph_header2_selection);
-       test_utils_add_test ("/paragraph/header2/typed", test_paragraph_header2_typed);
-       test_utils_add_test ("/paragraph/header3/selection", test_paragraph_header3_selection);
-       test_utils_add_test ("/paragraph/header3/typed", test_paragraph_header3_typed);
-       test_utils_add_test ("/paragraph/header4/selection", test_paragraph_header4_selection);
-       test_utils_add_test ("/paragraph/header4/typed", test_paragraph_header4_typed);
-       test_utils_add_test ("/paragraph/header5/selection", test_paragraph_header5_selection);
-       test_utils_add_test ("/paragraph/header5/typed", test_paragraph_header5_typed);
-       test_utils_add_test ("/paragraph/header6/selection", test_paragraph_header6_selection);
-       test_utils_add_test ("/paragraph/header6/typed", test_paragraph_header6_typed);
+       test_utils_add_test ("/emoticon/insert-typed", test_emoticon_insert_typed);
+       test_utils_add_test ("/emoticon/insert-typed-dash", test_emoticon_insert_typed_dash);
+       test_utils_add_test ("/paragraph/normal-selection", test_paragraph_normal_selection);
+       test_utils_add_test ("/paragraph/normal-typed", test_paragraph_normal_typed);
+       test_utils_add_test ("/paragraph/preformatted-selection", test_paragraph_preformatted_selection);
+       test_utils_add_test ("/paragraph/preformatted-typed", test_paragraph_preformatted_typed);
+       test_utils_add_test ("/paragraph/address-selection", test_paragraph_address_selection);
+       test_utils_add_test ("/paragraph/address-typed", test_paragraph_address_typed);
+       test_utils_add_test ("/paragraph/header1-selection", test_paragraph_header1_selection);
+       test_utils_add_test ("/paragraph/header1-typed", test_paragraph_header1_typed);
+       test_utils_add_test ("/paragraph/header2-selection", test_paragraph_header2_selection);
+       test_utils_add_test ("/paragraph/header2-typed", test_paragraph_header2_typed);
+       test_utils_add_test ("/paragraph/header3-selection", test_paragraph_header3_selection);
+       test_utils_add_test ("/paragraph/header3-typed", test_paragraph_header3_typed);
+       test_utils_add_test ("/paragraph/header4-selection", test_paragraph_header4_selection);
+       test_utils_add_test ("/paragraph/header4-typed", test_paragraph_header4_typed);
+       test_utils_add_test ("/paragraph/header5-selection", test_paragraph_header5_selection);
+       test_utils_add_test ("/paragraph/header5-typed", test_paragraph_header5_typed);
+       test_utils_add_test ("/paragraph/header6-selection", test_paragraph_header6_selection);
+       test_utils_add_test ("/paragraph/header6-typed", test_paragraph_header6_typed);
        test_utils_add_test ("/paragraph/wrap-lines", test_paragraph_wrap_lines);
-       test_utils_add_test ("/paste/singleline/html2html", test_paste_singleline_html2html);
-       test_utils_add_test ("/paste/singleline/html2plain", test_paste_singleline_html2plain);
-       test_utils_add_test ("/paste/singleline/plain2html", test_paste_singleline_plain2html);
-       test_utils_add_test ("/paste/singleline/plain2plain", test_paste_singleline_plain2plain);
-       test_utils_add_test ("/paste/multiline/html2html", test_paste_multiline_html2html);
-       test_utils_add_test ("/paste/multiline/html2plain", test_paste_multiline_html2plain);
-       test_utils_add_test ("/paste/multiline/div/html2html", test_paste_multiline_div_html2html);
-       test_utils_add_test ("/paste/multiline/div/html2plain", test_paste_multiline_div_html2plain);
-       test_utils_add_test ("/paste/multiline/p/html2html", test_paste_multiline_p_html2html);
-       test_utils_add_test ("/paste/multiline/p/html2plain", test_paste_multiline_p_html2plain);
-       test_utils_add_test ("/paste/multiline/plain2html", test_paste_multiline_plain2html);
-       test_utils_add_test ("/paste/multiline/plain2plain", test_paste_multiline_plain2plain);
-       test_utils_add_test ("/paste/quoted/singleline/html2html", test_paste_quoted_singleline_html2html);
-       test_utils_add_test ("/paste/quoted/singleline/html2plain", test_paste_quoted_singleline_html2plain);
-       test_utils_add_test ("/paste/quoted/singleline/plain2html", test_paste_quoted_singleline_plain2html);
-       test_utils_add_test ("/paste/quoted/singleline/plain2plain", 
test_paste_quoted_singleline_plain2plain);
-       test_utils_add_test ("/paste/quoted/multiline/html2html", test_paste_quoted_multiline_html2html);
-       test_utils_add_test ("/paste/quoted/multiline/html2plain", test_paste_quoted_multiline_html2plain);
-       test_utils_add_test ("/paste/quoted/multiline/plain2html", test_paste_quoted_multiline_plain2html);
-       test_utils_add_test ("/paste/quoted/multiline/plain2plain", test_paste_quoted_multiline_plain2plain);
+       test_utils_add_test ("/paste/singleline-html2html", test_paste_singleline_html2html);
+       test_utils_add_test ("/paste/singleline-html2plain", test_paste_singleline_html2plain);
+       test_utils_add_test ("/paste/singleline-plain2html", test_paste_singleline_plain2html);
+       test_utils_add_test ("/paste/singleline-plain2plain", test_paste_singleline_plain2plain);
+       test_utils_add_test ("/paste/multiline-html2html", test_paste_multiline_html2html);
+       test_utils_add_test ("/paste/multiline-html2plain", test_paste_multiline_html2plain);
+       test_utils_add_test ("/paste/multiline-div-html2html", test_paste_multiline_div_html2html);
+       test_utils_add_test ("/paste/multiline-div-html2plain", test_paste_multiline_div_html2plain);
+       test_utils_add_test ("/paste/multiline-p-html2html", test_paste_multiline_p_html2html);
+       test_utils_add_test ("/paste/multiline-p-html2plain", test_paste_multiline_p_html2plain);
+       test_utils_add_test ("/paste/multiline-plain2html", test_paste_multiline_plain2html);
+       test_utils_add_test ("/paste/multiline-plain2plain", test_paste_multiline_plain2plain);
+       test_utils_add_test ("/paste/quoted-singleline-html2html", test_paste_quoted_singleline_html2html);
+       test_utils_add_test ("/paste/quoted-singleline-html2plain", test_paste_quoted_singleline_html2plain);
+       test_utils_add_test ("/paste/quoted-singleline-plain2html", test_paste_quoted_singleline_plain2html);
+       test_utils_add_test ("/paste/quoted-singleline-plain2plain", 
test_paste_quoted_singleline_plain2plain);
+       test_utils_add_test ("/paste/quoted-multiline-html2html", test_paste_quoted_multiline_html2html);
+       test_utils_add_test ("/paste/quoted-multiline-html2plain", test_paste_quoted_multiline_html2plain);
+       test_utils_add_test ("/paste/quoted-multiline-plain2html", test_paste_quoted_multiline_plain2html);
+       test_utils_add_test ("/paste/quoted-multiline-plain2plain", test_paste_quoted_multiline_plain2plain);
        test_utils_add_test ("/cite/html2plain", test_cite_html2plain);
        test_utils_add_test ("/cite/shortline", test_cite_shortline);
        test_utils_add_test ("/cite/longline", test_cite_longline);
-       test_utils_add_test ("/cite/reply/html", test_cite_reply_html);
-       test_utils_add_test ("/cite/reply/plain", test_cite_reply_plain);
-       test_utils_add_test ("/undo/text/typed", test_undo_text_typed);
-       test_utils_add_test ("/undo/text/forward-delete", test_undo_text_forward_delete);
-       test_utils_add_test ("/undo/text/backward-delete", test_undo_text_backward_delete);
-       test_utils_add_test ("/undo/text/cut", test_undo_text_cut);
+       test_utils_add_test ("/cite/reply-html", test_cite_reply_html);
+       test_utils_add_test ("/cite/reply-html-to-plain", test_cite_reply_html_to_plain);
+       test_utils_add_test ("/cite/reply-plain", test_cite_reply_plain);
+       test_utils_add_test ("/cite/reply-link", test_cite_reply_link);
+       test_utils_add_test ("/cite/editing-html", test_cite_editing_html);
+       test_utils_add_test ("/cite/editing-plain", test_cite_editing_plain);
+       test_utils_add_test ("/undo/text-typed", test_undo_text_typed);
+       test_utils_add_test ("/undo/text-forward-delete", test_undo_text_forward_delete);
+       test_utils_add_test ("/undo/text-backward-delete", test_undo_text_backward_delete);
+       test_utils_add_test ("/undo/text-cut", test_undo_text_cut);
        test_utils_add_test ("/undo/style", test_undo_style);
        test_utils_add_test ("/undo/justify", test_undo_justify);
        test_utils_add_test ("/undo/indent", test_undo_indent);
-       test_utils_add_test ("/undo/link-paste/html", test_undo_link_paste_html);
-       test_utils_add_test ("/undo/link-paste/plain", test_undo_link_paste_plain);
+       test_utils_add_test ("/undo/link-paste-html", test_undo_link_paste_html);
+       test_utils_add_test ("/undo/link-paste-plain", test_undo_link_paste_plain);
        test_utils_add_test ("/delete/quoted", test_delete_quoted);
        test_utils_add_test ("/delete/after-quoted", test_delete_after_quoted);
-       test_utils_add_test ("/delete/quoted/selection", test_delete_quoted_selection);
+       test_utils_add_test ("/delete/quoted-selection", test_delete_quoted_selection);
+       test_utils_add_test ("/delete/quoted-multiselect", test_delete_quoted_multiselect);
        test_utils_add_test ("/replace/dialog", test_replace_dialog);
-       test_utils_add_test ("/replace-all/dialog", test_replace_dialog_all);
+       test_utils_add_test ("/replace/dialog-all", test_replace_dialog_all);
+       test_utils_add_test ("/wrap/basic", test_wrap_basic);
+       test_utils_add_test ("/wrap/nested", test_wrap_nested);
+       test_utils_add_test ("/pre-split/simple-html", test_pre_split_simple_html);
+       test_utils_add_test ("/pre-split/simple-plain", test_pre_split_simple_plain);
+       test_utils_add_test ("/pre-split/complex-html", test_pre_split_complex_html);
+       test_utils_add_test ("/pre-split/complex-plain", test_pre_split_complex_plain);
 
        test_add_html_editor_bug_tests ();
 
diff --git a/src/e-util/test-html-editor.c b/src/e-util/test-html-editor.c
index b21632f0d1..9d684e2a41 100644
--- a/src/e-util/test-html-editor.c
+++ b/src/e-util/test-html-editor.c
@@ -59,12 +59,19 @@ 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-draft-source'/>\n"
 "     <separator/>\n"
 "     <menuitem action='view-webkit-inspector'/>\n"
 "    </menu>\n"
 "  </menubar>\n"
 "</ui>";
 
+enum {
+       GET_CONTENT_PLAIN,
+       GET_CONTENT_HTML,
+       GET_CONTENT_DRAFT
+};
+
 static void create_new_editor (void);
 
 static void
@@ -139,19 +146,16 @@ save_dialog (EHTMLEditor *editor)
 }
 
 static void
-view_source_dialog (EHTMLEditor *editor,
-                    const gchar *title,
-                    gboolean plain_text,
-                    gboolean show_source)
+view_source_dialog_show (EHTMLEditor *editor,
+                        const gchar *title,
+                        gboolean plain_text,
+                        gboolean show_source,
+                        const gchar *content_text)
 {
        GtkWidget *dialog;
        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,
@@ -176,32 +180,20 @@ view_source_dialog (EHTMLEditor *editor,
        gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 6);
        gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 300);
 
-       if (plain_text) {
-               html = e_content_editor_get_content (cnt_editor,
-                       E_CONTENT_EDITOR_GET_PROCESSED | E_CONTENT_EDITOR_GET_TEXT_PLAIN,
-                       NULL, NULL);
-       } else {
-               GSList *inline_images = NULL;
-
-               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_slist_free_full (inline_images, g_object_unref);
-       }
-
        if (show_source || plain_text) {
                GtkTextBuffer *buffer;
 
                content = gtk_text_view_new ();
                buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (content));
-               gtk_text_buffer_set_text (buffer, html ? html : "", -1);
+               gtk_text_buffer_set_text (buffer, content_text ? content_text : "", -1);
                gtk_text_view_set_editable (GTK_TEXT_VIEW (content), FALSE);
+               gtk_text_view_set_monospace (GTK_TEXT_VIEW (content), TRUE);
+               if (!plain_text)
+                       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (content), GTK_WRAP_WORD_CHAR);
        } else {
                content = webkit_web_view_new ();
-               webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), html ? html : "", "evo-file://");
+               webkit_web_view_load_html (WEBKIT_WEB_VIEW (content), content_text ? content_text : "", 
"evo-file://");
        }
-       g_free (html);
 
        gtk_container_add (GTK_CONTAINER (scrolled_window), content);
        gtk_widget_show_all (scrolled_window);
@@ -210,6 +202,94 @@ view_source_dialog (EHTMLEditor *editor,
        gtk_widget_destroy (dialog);
 }
 
+typedef struct _ViewSourceData {
+       EHTMLEditor *editor;
+       gchar *title;
+       guint mode;
+       gboolean show_source;
+} ViewSourceData;
+
+static ViewSourceData *
+view_source_data_new (EHTMLEditor *editor,
+                     const gchar *title,
+                     guint mode,
+                     gboolean show_source)
+{
+       ViewSourceData *vsd;
+
+       vsd = g_slice_new (ViewSourceData);
+       vsd->editor = g_object_ref (editor);
+       vsd->title = g_strdup (title);
+       vsd->mode = mode;
+       vsd->show_source = show_source;
+
+       return vsd;
+}
+
+static void
+view_source_data_free (gpointer ptr)
+{
+       ViewSourceData *vsd = ptr;
+
+       if (vsd) {
+               g_clear_object (&vsd->editor);
+               g_free (vsd->title);
+               g_slice_free (ViewSourceData, vsd);
+       }
+}
+
+static void
+view_source_dialog_content_hash_ready_cb (GObject *source_object,
+                                         GAsyncResult *result,
+                                         gpointer user_data)
+{
+       ViewSourceData *vcd = user_data;
+       EContentEditorContentHash *content_hash;
+       GError *error = NULL;
+
+       g_return_if_fail (vcd != NULL);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
+
+       if (!content_hash) {
+               g_warning ("%s: Failed to get content: %s", G_STRFUNC, error ? error->message : "Unknown 
error");
+       } else {
+               view_source_dialog_show (vcd->editor, vcd->title, vcd->mode == GET_CONTENT_PLAIN, 
vcd->show_source,
+                       e_content_editor_util_get_content_data (content_hash,
+                               vcd->mode == GET_CONTENT_PLAIN ? E_CONTENT_EDITOR_GET_TO_SEND_PLAIN :
+                               vcd->mode == GET_CONTENT_HTML ? E_CONTENT_EDITOR_GET_TO_SEND_HTML :
+                               E_CONTENT_EDITOR_GET_RAW_DRAFT));
+
+               e_content_editor_util_free_content_hash (content_hash);
+       }
+
+       view_source_data_free (vcd);
+       g_clear_error (&error);
+}
+
+static void
+view_source_dialog (EHTMLEditor *editor,
+                    const gchar *title,
+                    guint mode,
+                    gboolean show_source)
+{
+       EContentEditor *cnt_editor;
+       ViewSourceData *vcd;
+       guint32 flags;
+
+       vcd = view_source_data_new (editor, title, mode, show_source);
+
+       cnt_editor = e_html_editor_get_content_editor (editor);
+
+       flags = mode == GET_CONTENT_PLAIN ? E_CONTENT_EDITOR_GET_TO_SEND_PLAIN :
+               mode == GET_CONTENT_HTML ? E_CONTENT_EDITOR_GET_TO_SEND_HTML | 
E_CONTENT_EDITOR_GET_INLINE_IMAGES :
+               E_CONTENT_EDITOR_GET_ALL;
+
+       e_content_editor_get_content (cnt_editor, flags, "test-domain", NULL,
+               view_source_dialog_content_hash_ready_cb, vcd);
+}
+
 static void
 action_new_editor_cb (GtkAction *action,
                      EHTMLEditor *editor)
@@ -240,13 +320,24 @@ action_quit_cb (GtkAction *action,
        gtk_main_quit ();
 }
 
+static void
+html_editor_save_done_cb (GObject *source_object,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+       GError *error = NULL;
+
+       e_html_editor_save_finish (E_HTML_EDITOR (source_object), result, &error);
+
+       handle_error (&error);
+}
+
 static void
 action_save_cb (GtkAction *action,
                 EHTMLEditor *editor)
 {
        const gchar *filename;
        gboolean as_html;
-       GError *error = NULL;
 
        if (e_html_editor_get_filename (editor) == NULL)
                if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
@@ -255,8 +346,7 @@ action_save_cb (GtkAction *action,
        filename = e_html_editor_get_filename (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);
+       e_html_editor_save (editor, filename, as_html, NULL, html_editor_save_done_cb, NULL);
 }
 
 static void
@@ -265,7 +355,6 @@ action_save_as_cb (GtkAction *action,
 {
        const gchar *filename;
        gboolean as_html;
-       GError *error = NULL;
 
        if (save_dialog (editor) == GTK_RESPONSE_CANCEL)
                return;
@@ -273,8 +362,7 @@ action_save_as_cb (GtkAction *action,
        filename = e_html_editor_get_filename (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);
+       e_html_editor_save (editor, filename, as_html, NULL, html_editor_save_done_cb, NULL);
 }
 
 static void
@@ -291,21 +379,28 @@ static void
 action_view_html_output (GtkAction *action,
                          EHTMLEditor *editor)
 {
-       view_source_dialog (editor, _("HTML Output"), FALSE, FALSE);
+       view_source_dialog (editor, _("HTML Output"), GET_CONTENT_HTML, FALSE);
 }
 
 static void
 action_view_html_source (GtkAction *action,
                          EHTMLEditor *editor)
 {
-       view_source_dialog (editor, _("HTML Source"), FALSE, TRUE);
+       view_source_dialog (editor, _("HTML Source"), GET_CONTENT_HTML, TRUE);
 }
 
 static void
 action_view_plain_source (GtkAction *action,
                           EHTMLEditor *editor)
 {
-       view_source_dialog (editor, _("Plain Source"), TRUE, FALSE);
+       view_source_dialog (editor, _("Plain Source"), GET_CONTENT_PLAIN, FALSE);
+}
+
+static void
+action_view_draft_source (GtkAction *action,
+                         EHTMLEditor *editor)
+{
+       view_source_dialog (editor, _("Draft"), GET_CONTENT_DRAFT, TRUE);
 }
 
 static void
@@ -407,6 +502,13 @@ static GtkActionEntry view_entries[] = {
          NULL,
          G_CALLBACK (action_view_plain_source) },
 
+       { "view-draft-source",
+         NULL,
+         N_("_Draft Source"),
+         NULL,
+         NULL,
+         G_CALLBACK (action_view_draft_source) },
+
        { "view-webkit-inspector",
          NULL,
          N_("Inspector"),
@@ -423,6 +525,7 @@ static GtkActionEntry view_entries[] = {
 };
 
 static guint glob_editors = 0;
+static const gchar *glob_prefill_body = NULL;
 
 static void
 editor_destroyed_cb (GtkWidget *editor)
@@ -446,6 +549,7 @@ create_new_editor_cb (GObject *source_object,
        GtkWidget *widget;
        EHTMLEditor *editor;
        EContentEditor *cnt_editor;
+       EFocusTracker *focus_tracker;
        GError *error = NULL;
 
        widget = e_html_editor_new_finish (result, &error);
@@ -485,6 +589,27 @@ create_new_editor_cb (GObject *source_object,
        gtk_widget_set_size_request (widget, 600, 400);
        gtk_widget_show (widget);
 
+       focus_tracker = e_focus_tracker_new (GTK_WINDOW (widget));
+       g_object_set_data_full (G_OBJECT (widget), "e-focus-tracker", focus_tracker, g_object_unref);
+
+       e_focus_tracker_set_cut_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "cut"));
+
+       e_focus_tracker_set_copy_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "copy"));
+
+       e_focus_tracker_set_paste_clipboard_action (focus_tracker,
+               e_html_editor_get_action (editor, "paste"));
+
+       e_focus_tracker_set_select_all_action (focus_tracker,
+               e_html_editor_get_action (editor, "select-all"));
+
+       e_focus_tracker_set_undo_action (focus_tracker,
+               e_html_editor_get_action (editor, "undo"));
+
+       e_focus_tracker_set_redo_action (focus_tracker,
+               e_html_editor_get_action (editor, "redo"));
+
        g_signal_connect_swapped (
                widget, "destroy",
                G_CALLBACK (editor_destroyed_cb), NULL);
@@ -546,6 +671,13 @@ create_new_editor_cb (GObject *source_object,
        }
 
        gtk_ui_manager_ensure_update (manager);
+
+       if (glob_prefill_body) {
+               e_content_editor_insert_content (cnt_editor, glob_prefill_body, 
E_CONTENT_EDITOR_INSERT_REPLACE_ALL |
+                       (*glob_prefill_body == '<' ? E_CONTENT_EDITOR_INSERT_TEXT_HTML : 
E_CONTENT_EDITOR_INSERT_TEXT_PLAIN));
+
+               glob_prefill_body = NULL;
+       }
 }
 
 static void
@@ -571,9 +703,16 @@ main (gint argc,
        e_util_init_main_thread (NULL);
        e_passwords_init ();
 
+       g_setenv ("E_HTML_EDITOR_TEST_SOURCES", "1", FALSE);
+
+       gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), EVOLUTION_ICONDIR);
+
        modules = e_module_load_all_in_directory (EVOLUTION_MODULEDIR);
        g_list_free_full (modules, (GDestroyNotify) g_type_module_unuse);
 
+       if (argc == 2)
+               glob_prefill_body = argv[1];
+
        create_new_editor ();
 
        gtk_main ();
diff --git a/src/e-util/test-web-view-jsc.c b/src/e-util/test-web-view-jsc.c
index 33780da89a..dab6108109 100644
--- a/src/e-util/test-web-view-jsc.c
+++ b/src/e-util/test-web-view-jsc.c
@@ -148,6 +148,7 @@ test_utils_fixture_set_up (TestFixture *fixture,
 
        settings = webkit_web_view_get_settings (fixture->web_view);
        webkit_settings_set_enable_developer_extras (settings, TRUE);
+       webkit_settings_set_enable_write_console_messages_to_stdout (settings, TRUE);
 
        g_signal_connect (
                fixture->window, "key-press-event",
@@ -1419,6 +1420,7 @@ test_selection (TestFixture *fixture)
                "<font color=\"red\">R</font>"
                "<font color=\"green\">G</font>"
                "<font color=\"blue\">B</font>"
+               "<span id=\"rgb-end\"></span>"
                "</div>"
                "<div id=\"styled\">"
                "<span style=\"color:blue;\">bb</span>"
@@ -1448,16 +1450,23 @@ test_selection (TestFixture *fixture)
        test_selection_select_in_iframe (fixture, "frm1", "plain", "rgb");
 
        g_assert_cmpint (e_web_view_has_selection (E_WEB_VIEW (fixture->web_view)) ? 1 : 0, ==, 1);
-       test_selection_verify (fixture, "unformatted text\n", NULL);
+       test_selection_verify (fixture, "unformatted text\n\n", NULL);
        test_selection_verify (fixture, NULL, "<div id=\"plain\">unformatted text</div><br><div 
id=\"rgb\"></div>");
-       test_selection_verify (fixture, "unformatted text\n", "<div id=\"plain\">unformatted 
text</div><br><div id=\"rgb\"></div>");
+       test_selection_verify (fixture, "unformatted text\n\n", "<div id=\"plain\">unformatted 
text</div><br><div id=\"rgb\"></div>");
 
        test_selection_select_in_iframe (fixture, "frm1", "rgb", "styled");
 
+       g_assert_cmpint (e_web_view_has_selection (E_WEB_VIEW (fixture->web_view)) ? 1 : 0, ==, 1);
+       test_selection_verify (fixture, "RGB\n", NULL);
+       test_selection_verify (fixture, NULL, "<div id=\"rgb\"><font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font><span id=\"rgb-end\"></span></div><div 
id=\"styled\"></div>");
+       test_selection_verify (fixture, "RGB\n", "<div id=\"rgb\"><font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font><span id=\"rgb-end\"></span></div><div 
id=\"styled\"></div>");
+
+       test_selection_select_in_iframe (fixture, "frm1", "rgb", "rgb-end");
+
        g_assert_cmpint (e_web_view_has_selection (E_WEB_VIEW (fixture->web_view)) ? 1 : 0, ==, 1);
        test_selection_verify (fixture, "RGB", NULL);
-       test_selection_verify (fixture, NULL, "<div id=\"rgb\"><font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font></div><div id=\"styled\"></div>");
-       test_selection_verify (fixture, "RGB", "<div id=\"rgb\"><font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font></div><div id=\"styled\"></div>");
+       test_selection_verify (fixture, NULL, "<font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font>");
+       test_selection_verify (fixture, "RGB", "<font color=\"red\">R</font><font 
color=\"green\">G</font><font color=\"blue\">B</font>");
 
        test_selection_select_in_iframe (fixture, "frm1", "styled", "end");
 
@@ -1610,7 +1619,7 @@ test_get_content (TestFixture *fixture)
        test_get_document_content_verify (fixture, "", NULL, expect_html);
        test_get_document_content_verify (fixture, "", expect_plain, expect_html);
 
-       expect_plain = "frm1 div";
+       expect_plain = "frm1 div\n";
        expect_html = html_frm1;
        test_get_document_content_verify (fixture, "frm1", expect_plain, NULL);
        test_get_document_content_verify (fixture, "frm1", NULL, expect_html);
@@ -1654,7 +1663,7 @@ test_get_content (TestFixture *fixture)
        test_get_element_content_verify (fixture, "", "*body", TRUE, NULL, expect_html);
        test_get_element_content_verify (fixture, "", "*body", TRUE, expect_plain, expect_html);
 
-       expect_plain = "frm1 div";
+       expect_plain = "frm1 div\n";
        expect_html ="<span id=\"frm1p\"><div id=\"frst\">frm1 div</div></span>";
        test_get_element_content_verify (fixture, "frm1", "*body", FALSE, expect_plain, NULL);
        test_get_element_content_verify (fixture, "frm1", "*body", FALSE, NULL, expect_html);
@@ -1687,6 +1696,7 @@ test_get_content (TestFixture *fixture)
        test_get_element_content_verify (fixture, "frm1", "frst", TRUE, NULL, expect_html);
        test_get_element_content_verify (fixture, "frm1", "frst", TRUE, expect_plain, expect_html);
 
+       expect_plain = "frm1 div\n";
        test_get_element_content_verify (fixture, "frm1", "frm1p", FALSE, expect_plain, NULL);
        test_get_element_content_verify (fixture, "frm1", "frm1p", FALSE, NULL, expect_html);
        test_get_element_content_verify (fixture, "frm1", "frm1p", FALSE, expect_plain, expect_html);
@@ -1922,6 +1932,1005 @@ test_get_element_from_point (TestFixture *fixture)
        g_assert_cmpint (tested, >, 0);
 }
 
+static void
+test_convert_to_plain (TestFixture *fixture)
+{
+       #define HTML(_body) ("<html><head><style><!-- span.Apple-tab-span { white-space:pre; } 
--></style></head><body style='font-family:monospace;'>" _body "</body></html>")
+       #define TAB "<span class='Apple-tab-span' style='white-space:pre;'>\t</span>"
+       #define BREAK_STYLE " word-break:break-word; word-wrap:break-word; line-break:after-white-space;"
+       #define WRAP_STYLE(_type) " white-space:" _type "; " BREAK_STYLE
+       #define ALIGN_STYLE(_type) " text-align:" _type ";"
+       #define INDENT_STYLE(_type, _val) " margin-" _type ":" _val "ch;"
+       #define DIR_STYLE(_type) " direction:" _type ";"
+
+       struct _tests {
+               const gchar *html;
+               const gchar *plain;
+               gint normal_div_width;
+       } tests[] = {
+       /* 0 */ { HTML ("<div style='width:10ch; " BREAK_STYLE "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678\n0123 5678\n0123 678\n1234567890\nabcdefghij\nklmnopq\n012345 356\n9\n0\n",
+                 -1 },
+       /* 1 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("normal") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678\n0123 5678\n0123 678\n1234567890\nabcdefghij\nklmnopq\n012345 356\n9\n0\n",
+                 -1 },
+       /* 2 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("nowrap") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678 0123 5678 0123 678 1234567890abcdefghijklmnopq 012345 356 9 0\n",
+                 -1 },
+       /* 3 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("pre") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678 0\n123 5678 0\n123  678 1\n234567890a\nbcdefghijk\nlmnopq 012\n345       \n  356  
9  \n       \t\t\n0\n",
+                 -1 },
+       /* 4 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("pre") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9          " TAB TAB "0</div>"),
+                 "123 5678 0\n123 5678 0\n123  678 1\n234567890a\nbcdefghijk\nlmnopq 012\n345       \n  356  
9  \n        \t\n\t0\n",
+                 -1 },
+       /* 5 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("pre-line") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678\n0123 5678\n0123 678\n1234567890\nabcdefghij\nklmnopq\n012345 356\n9\n0\n",
+                 -1 },
+       /* 6 */ { HTML ("<div style='width:10ch; " WRAP_STYLE ("pre-wrap") "'>123 5678 0123 5678 0123  678 
1234567890abcdefghijklmnopq 012345         356  9         " TAB TAB "0</div>"),
+                 "123 5678\n0123 5678\n0123  678\n1234567890\nabcdefghij\nklmnopq\n012345   \n356  9   
\n\t\n\t0\n",
+                 -1 },
+       /* 7 */ { HTML ("<pre>123456789012345\n1\t90123\n123   78901\n  34567   <br>123 5</pre>"),
+                 "123456789012345\n1\t90123\n123   78901\n  34567   \n123 5\n",
+                 -1 },
+       /* 8 */ { HTML ("<pre>123456789012345\n1\t90123\n123   78901\n  34567   <br>123 5</pre>"),
+                 "123456789012345\n1\t90123\n123   78901\n  34567   \n123 5\n",
+                 10 },
+       /* 9 */ { HTML ("<h1>Header1</h1>"
+                       "<div style='width:10ch; " WRAP_STYLE ("normal") "'>123456 789 123 4567890 123456 789 
122</div>"
+                       "<div style='width:10ch; " WRAP_STYLE ("normal") "'>987654321 987 654 321 
12345678901234567890</div>"),
+                 "Header1\n"
+                 "123456 789\n123\n4567890\n123456 789\n122\n"
+                 "987654321\n987 654\n321\n1234567890\n1234567890\n",
+                 -1 },
+       /* 10 */{ HTML ("<h1>Header1</h1>"
+                       "<div style='width:10ch; " WRAP_STYLE ("pre-wrap") "'>123456 789 123 4567890 123456 
789 122</div>"
+                       "<div style='width:10ch; " WRAP_STYLE ("pre-wrap") "'>987654321 987 654 321 
12345678901234567890</div>"),
+                 "Header1\n"
+                 "123456 789\n123\n4567890\n123456 789\n122\n"
+                 "987654321\n987 654\n321\n1234567890\n1234567890\n",
+                 -1 },
+       /* 11 */{ HTML ("<h1>H1</h1><h2>H2</h2><h3>H3</h3><h4>H4</h4><h5>H5</h5><h6>H6</h6>"),
+                 "H1\nH2\nH3\nH4\nH5\nH6\n",
+                 -1 },
+       /* 12 */{ HTML ("<address>Line 1<br>Line 2<br>Line 3 ...</address>"),
+                 "Line 1\nLine 2\nLine 3 ...\n",
+                 -1 },
+       /* 13 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1 2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1 2 3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1 2 3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1 2 3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>1 2 3 4 5 6</div>"),
+                 "1\n1 2\n1 2 3\n1 2 3 4\n1 2 3 4 5\n1 2 3 4 5\n6\n",
+                 -1 },
+       /* 14 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1 2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1 2 3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1 2 3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1 2 3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>1 2 3 4 5 6</div>"),
+                 "    1\n   1 2\n  1 2 3\n 1 2 3 4\n1 2 3 4 5\n1 2 3 4 5\n    6\n",
+                 -1 },
+       /* 15 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1 2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1 2 3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1 2 3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1 2 3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>1 2 3 4 5 6</div>"),
+                 "         1\n"
+                 "       1 2\n"
+                 "     1 2 3\n"
+                 "   1 2 3 4\n"
+                 " 1 2 3 4 5\n"
+                 " 1 2 3 4 5\n"
+                 "         6\n",
+                 -1 },
+       /* 16 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 aaaaaaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 2 aaaaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 2 3 aaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 2 3 4 aaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 2 3 4 5 a</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>1 2 3 4 5 6 
7</div>"),
+                 "1\n"
+                 "aaaaaaaaa\n"
+                 "1        2\n"
+                 "aaaaaaa\n"
+                 "1    2   3\n"
+                 "aaaaa\n"
+                 "1  2  3  4\n"
+                 "aaa\n"
+                 "1  2 3 4 5\n"
+                 "a\n"
+                 "1  2 3 4 5\n"
+                 "6 7\n",
+                 -1 },
+       /* 17 */{ HTML ("<div style='width:10ch; " BREAK_STYLE "'>123456 789 123 4567890 123456 789 
122</div>"),
+                 "123456 789\n123\n4567890\n123456 789\n122\n",
+                 -1 },
+       /* 18 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") "'>123456 789 123 4567890 
123456 789 122</div>"),
+                 "123456 789\n123\n4567890\n123456 789\n122\n",
+                 -1 },
+       /* 19 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") "'>123456 789 123 
4567890 123456 789 122</div>"),
+                 "123456 789\n"
+                 "   123\n"
+                 " 4567890\n"
+                 "123456 789\n"
+                 "   122\n",
+                 -1 },
+       /* 20 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") "'>123456 789 123 
4567890 123456 789 122</div>"),
+                 "123456 789\n"
+                 "       123\n"
+                 "   4567890\n"
+                 "123456 789\n"
+                 "       122\n",
+                 -1 },
+       /* 21 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") "'>123456 789 1 3 456 
890 1234 789 1 2 3 4 5 122</div>"),
+                 "123456 789\n"
+                 "1   3  456\n"
+                 "890   1234\n"
+                 "789  1 2 3\n"
+                 "4 5 122\n",
+                 -1 },
+       /* 22 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") 
"'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") "'>1 
2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") "'>1 2 
3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") "'>1 2 
3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") "'>1 2 
3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") "'>1 2 
3 4 5 6</div>"),
+                 "1         \n"
+                 "1 2       \n"
+                 "1 2 3     \n"
+                 "1 2 3 4   \n"
+                 "1 2 3 4 5 \n"
+                 "1 2 3 4 5 \n"
+                 "6         \n",
+                 -1 },
+       /* 23 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") 
"'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") "'>1 
2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") "'>1 
2 3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") "'>1 
2 3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") "'>1 
2 3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") "'>1 
2 3 4 5 6</div>"),
+                 "1    \n1 2   \n1 2 3  \n1 2 3 4 \n1 2 3 4 5\n1 2 3 4 5\n6    \n",
+                 -1 },
+       /* 24 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") 
"'>1</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") "'>1 
2</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") "'>1 2 
3</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") "'>1 2 
3 4</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") "'>1 2 
3 4 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") "'>1 2 
3 4 5 6</div>"),
+                 "1\n1 2\n1 2 3\n1 2 3 4\n1 2 3 4 5\n1 2 3 4 5\n6\n",
+                 -1 },
+       /* 25 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
aaaaaaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
2 aaaaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
2 3 aaaaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
2 3 4 aaa</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
2 3 4 5 a</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") "'>1 
2 3 4 5 6 7</div>"),
+                 "1\n"
+                 "aaaaaaaaa\n"
+                 "1        2\n"
+                 "aaaaaaa\n"
+                 "1    2   3\n"
+                 "aaaaa\n"
+                 "1  2  3  4\n"
+                 "aaa\n"
+                 "1  2 3 4 5\n"
+                 "a\n"
+                 "1  2 3 4 5\n"
+                 "6 7\n",
+                 -1 },
+       /* 26 */{ HTML ("<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") "'>123456 789 123 4567890 
123456 789 122</div>"),
+                 "123456 789\n"
+                 "123\n"
+                 "4567890\n"
+                 "123456 789\n"
+                 "122\n",
+                 -1 },
+       /* 27 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") DIR_STYLE ("rtl") 
"'>123456 789 123 4567890 123456 789 122</div>"),
+                 "123456 789\n"
+                 "123       \n"
+                 "4567890   \n"
+                 "123456 789\n"
+                 "122       \n",
+                 -1 },
+       /* 28 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") DIR_STYLE ("rtl") 
"'>123456 789 123 4567890 123456 789 122</div>"),
+                 "123456 789\n"
+                 "123   \n"
+                 "4567890 \n"
+                 "123456 789\n"
+                 "122   \n",
+                 -1 },
+       /* 29 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("right") DIR_STYLE ("rtl") 
"'>123456 789 123 4567890 123456 789 122</div>"),
+                 "123456 789\n123\n4567890\n123456 789\n122\n",
+                 -1 },
+       /* 30 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") DIR_STYLE ("rtl") 
"'>123456 789 1 3 456 890 1234 789 1 2 3 4 5 122</div>"),
+                 "123456 789\n"
+                 "1   3  456\n"
+                 "890   1234\n"
+                 "789  1 2 3\n"
+                 "4 5 122\n",
+                 -1 },
+       /* 31 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") INDENT_STYLE ("left", 
"3") "'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") INDENT_STYLE ("left", 
"6") "'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("left") INDENT_STYLE ("left", 
"3") "'>111 222 333 444 555 666 7</div>"),
+                 "   123 567\n"
+                 "   901 345\n"
+                 "   789 123 5\n"
+                 "      987 543\n"
+                 "      109 765\n"
+                 "      321 098 6\n"
+                 "   111 222\n"
+                 "   333 444\n"
+                 "   555 666 7\n",
+                 -1 },
+       /* 32 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") INDENT_STYLE ("left", 
"3") "'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") INDENT_STYLE ("left", 
"6") "'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("center") INDENT_STYLE ("left", 
"3") "'>111 222 333 444 555 666 7</div>"),
+                 "    123 567\n"
+                 "    901 345\n"
+                 "   789 123 5\n"
+                 "       987 543\n"
+                 "       109 765\n"
+                 "      321 098 6\n"
+                 "    111 222\n"
+                 "    333 444\n"
+                 "   555 666 7\n",
+                 -1 },
+       /* 33 */{ HTML ("<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") INDENT_STYLE ("right", "3") 
"'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") INDENT_STYLE ("right", "6") 
"'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") INDENT_STYLE ("right", "3") 
"'>111 222 333 444 555 666 7</div>"),
+                 "123 567   \n"
+                 "901 345   \n"
+                 "789 123 5   \n"
+                 "987 543      \n"
+                 "109 765      \n"
+                 "321 098 6      \n"
+                 "111 222   \n"
+                 "333 444   \n"
+                 "555 666 7   \n",
+                 -1 },
+       /* 34 */{ HTML ("<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("ltr") ALIGN_STYLE ("right") 
INDENT_STYLE ("left", "3") "'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("ltr") ALIGN_STYLE ("right") 
INDENT_STYLE ("left", "6") "'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("ltr") ALIGN_STYLE ("right") 
INDENT_STYLE ("left", "3") "'>111 222 333 444 555 666 7</div>"),
+                 "   123 567\n"
+                 "   901 345\n"
+                 " 789 123 5\n"
+                 "   987 543\n"
+                 "   109 765\n"
+                 " 321 098 6\n"
+                 "   111 222\n"
+                 "   333 444\n"
+                 " 555 666 7\n",
+                 -1 },
+       /* 35 */{ HTML ("<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") ALIGN_STYLE ("left") 
INDENT_STYLE ("right", "3") "'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") ALIGN_STYLE ("left") 
INDENT_STYLE ("right", "6") "'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE DIR_STYLE ("rtl") ALIGN_STYLE ("left") 
INDENT_STYLE ("right", "3") "'>111 222 333 444 555 666 7</div>"),
+                 "123 567   \n"
+                 "901 345   \n"
+                 "789 123 5 \n"
+                 "987 543   \n"
+                 "109 765   \n"
+                 "321 098 6 \n"
+                 "111 222   \n"
+                 "333 444   \n"
+                 "555 666 7 \n",
+                 -1 },
+       /* 36 */{ HTML ("<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") INDENT_STYLE ("left", 
"3") "'>123 567 901 345 789 123 5</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") INDENT_STYLE ("left", 
"6") "'>987 543 109 765 321 098 6</div>"
+                       "<div style='width:10ch; " BREAK_STYLE ALIGN_STYLE ("justify") INDENT_STYLE ("left", 
"3") "'>111 222 333 444 555 666 7</div>"),
+                 "   123    567\n"
+                 "   901    345\n"
+                 "   789 123 5\n"
+                 "      987    543\n"
+                 "      109    765\n"
+                 "      321 098 6\n"
+                 "   111    222\n"
+                 "   333    444\n"
+                 "   555 666 7\n",
+                 -1 },
+       /* 37 */{ HTML ("<ul style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ul>"),
+                 " * 1 11 111\n"
+                 "   1111 111\n"
+                 "   11 1\n"
+                 " * 2 22 222\n"
+                 "   2222 222\n"
+                 "   22 2\n"
+                 " * 3 33 333\n"
+                 "   3333\n"
+                 "   33333 333\n"
+                 "   33 3\n",
+                 -1 },
+       /* 38 */{ HTML ("<ol style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ol>"),
+                 "   1. 1 11 111\n"
+                 "      1111 111\n"
+                 "      11 1\n"
+                 "   2. 2 22 222\n"
+                 "      2222 222\n"
+                 "      22 2\n"
+                 "   3. 3 33 333\n"
+                 "      3333\n"
+                 "      33333 333\n"
+                 "      33 3\n",
+                 -1 },
+       /* 39 */{ HTML ("<ol type='A' style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ol>"),
+                 "   A. 1 11 111\n"
+                 "      1111 111\n"
+                 "      11 1\n"
+                 "   B. 2 22 222\n"
+                 "      2222 222\n"
+                 "      22 2\n"
+                 "   C. 3 33 333\n"
+                 "      3333\n"
+                 "      33333 333\n"
+                 "      33 3\n",
+                 -1 },
+       /* 40 */{ HTML ("<ol type='a' style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ol>"),
+                 "   a. 1 11 111\n"
+                 "      1111 111\n"
+                 "      11 1\n"
+                 "   b. 2 22 222\n"
+                 "      2222 222\n"
+                 "      22 2\n"
+                 "   c. 3 33 333\n"
+                 "      3333\n"
+                 "      33333 333\n"
+                 "      33 3\n",
+                 -1 },
+       /* 41 */{ HTML ("<ol type='I' style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ol>"),
+                 "   I. 1 11 111\n"
+                 "      1111 111\n"
+                 "      11 1\n"
+                 "  II. 2 22 222\n"
+                 "      2222 222\n"
+                 "      22 2\n"
+                 " III. 3 33 333\n"
+                 "      3333\n"
+                 "      33333 333\n"
+                 "      33 3\n",
+                 -1 },
+       /* 42 */{ HTML ("<ol type='i' style='width: 9ch;'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "</ol>"),
+                 "   i. 1 11 111\n"
+                 "      1111 111\n"
+                 "      11 1\n"
+                 "  ii. 2 22 222\n"
+                 "      2222 222\n"
+                 "      22 2\n"
+                 " iii. 3 33 333\n"
+                 "      3333\n"
+                 "      33333 333\n"
+                 "      33 3\n",
+                 -1 },
+       /* 43 */{ HTML ("<ol type='i' style='width: 9ch;'>"
+                       "<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>"
+                       "<li>19</li>"
+                       "<li>20</li>"
+                       "</ol>"),
+                 "    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\n"
+                 "  xix. 19\n"
+                 "   xx. 20\n",
+                 -1 },
+       /* 44 */{ HTML ("<ol style='width: 9ch; " DIR_STYLE ("rtl") "'>"
+                       "<li>1 11 111 1111 111 11 1</li>"
+                       "<li>2 22 222 2222 222 22 2</li>"
+                       "<li>3 33 333 3333 33333 333 33 3</li>"
+                       "<li>4</li>"
+                       "</ol>"),
+                 "1 11 111 .1   \n"
+                 "1111 111      \n"
+                 "11 1      \n"
+                 "2 22 222 .2   \n"
+                 "2222 222      \n"
+                 "22 2      \n"
+                 "3 33 333 .3   \n"
+                 "3333      \n"
+                 "33333 333      \n"
+                 "33 3      \n"
+                 "4 .4   \n",
+                 -1 },
+       /* 45 */{ HTML ("<ol type='I' style='width: 9ch; " DIR_STYLE ("rtl") "'>"
+                       "<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>"
+                       "<li>19</li>"
+                       "<li>20</li>"
+                       "</ol>"),
+                 "1 .I    \n"
+                 "2 .II   \n"
+                 "3 .III  \n"
+                 "4 .IV   \n"
+                 "5 .V    \n"
+                 "6 .VI   \n"
+                 "7 .VII  \n"
+                 "8 .VIII \n"
+                 "9 .IX   \n"
+                 "10 .X    \n"
+                 "11 .XI   \n"
+                 "12 .XII  \n"
+                 "13 .XIII \n"
+                 "14 .XIV  \n"
+                 "15 .XV   \n"
+                 "16 .XVI  \n"
+                 "17 .XVII \n"
+                 "18 .XVIII\n"
+                 "19 .XIX  \n"
+                 "20 .XX   \n",
+                 -1 },
+       /* 46 */{ HTML ("<ul style='width: 15ch; padding-inline-start: 3ch;'>"
+                       "<li>AA 1 2 3 4 5 6 7 8 9 11 22 33</li>"
+                       "<ul style='width: 12ch; padding-inline-start: 3ch;'>"
+                       "<li>BA 1 2 3 4 5 6 7 8 9</li>"
+                       "<li>BB 1 2 3 4 5 6 7 8 9</li>"
+                       "<ul style='width: 9ch; padding-inline-start: 3ch;'>"
+                       "<li>CA 1 2 3 4 5 6</li>"
+                       "<li>CB 1 2 3 4 5 6</li>"
+                       "</ul>"
+                       "<li>BC 1 2 3 4 5 6</li>"
+                       "</ul>"
+                       "<li>AB 1 2 3 4 5 6 7</li>"
+                       "</ul>"),
+                 " * AA 1 2 3 4 5 6\n"
+                 "   7 8 9 11 22 33\n"
+                 "    - BA 1 2 3 4 5\n"
+                 "      6 7 8 9\n"
+                 "    - BB 1 2 3 4 5\n"
+                 "      6 7 8 9\n"
+                 "       + CA 1 2 3\n"
+                 "         4 5 6\n"
+                 "       + CB 1 2 3\n"
+                 "         4 5 6\n"
+                 "    - BC 1 2 3 4 5\n"
+                 "      6\n"
+                 " * AB 1 2 3 4 5 6\n"
+                 "   7\n",
+                 -1 },
+       /* 47 */{ HTML ("<ol>"
+                         "<li>1</li>"
+                         "<ul>"
+                           "<li>1.-</li>"
+                           "<ol type='i'>"
+                             "<li>1.-.i</li>"
+                             "<ol type='a'>"
+                               "<li>1.-.i.a</li>"
+                               "<li>1.-.i.b</li>"
+                             "</ol>"
+                             "<li>1.-.ii</li>"
+                             "<ol type='A'>"
+                               "<li>1.-.ii.A</li>"
+                               "<ul>"
+                                 "<li>1.-.ii.A.-</li>"
+                                 "<ul>"
+                                   "<li>1.-.ii.A.-.+</li>"
+                                   "<ol type='I'>"
+                                      "<li>1.-.ii.A.-.+.I</li>"
+                                      "<li>1.-.ii.A.-.+.II</li>"
+                                      "<li>1.-.ii.A.-.+.III</li>"
+                                   "</ol>"
+                                   "<li>1.-.ii.A.-.+</li>"
+                                 "</ul>"
+                                 "<li>1.-.ii.A.-</li>"
+                               "</ul>"
+                               "<li>1.-.ii.B</li>"
+                             "</ol>"
+                             "<li>1.-.iii</li>"
+                           "</ol>"
+                           "<li>1.-</li>"
+                         "</ul>"
+                         "<li>2</li>"
+                       "</ol>"),
+                 "   1. 1\n"
+                 "       - 1.-\n"
+                 "            i. 1.-.i\n"
+                 "                  a. 1.-.i.a\n"
+                 "                  b. 1.-.i.b\n"
+                 "           ii. 1.-.ii\n"
+                 "                  A. 1.-.ii.A\n"
+                 "                      - 1.-.ii.A.-\n"
+                 "                         + 1.-.ii.A.-.+\n"
+                 "                              I. 1.-.ii.A.-.+.I\n"
+                 "                             II. 1.-.ii.A.-.+.II\n"
+                 "                            III. 1.-.ii.A.-.+.III\n"
+                 "                         + 1.-.ii.A.-.+\n"
+                 "                      - 1.-.ii.A.-\n"
+                 "                  B. 1.-.ii.B\n"
+                 "          iii. 1.-.iii\n"
+                 "       - 1.-\n"
+                 "   2. 2\n",
+                 -1 },
+       /* 48 */{ HTML ("<div style='width:10ch'>123456789 1234567890123456789 12345678901234567890 
123456789012345678901</div>"),
+               "123456789\n"
+               "1234567890\n"
+               "123456789\n"
+               "1234567890\n"
+               "1234567890\n"
+               "1234567890\n"
+               "1234567890\n"
+               "1\n",
+               10 },
+       /* 49 */{ HTML ("<div style='width:70ch'>before <img src='https://no.where/img.img'> after</div>"),
+               "before after\n",
+               70 },
+       /* 50 */{ HTML ("<div style='width:70ch'>before <img src='https://no.where/img.img' alt='alt'> 
after</div>"),
+               "before alt after\n",
+               70 },
+       /* 51 */{ HTML ("<div style='width:70ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"),
+               "before https://no.where/ after\n",
+               70 },
+       /* 52 */{ HTML ("<div style='width:70ch'>before <a href='https://no.where/'>here</a> after</div>"),
+               "before here after\n",
+               70 },
+       /* 53 */{ HTML ("<div style='width:31ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"),
+               "before https://no.where/ after\n",
+               31 },
+       /* 54 */{ HTML ("<div style='width:26ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"),
+               "before https://no.where/\n";
+               "after\n",
+               26 },
+       /* 55 */{ HTML ("<div style='width:20ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"),
+               "before\n"
+               "https://no.where/\n";
+               "after\n",
+               20 },
+       /* 56 */{ HTML ("<div style='width:20ch'>before <a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a> after</div>"),
+               "before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "after\n",
+               20 },
+       /* 57 */{ HTML ("<p><div style='width:20ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div></p>"),
+               "before\n"
+               "https://no.where/\n";
+               "after\n",
+               20 },
+       /* 58 */{ HTML ("<p><div style='width:20ch'>before <a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a> after</div></p>"),
+               "before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "after\n",
+               20 },
+       /* 59 */{ HTML ("<div style='width:16ch'>before <a href='https://no.where/'>anchor text</a> 
after</div>"),
+               "before anchor\n"
+               "text after\n",
+               16 },
+       /* 60 */{ HTML ("<div>text before<br class=\"-x-evo-wrap-br\"><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br 
class=\"-x-evo-wrap-br\">text after</div>"),
+               "text\n"
+               "before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text\n"
+               "after\n",
+               6 },
+       /* 61 */{ HTML ("<div>text before<br class=\"-x-evo-wrap-br\"><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br 
class=\"-x-evo-wrap-br\">text after</div>"),
+               "text\n"
+               "before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text\n"
+               "after\n",
+               9 },
+       /* 62 */{ HTML ("<div>text before<br class=\"-x-evo-wrap-br\"><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br 
class=\"-x-evo-wrap-br\">text after</div>"),
+               "text\n"
+               "before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text after\n",
+               10 },
+       /* 63 */{ HTML ("<div>text before<br class=\"-x-evo-wrap-br\"><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br 
class=\"-x-evo-wrap-br\">text after</div>"),
+               "text before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text after\n",
+               11 },
+       /* 64 */{ HTML ("<div>text before<br class=\"-x-evo-wrap-br\"><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br 
class=\"-x-evo-wrap-br\">text after</div>"),
+               "text before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text after\n",
+               12 },
+       /* 65 */{ HTML ("<div>text before<br><a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br>text after</div>"),
+               "text before\n"
+               "https://no.where/1234567890/123457890/1234567890\n";
+               "text after\n",
+               12 },
+       /* 66 */{ HTML ("<div>line1<br>\n"
+               "line2<br>\n"
+               "line3<br>\n"
+               "<br>\n"
+               "<br>\n"
+               "line6<br>\n"
+               "</div>"),
+               "line1\n"
+               "line2\n"
+               "line3\n"
+               "\n"
+               "\n"
+               "line6\n",
+               71 }
+       };
+
+       #undef HTML
+       #undef TAB
+       #undef BREAK_STYLE
+       #undef WRAP_STYLE
+       #undef ALIGN_STYLE
+       #undef INDENT_STYLE
+       #undef DIR_STYLE
+
+       gchar *script;
+       gint ii;
+
+       for (ii = 0; ii < G_N_ELEMENTS (tests); ii++) {
+               test_utils_load_string (fixture, tests[ii].html);
+
+               script = e_web_view_jsc_printf_script ("EvoConvert.ToPlainText(document.body, %d);", 
tests[ii].normal_div_width);
+
+               test_utils_jsc_call_string_and_verify (fixture, script, tests[ii].plain);
+
+               g_free (script);
+       }
+}
+
+static void
+test_convert_to_plain_quoted (TestFixture *fixture)
+{
+       #define HTML(_body) ("<html><head><style><!-- span.Apple-tab-span { white-space:pre; } 
--></style></head><body style='font-family:monospace;'>" _body "</body></html>")
+       #define QUOTE_SPAN(x) "<span class='-x-evo-quoted'>" x "</span>"
+       #define QUOTE_CHR "<span class='-x-evo-quote-character'>&gt; </span>"
+
+       struct _tests {
+               const gchar *html;
+               const gchar *plain;
+               gint normal_div_width;
+       } tests[] = {
+       /* 0 */ { HTML ("<div style='width:10ch;'>123 456 789 123</div>"
+               "<blockquote type='cite'>"
+                       "<div style='width:8ch;'>123 456 789 1 2 3 4</div>"
+                       "<div style='width:8ch;'>abc def ghi j k l m</div>"
+               "</blockquote>"
+               "<div>end</div>"),
+               "123 456\n"
+               "789 123\n"
+               "> 123 456\n"
+               "> 789 1 2\n"
+               "> 3 4\n"
+               "> abc def\n"
+               "> ghi j k\n"
+               "> l m\n"
+               "end\n",
+               10 },
+       /* 1 */ { HTML ("<div style='width:12ch;'>123 456</div>"
+               "<blockquote type='cite'>"
+                       "<div style='width:10ch;'>123 456</div>"
+                       "<blockquote>"
+                               "<div style='width:8ch;'>789 1 2 3 4</div>"
+                       "</blockquote>"
+                       "<div style='width:10ch;'>mid</div>"
+                       "<blockquote>"
+                               "<blockquote>"
+                                       "<div style='width:6ch;'>abc</div>"
+                                       "<div style='width:6ch;'>def ghi j k l m</div>"
+                               "</blockquote>"
+                               "<div style='width:8ch;'>abc d e f g h i j</div>"
+                       "</blockquote>"
+                       "<div style='width:10ch;'>l1 a b c d e f g</div>"
+               "</blockquote>"
+               "<div>end</div>"),
+               "123 456\n"
+               "> 123 456\n"
+               "> > 789 1 2\n"
+               "> > 3 4\n"
+               "> mid\n"
+               "> > > abc\n"
+               "> > > def\n"
+               "> > > ghi j\n"
+               "> > > k l m\n"
+               "> > abc d e\n"
+               "> > f g h i\n"
+               "> > j\n"
+               "> l1 a b c d\n"
+               "> e f g\n"
+               "end\n",
+               10 },
+       /* 2 */ { HTML ("<div style='width:10ch;'>123 456<br>789 123</div>"
+               "<blockquote type='cite'>"
+                       "<div style='width:8ch;'>123 456<br>789 1 2 3 4</div>"
+                       "<blockquote type='cite'>"
+                               "<div style='width:6ch;'>abc<br>def g h i j k</div>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<div>end</div>"),
+               "123 456\n"
+               "789 123\n"
+               "> 123 456\n"
+               "> 789 1 2\n"
+               "> 3 4\n"
+               "> > abc\n"
+               "> > def g\n"
+               "> > h i j\n"
+               "> > k\n"
+               "end\n",
+               10 },
+       /* 3 */ { HTML ("<p style='width:10ch;'>123 456<br>789 123</p>"
+               "<blockquote type='cite'>"
+                       "<p style='width:8ch;'>123 456<br>789 1 2 3 4</p>"
+                       "<blockquote type='cite'>"
+                               "<p style='width:6ch;'>abc<br>def g h i j k</p>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<p>end</p>"),
+               "123 456\n"
+               "789 123\n"
+               "> 123 456\n"
+               "> 789 1 2\n"
+               "> 3 4\n"
+               "> > abc\n"
+               "> > def g\n"
+               "> > h i j\n"
+               "> > k\n"
+               "end\n",
+               10 },
+       /* 4 */ { HTML ("<pre>123 456 789 123</pre>"
+               "<blockquote type='cite'>"
+                       "<pre>123 456 789 1 2 3 4</pre>"
+                       "<blockquote type='cite'>"
+                               "<pre>abc def g h i j k</pre>"
+                       "</blockquote>"
+               "</blockquote>"
+               "<pre>end</pre>"),
+               "123 456 789 123\n"
+               "> 123 456 789 1 2 3 4\n"
+               "> > abc def g h i j k\n"
+               "end\n",
+               10 },
+       /* 5 */ { HTML ("<pre>123 456\n789 123</pre>"
+               "<blockquote type='cite'>"
+                       "<pre>123 456\n789 1 2 3 4</pre>"
+                       "<blockquote type='cite'>"
+                               "<pre>abc def\ng h\ni j k</pre>"
+                       "</blockquote>"
+                       "<pre>a b\nc\nd e f</pre>"
+               "</blockquote>"
+               "<pre>end</pre>"),
+               "123 456\n"
+               "789 123\n"
+               "> 123 456\n"
+               "> 789 1 2 3 4\n"
+               "> > abc def\n"
+               "> > g h\n"
+               "> > i j k\n"
+               "> a b\n"
+               "> c\n"
+               "> d e f\n"
+               "end\n",
+               10 },
+       /* 6 */ { HTML ("<blockquote type='cite'>"
+                       "<div style='width:70ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"
+               "</blockquote>"),
+               "> before https://no.where/ after\n",
+               70 },
+       /* 7 */ { HTML ("<blockquote type='cite'>"
+                       "<div style='width:70ch'>before <a href='https://no.where/'>here</a> after</div>"
+               "</blockquote>"),
+               "> before here after\n",
+               70 },
+       /* 8 */ { HTML ("<blockquote type='cite'>"
+                       "<div style='width:31ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"
+               "</blockquote>"),
+               "> before https://no.where/ after\n",
+               33 },
+       /* 9 */ { HTML ("<blockquote type='cite'>"
+                       "<div style='width:26ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"
+               "</blockquote>"),
+               "> before https://no.where/\n";
+               "> after\n",
+               26 },
+       /* 10 */{ HTML ("<blockquote type='cite'>"
+                       "<div style='width:20ch'>before <a href='https://no.where/'>https://no.where/</a> 
after</div>"
+               "</blockquote>"),
+               "> before\n"
+               "> https://no.where/\n";
+               "> after\n",
+               20 },
+       /* 11 */{ HTML ("<blockquote type='cite'>"
+                       "<div style='width:20ch'>before <a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a> after</div>"
+               "</blockquote>"),
+               "> before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> after\n",
+               20 },
+       /* 12 */{ HTML ("<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div style='width:20ch'>before <a 
href='https://no.where/'>https://no.where/</a> after</div>"
+                       "</blockquote>"
+               "</blockquote>"),
+               "> > before\n"
+               "> > https://no.where/\n";
+               "> > after\n",
+               20 },
+       /* 13 */{ HTML ("<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div style='width:20ch'>before <a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a> after</div>"
+                       "</blockquote>"
+               "</blockquote>"),
+               "> > before\n"
+               "> > https://no.where/1234567890/123457890/1234567890\n";
+               "> > after\n",
+               20 },
+       /* 14 */{ HTML ("<blockquote type='cite'>"
+                       "<blockquote type='cite'>"
+                               "<div style='width:16ch'>before <a href='https://no.where/'>anchor text</a> 
after</div>"
+                       "</blockquote>"
+               "</blockquote>"),
+               "> > before anchor\n"
+               "> > text after\n",
+               16 },
+       /* 15 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text\n"
+               "> before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text\n"
+               "> after\n",
+               8 },
+       /* 16 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text\n"
+               "> before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text\n"
+               "> after\n",
+               11 },
+       /* 17 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text\n"
+               "> before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text after\n",
+               12 },
+       /* 18 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text after\n",
+               13 },
+       /* 19 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br class=\"-x-evo-wrap-br\">"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text after\n",
+               14 },
+       /* 20 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "text before<br>"
+                       QUOTE_SPAN (QUOTE_CHR) "<a 
href='https://no.where/'>https://no.where/1234567890/123457890/1234567890</a><br>"
+                       QUOTE_SPAN (QUOTE_CHR) "text after</div>"
+               "</blockquote>"),
+               "> text before\n"
+               "> https://no.where/1234567890/123457890/1234567890\n";
+               "> text after\n",
+               14 },
+       /* 21 */{ HTML ("<blockquote type='cite'>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "line1<br>\n"
+                       QUOTE_SPAN (QUOTE_CHR) "line2<br>\n"
+                       QUOTE_SPAN (QUOTE_CHR) "line3<br>\n"
+                       QUOTE_SPAN (QUOTE_CHR) "<br>\n"
+                       QUOTE_SPAN (QUOTE_CHR) "<br>\n"
+                       QUOTE_SPAN (QUOTE_CHR) "line6<br>\n"
+                       "</div>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "paragraph 2<br>\n</div>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "paragraph 3\n</div>"
+                       "<div>" QUOTE_SPAN (QUOTE_CHR) "paragraph 4</div>"
+               "</blockquote>"),
+               "> line1\n"
+               "> line2\n"
+               "> line3\n"
+               "> \n"
+               "> \n"
+               "> line6\n"
+               "> paragraph 2\n"
+               "> paragraph 3\n"
+               "> paragraph 4\n",
+               71 }
+       };
+
+       #undef QUOTE_SPAN
+       #undef QUOTE_CHR
+       #undef HTML
+
+       gchar *script;
+       gint ii;
+
+       for (ii = 0; ii < G_N_ELEMENTS (tests); ii++) {
+               test_utils_load_string (fixture, tests[ii].html);
+
+               script = e_web_view_jsc_printf_script ("EvoConvert.ToPlainText(document.body, %d);", 
tests[ii].normal_div_width);
+
+               test_utils_jsc_call_string_and_verify (fixture, script, tests[ii].plain);
+
+               g_free (script);
+       }
+}
+
 gint
 main (gint argc,
       gchar *argv[])
@@ -1933,6 +2942,8 @@ main (gint argc,
        g_test_init (&argc, &argv, NULL);
        g_test_bug_base ("https://gitlab.gnome.org/GNOME/evolution/issues/";);
 
+       g_setenv ("E_WEB_VIEW_TEST_SOURCES", "1", FALSE);
+
        gtk_init (&argc, &argv);
 
        e_util_init_main_thread (NULL);
@@ -1950,6 +2961,8 @@ main (gint argc,
        test_utils_add_test ("/EWebView/Selection", test_selection);
        test_utils_add_test ("/EWebView/GetContent", test_get_content);
        test_utils_add_test ("/EWebView/GetElementFromPoint", test_get_element_from_point);
+       test_utils_add_test ("/EWebView/ConvertToPlain", test_convert_to_plain);
+       test_utils_add_test ("/EWebView/ConvertToPlainQuoted", test_convert_to_plain_quoted);
 
        res = g_test_run ();
 
diff --git a/src/mail/e-cid-request.c b/src/mail/e-cid-request.c
index 16d58ec73f..ef12b682a1 100644
--- a/src/mail/e-cid-request.c
+++ b/src/mail/e-cid-request.c
@@ -19,9 +19,49 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "e-mail-display.h"
 #include "e-cid-request.h"
 
+G_DEFINE_INTERFACE (ECidResolver, e_cid_resolver, G_TYPE_OBJECT)
+
+static void
+e_cid_resolver_default_init (ECidResolverInterface *iface)
+{
+}
+
+CamelMimePart *
+e_cid_resolver_ref_part (ECidResolver *resolver,
+                        const gchar *uri)
+{
+       ECidResolverInterface *iface;
+
+       g_return_val_if_fail (E_IS_CID_RESOLVER (resolver), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       iface = E_CID_RESOLVER_GET_INTERFACE (resolver);
+       g_return_val_if_fail (iface != NULL, NULL);
+       g_return_val_if_fail (iface->ref_part != NULL, NULL);
+
+       return iface->ref_part (resolver, uri);
+}
+
+gchar *
+e_cid_resolver_dup_mime_type (ECidResolver *resolver,
+                             const gchar *uri)
+{
+       ECidResolverInterface *iface;
+
+       g_return_val_if_fail (E_IS_CID_RESOLVER (resolver), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       iface = E_CID_RESOLVER_GET_INTERFACE (resolver);
+       g_return_val_if_fail (iface != NULL, NULL);
+
+       if (iface->dup_mime_type)
+               return iface->dup_mime_type (resolver, uri);
+
+       return NULL;
+}
+
 struct _ECidRequestPrivate {
        gint dummy;
 };
@@ -51,9 +91,6 @@ e_cid_request_process_sync (EContentRequest *request,
                            GCancellable *cancellable,
                            GError **error)
 {
-       EMailDisplay *display;
-       EMailPartList *part_list;
-       EMailPart *part;
        GByteArray *byte_array;
        CamelStream *output_stream;
        CamelDataWrapper *dw;
@@ -66,20 +103,13 @@ e_cid_request_process_sync (EContentRequest *request,
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
                return FALSE;
 
-       if (!E_IS_MAIL_DISPLAY (requester))
+       if (!E_IS_CID_RESOLVER (requester))
                return FALSE;
 
-       display = E_MAIL_DISPLAY (requester);
-
-       part_list = e_mail_display_get_part_list (display);
-       if (!part_list)
+       mime_part = e_cid_resolver_ref_part (E_CID_RESOLVER (requester), uri);
+       if (!mime_part)
                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);
@@ -102,11 +132,14 @@ e_cid_request_process_sync (EContentRequest *request,
                *out_stream_length = g_bytes_get_size (bytes);
 
                mime_type = camel_data_wrapper_get_mime_type (dw);
-               if (mime_type && *mime_type)
+               if (mime_type && *mime_type) {
                        *out_mime_type = mime_type;
-               else {
+               } else {
                        g_free (mime_type);
-                       *out_mime_type = g_strdup (e_mail_part_get_mime_type (part));
+                       *out_mime_type = e_cid_resolver_dup_mime_type (E_CID_RESOLVER (requester), uri);
+
+                       if (!*out_mime_type)
+                               *out_mime_type = g_strdup ("application/octet-stream");
                }
 
                g_bytes_unref (bytes);
@@ -116,7 +149,6 @@ e_cid_request_process_sync (EContentRequest *request,
 
        g_object_unref (output_stream);
        g_object_unref (mime_part);
-       g_object_unref (part);
 
        return success;
 }
diff --git a/src/mail/e-cid-request.h b/src/mail/e-cid-request.h
index 0ed6e4b242..15b12c7066 100644
--- a/src/mail/e-cid-request.h
+++ b/src/mail/e-cid-request.h
@@ -20,6 +20,24 @@
 #include <e-util/e-util.h>
 
 /* Standard GObject macros */
+#define E_TYPE_CID_RESOLVER \
+       (e_cid_resolver_get_type ())
+#define E_CID_RESOLVER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_CID_RESOLVER, ECidResolver))
+#define E_CID_RESOLVER_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_CID_RESOLVER, ECidResolverInterface))
+#define E_IS_CID_RESOLVER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_CID_RESOLVER))
+#define E_IS_CID_RESOLVER_INTERFACE(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_CID_RESOLVER))
+#define E_CID_RESOLVER_GET_INTERFACE(obj) \
+       (G_TYPE_INSTANCE_GET_INTERFACE \
+       ((obj), E_TYPE_CID_RESOLVER, ECidResolverInterface))
+
 #define E_TYPE_CID_REQUEST \
        (e_cid_request_get_type ())
 #define E_CID_REQUEST(obj) \
@@ -40,6 +58,25 @@
 
 G_BEGIN_DECLS
 
+typedef struct _ECidResolver ECidResolver;
+typedef struct _ECidResolverInterface ECidResolverInterface;
+
+struct _ECidResolverInterface {
+       GTypeInterface parent_interface;
+
+       CamelMimePart * (* ref_part)            (ECidResolver *resolver,
+                                                const gchar *uri);
+
+       gchar *         (* dup_mime_type)       (ECidResolver *resolver,
+                                                const gchar *uri);
+};
+
+GType          e_cid_resolver_get_type         (void);
+CamelMimePart *        e_cid_resolver_ref_part         (ECidResolver *resolver,
+                                                const gchar *uri);
+gchar *                e_cid_resolver_dup_mime_type    (ECidResolver *resolver,
+                                                const gchar *uri);
+
 typedef struct _ECidRequest ECidRequest;
 typedef struct _ECidRequestClass ECidRequestClass;
 typedef struct _ECidRequestPrivate ECidRequestPrivate;
diff --git a/src/mail/e-mail-display.c b/src/mail/e-mail-display.c
index 4ae60f7929..859f5dacb5 100644
--- a/src/mail/e-mail-display.c
+++ b/src/mail/e-mail-display.c
@@ -113,7 +113,10 @@ enum {
 static guint signals[LAST_SIGNAL];
 static CamelDataCache *emd_global_http_cache = NULL;
 
-G_DEFINE_TYPE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW)
+static void e_mail_display_cid_resolver_init (ECidResolverInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EMailDisplay, e_mail_display, E_TYPE_WEB_VIEW,
+       G_IMPLEMENT_INTERFACE (E_TYPE_CID_RESOLVER, e_mail_display_cid_resolver_init))
 
 static const gchar *ui =
 "<ui>"
@@ -2135,6 +2138,71 @@ mail_display_web_process_crashed_cb (EMailDisplay *display)
                e_alert_submit (alert_sink, "mail:webkit-web-process-crashed", NULL);
 }
 
+static EMailPart *
+e_mail_display_ref_mail_part (EMailDisplay *mail_display,
+                             const gchar *uri)
+{
+       EMailPartList *part_list;
+
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (mail_display), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       part_list = e_mail_display_get_part_list (mail_display);
+       if (!part_list)
+               return NULL;
+
+       return e_mail_part_list_ref_part (part_list, uri);
+}
+
+static CamelMimePart *
+e_mail_display_cid_resolver_ref_part (ECidResolver *resolver,
+                                     const gchar *uri)
+{
+       EMailPart *mail_part;
+       CamelMimePart *mime_part;
+
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (resolver), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       mail_part = e_mail_display_ref_mail_part (E_MAIL_DISPLAY (resolver), uri);
+       if (!mail_part)
+               return NULL;
+
+       mime_part = e_mail_part_ref_mime_part (mail_part);
+
+       g_object_unref (mail_part);
+
+       return mime_part;
+}
+
+static gchar *
+e_mail_display_cid_resolver_dup_mime_type (ECidResolver *resolver,
+                                          const gchar *uri)
+{
+       EMailPart *mail_part;
+       gchar *mime_type;
+
+       g_return_val_if_fail (E_IS_MAIL_DISPLAY (resolver), NULL);
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       mail_part = e_mail_display_ref_mail_part (E_MAIL_DISPLAY (resolver), uri);
+       if (!mail_part)
+               return NULL;
+
+       mime_type = g_strdup (e_mail_part_get_mime_type (mail_part));
+
+       g_object_unref (mail_part);
+
+       return mime_type;
+}
+
+static void
+e_mail_display_cid_resolver_init (ECidResolverInterface *iface)
+{
+       iface->ref_part = e_mail_display_cid_resolver_ref_part;
+       iface->dup_mime_type = e_mail_display_cid_resolver_dup_mime_type;
+}
+
 static void
 e_mail_display_class_init (EMailDisplayClass *class)
 {
diff --git a/src/mail/e-mail-notes.c b/src/mail/e-mail-notes.c
index a730afd101..d99e4b960e 100644
--- a/src/mail/e-mail-notes.c
+++ b/src/mail/e-mail-notes.c
@@ -150,13 +150,11 @@ static void
 e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes_editor,
                                                         CamelMultipart *multipart)
 {
-       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));
 
-       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        nparts = camel_multipart_get_number (multipart);
 
        for (ii = 0; ii < nparts; ii++) {
@@ -173,11 +171,14 @@ e_mail_notes_editor_extract_text_from_multipart_related (EMailNotesEditor *notes
                        continue;
 
                if (camel_content_type_is (ct, "image", "*")) {
-                       e_content_editor_insert_image_from_mime_part (cnt_editor, part);
+                       e_html_editor_add_cid_part (notes_editor->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 (cnt_editor, 
CAMEL_MULTIPART (content));
+
+                       if (CAMEL_IS_MULTIPART (content)) {
+                               e_mail_notes_extract_text_from_multipart_alternative (
+                                       e_html_editor_get_content_editor (notes_editor->editor), 
CAMEL_MULTIPART (content));
+                       }
                }
        }
 }
@@ -289,7 +290,8 @@ e_mail_notes_editor_extract_text_from_message (EMailNotesEditor *notes_editor,
 }
 
 static CamelMimeMessage *
-e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
+e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor,
+                                           EContentEditorContentHash *content_hash)
 {
        EContentEditor *cnt_editor;
        EAttachmentStore *attachment_store;
@@ -301,6 +303,7 @@ 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);
+       g_return_val_if_fail (content_hash != NULL, NULL);
 
        cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
        g_return_val_if_fail (E_IS_CONTENT_EDITOR (cnt_editor), NULL);
@@ -330,24 +333,20 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
                CamelMultipart *multipart_body;
                CamelMimePart *part;
                GSList *inline_images_parts = NULL;
-               gchar *text;
+               const 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_content_editor_get_content (
-                       cnt_editor,
-                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
-                       E_CONTENT_EDITOR_GET_PROCESSED,
-                       NULL, NULL);
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
 
                if (text && *text) {
-                       if (!g_str_has_suffix (text, "\r\n")) {
-                               gchar *tmp = text;
+                       gchar *tmp = NULL;
 
-                               text = g_strconcat (tmp, "\r\n", NULL);
-                               g_free (tmp);
+                       if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n")) {
+                               tmp = g_strconcat (text, "\r\n", NULL);
+                               text = tmp;
                        }
 
                        part = camel_mime_part_new ();
@@ -355,33 +354,26 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
                        camel_multipart_add_part (multipart_alternative, part);
 
                        g_object_unref (part);
+                       g_free (tmp);
 
                        has_text = TRUE;
                }
 
-               g_free (text);
-
-               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);
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_HTML);
+               inline_images_parts = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_INLINE_IMAGES);
 
                if (has_attachments && !has_text && (!text || !*text)) {
                        /* Text is required, thus if there are attachments,
                           but no text, then store at least a space. */
-                       g_free (text);
-                       text = g_strdup ("\r\n");
+                       text = "\r\n";
                }
 
                if (text && *text) {
-                       if (!g_str_has_suffix (text, "\r\n")) {
-                               gchar *tmp = text;
+                       gchar *tmp = NULL;
 
-                               text = g_strconcat (tmp, "\r\n", NULL);
-                               g_free (tmp);
+                       if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n")) {
+                               tmp = g_strconcat (text, "\r\n", NULL);
+                               text = tmp;
                        }
 
                        part = camel_mime_part_new ();
@@ -389,15 +381,13 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
                        camel_multipart_add_part (multipart_alternative, part);
 
                        g_object_unref (part);
+                       g_free (tmp);
 
                        has_text = TRUE;
                } else {
-                       g_slist_free_full (inline_images_parts, g_object_unref);
                        inline_images_parts = NULL;
                }
 
-               g_free (text);
-
                if (inline_images_parts) {
                        GSList *link;
 
@@ -443,31 +433,25 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
 
                camel_medium_set_content (CAMEL_MEDIUM (message), CAMEL_DATA_WRAPPER (multipart_body));
 
-               g_slist_free_full (inline_images_parts, g_object_unref);
                g_clear_object (&multipart_alternative);
                g_clear_object (&multipart_body);
        } else {
-               gchar *text;
+               const gchar *text;
 
-               text = e_content_editor_get_content (
-                       cnt_editor,
-                       E_CONTENT_EDITOR_GET_TEXT_PLAIN |
-                       E_CONTENT_EDITOR_GET_PROCESSED,
-                       NULL, NULL);
+               text = e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN);
 
                if (has_attachments && !has_text && (!text || !*text)) {
                        /* Text is required, thus if there are attachments,
                           but no text, then store at least a space. */
-                       g_free (text);
-                       text = g_strdup ("\r\n");
+                       text = "\r\n";
                }
 
                if (text && *text) {
-                       if (!g_str_has_suffix (text, "\r\n")) {
-                               gchar *tmp = text;
+                       gchar *tmp = NULL;
 
-                               text = g_strconcat (tmp, "\r\n", NULL);
-                               g_free (tmp);
+                       if (!g_str_has_suffix (text, "\r\n") && !g_str_has_suffix (text, "\n")) {
+                               tmp = g_strconcat (text, "\r\n", NULL);
+                               text = tmp;
                        }
 
                        if (has_attachments) {
@@ -491,10 +475,11 @@ e_mail_notes_editor_encode_text_to_message (EMailNotesEditor *notes_editor)
                        } else {
                                camel_mime_part_set_content (CAMEL_MIME_PART (message), text, strlen (text), 
"text/plain");
                        }
+
                        has_text = TRUE;
-               }
 
-               g_free (text);
+                       g_free (tmp);
+               }
        }
 
        if (has_text) {
@@ -790,9 +775,22 @@ action_close_cb (GtkAction *action,
 typedef struct {
        EMailNotesEditor *notes_editor;
        CamelMimeMessage *inner_message;
+       EActivity *activity;
+       GError *error;
        gboolean success;
 } SaveAndCloseData;
 
+static SaveAndCloseData *
+save_and_close_data_new (EMailNotesEditor *notes_editor)
+{
+       SaveAndCloseData *scd;
+
+       scd = g_slice_new0 (SaveAndCloseData);
+       scd->notes_editor = g_object_ref (notes_editor);
+
+       return scd;
+}
+
 static void
 save_and_close_data_free (gpointer ptr)
 {
@@ -804,6 +802,8 @@ save_and_close_data_free (gpointer ptr)
                else
                        g_clear_object (&scd->notes_editor);
                g_clear_object (&scd->inner_message);
+               g_clear_object (&scd->activity);
+               g_clear_error (&scd->error);
                g_slice_free (SaveAndCloseData, scd);
        }
 }
@@ -819,6 +819,12 @@ e_mail_notes_store_changes_thread (EAlertSinkThreadJobData *job_data,
 
        g_return_if_fail (scd != NULL);
 
+       if (scd->error) {
+               g_propagate_error (error, scd->error);
+               scd->error = NULL;
+               return;
+       }
+
        if (g_cancellable_set_error_if_cancelled (cancellable, error))
                return;
 
@@ -841,35 +847,76 @@ e_mail_notes_store_changes_thread (EAlertSinkThreadJobData *job_data,
 }
 
 static void
-action_save_and_close_cb (GtkAction *action,
-                         EMailNotesEditor *notes_editor)
+mail_notes_get_content_ready_cb (GObject *source_object,
+                                GAsyncResult *result,
+                                gpointer user_data)
 {
-       SaveAndCloseData *scd;
-       gchar *full_display_name;
+       SaveAndCloseData *scd = user_data;
+       EContentEditorContentHash *content_hash;
        EActivityBar *activity_bar;
        EActivity *activity;
+       gchar *full_display_name;
+       GError *error = NULL;
 
-       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+       g_return_if_fail (scd != NULL);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
 
-       scd = g_slice_new0 (SaveAndCloseData);
-       scd->notes_editor = g_object_ref (notes_editor);
-       scd->inner_message = e_mail_notes_editor_encode_text_to_message (notes_editor);
-       scd->success = FALSE;
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
 
-       full_display_name = e_mail_folder_to_full_display_name (notes_editor->folder, NULL);
+       if (content_hash) {
+               scd->inner_message = e_mail_notes_editor_encode_text_to_message (scd->notes_editor, 
content_hash);
 
-       activity_bar = e_html_editor_get_activity_bar (notes_editor->editor);
-       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (notes_editor->editor),
+               if (!scd->inner_message)
+                       scd->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to convert 
text to message"));
+       } else {
+               scd->error = error;
+
+               if (!scd->error)
+                       scd->error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Unknown error"));
+       }
+
+       g_clear_object (&scd->activity);
+
+       full_display_name = e_mail_folder_to_full_display_name (scd->notes_editor->folder, NULL);
+
+       activity_bar = e_html_editor_get_activity_bar (scd->notes_editor->editor);
+       activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (scd->notes_editor->editor),
                _("Storing changes…"), "mail:failed-store-note",
-               full_display_name ? full_display_name : camel_folder_get_display_name (notes_editor->folder),
+               full_display_name ? full_display_name : camel_folder_get_display_name 
(scd->notes_editor->folder),
                e_mail_notes_store_changes_thread,
                scd, save_and_close_data_free);
        e_activity_bar_set_activity (activity_bar, activity);
        g_clear_object (&activity);
 
+       e_content_editor_util_free_content_hash (content_hash);
        g_free (full_display_name);
 }
 
+static void
+action_save_and_close_cb (GtkAction *action,
+                         EMailNotesEditor *notes_editor)
+{
+       SaveAndCloseData *scd;
+       EActivity *activity;
+       EContentEditor *cnt_editor;
+
+       g_return_if_fail (E_IS_MAIL_NOTES_EDITOR (notes_editor));
+
+       cnt_editor = e_html_editor_get_content_editor (notes_editor->editor);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (cnt_editor));
+
+       activity = e_html_editor_new_activity (notes_editor->editor);
+       e_activity_set_text (activity, _("Storing changes…"));
+
+       scd = save_and_close_data_new (notes_editor);
+       scd->activity = activity; /* takes ownership */
+
+       e_content_editor_get_content (cnt_editor,
+               E_CONTENT_EDITOR_GET_INLINE_IMAGES | E_CONTENT_EDITOR_GET_TO_SEND_HTML | 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN,
+               g_get_host_name (), e_activity_get_cancellable (activity),
+               mail_notes_get_content_ready_cb, scd);
+}
+
 static void
 e_mail_notes_editor_dispose (GObject *object)
 {
diff --git a/src/modules/composer-to-meeting/e-composer-to-meeting.c 
b/src/modules/composer-to-meeting/e-composer-to-meeting.c
index 8bc2b52bfc..04a328ce61 100644
--- a/src/modules/composer-to-meeting/e-composer-to-meeting.c
+++ b/src/modules/composer-to-meeting/e-composer-to-meeting.c
@@ -64,11 +64,10 @@ GType e_composer_to_meeting_get_type (void) G_GNUC_CONST;
 G_DEFINE_DYNAMIC_TYPE (EComposerToMeeting, e_composer_to_meeting, E_TYPE_EXTENSION)
 
 static ECalComponent *
-composer_to_meeting_component (EMsgComposer *composer)
+composer_to_meeting_component (EMsgComposer *composer,
+                              EContentEditorContentHash *content_hash)
 {
        ECalComponent *comp;
-       EHTMLEditor *html_editor;
-       EContentEditor *cnt_editor;
        EComposerHeaderTable *header_table;
        EDestination **destinations_array[3];
        ESource *source;
@@ -210,20 +209,12 @@ composer_to_meeting_component (EMsgComposer *composer)
        g_slist_free_full (attendees, e_cal_component_attendee_free);
 
        /* Description */
-       html_editor = e_msg_composer_get_editor (composer);
-       cnt_editor = e_html_editor_get_content_editor (html_editor);
-       text = e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_PROCESSED | 
E_CONTENT_EDITOR_GET_TEXT_PLAIN, NULL, NULL);
+       text = content_hash ? e_content_editor_util_get_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN) : NULL;
+
        if (text && *text) {
                ECalComponentText *description;
                GSList *descr_list = NULL;
 
-               if (!g_str_has_suffix (text, "\r\n")) {
-                       gchar *tmp = text;
-
-                       text = g_strconcat (tmp, "\r\n", NULL);
-                       g_free (tmp);
-               }
-
                description = e_cal_component_text_new (text, NULL);
 
                descr_list = g_slist_append (descr_list, description);
@@ -232,7 +223,6 @@ composer_to_meeting_component (EMsgComposer *composer)
 
                g_slist_free_full (descr_list, e_cal_component_text_free);
        }
-       g_free (text);
 
        return comp;
 }
@@ -270,38 +260,102 @@ composer_to_meeting_copy_attachments (EMsgComposer *composer,
        g_list_free_full (attachments, g_object_unref);
 }
 
+typedef struct _AsyncContext {
+       EMsgComposer *composer;
+       EActivity *activity;
+} AsyncContext;
+
+static AsyncContext *
+async_context_new (EMsgComposer *composer, /* adds reference */
+                  EActivity *activity) /* assumes ownership */
+{
+       AsyncContext *async_context;
+
+       async_context = g_slice_new (AsyncContext);
+       async_context->composer = g_object_ref (composer);
+       async_context->activity = activity;
+
+       return async_context;
+}
+
+static void
+async_context_free (AsyncContext *async_context)
+{
+       if (async_context) {
+               g_clear_object (&async_context->composer);
+               g_clear_object (&async_context->activity);
+               g_slice_free (AsyncContext, async_context);
+       }
+}
+
+static void
+compose_to_meeting_content_ready_cb (GObject *source_object,
+                                    GAsyncResult *result,
+                                    gpointer user_data)
+{
+       AsyncContext *async_context = user_data;
+       EContentEditorContentHash *content_hash;
+       ECalComponent *comp;
+       GError *error = NULL;
+
+       g_return_if_fail (async_context != NULL);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+
+       content_hash = e_content_editor_get_content_finish (E_CONTENT_EDITOR (source_object), result, &error);
+
+       comp = composer_to_meeting_component (async_context->composer, content_hash);
+
+       if (comp) {
+               ECompEditor *comp_editor;
+               ECompEditorFlags flags;
+
+               flags = E_COMP_EDITOR_FLAG_IS_NEW |
+                       E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER |
+                       E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
+
+               comp_editor = e_comp_editor_open_for_component (NULL, e_msg_composer_get_shell 
(async_context->composer),
+                       NULL, e_cal_component_get_icalcomponent (comp), flags);
+
+               /* Attachments */
+               composer_to_meeting_copy_attachments (async_context->composer, comp_editor);
+
+               gtk_window_present (GTK_WINDOW (comp_editor));
+
+               g_object_unref (comp);
+
+               gtk_widget_destroy (GTK_WIDGET (async_context->composer));
+       }
+
+       e_content_editor_util_free_content_hash (content_hash);
+       async_context_free (async_context);
+       g_clear_error (&error);
+}
+
 static void
 action_composer_to_meeting_cb (GtkAction *action,
                               EMsgComposer *composer)
 {
-       ECalComponent *comp;
-       ECompEditor *comp_editor;
-       ECompEditorFlags flags;
+       EHTMLEditor *editor;
+       EContentEditor *cnt_editor;
+       EActivity *activity;
+       AsyncContext *async_context;
 
        g_return_if_fail (E_IS_MSG_COMPOSER (composer));
 
        if (!e_util_prompt_user (GTK_WINDOW (composer), NULL, NULL, 
"mail-composer:prompt-composer-to-meeting", NULL))
                return;
 
-       comp = composer_to_meeting_component (composer);
-       if (!comp)
-               return;
-
-       flags = E_COMP_EDITOR_FLAG_IS_NEW |
-               E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER |
-               E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
-
-       comp_editor = e_comp_editor_open_for_component (NULL, e_msg_composer_get_shell (composer),
-               NULL, e_cal_component_get_icalcomponent (comp), flags);
-
-       /* Attachments */
-       composer_to_meeting_copy_attachments (composer, comp_editor);
+       editor = e_msg_composer_get_editor (composer);
+       cnt_editor = e_html_editor_get_content_editor (editor);
 
-       gtk_window_present (GTK_WINDOW (comp_editor));
+       activity = e_html_editor_new_activity (editor);
+       e_activity_set_text (activity, _("Reading text content…"));
 
-       g_object_unref (comp);
+       async_context = async_context_new (composer, activity);
 
-       gtk_widget_destroy (GTK_WIDGET (composer));
+       e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, NULL,
+               e_activity_get_cancellable (activity),
+               compose_to_meeting_content_ready_cb, async_context);
 }
 
 static void
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 002724e9a6..885b0f42c1 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -18,10 +18,9 @@
 
 #include "e-webkit-editor.h"
 
-#include "web-extension/e-editor-web-extension-names.h"
-
 #include "e-util/e-util.h"
 #include "composer/e-msg-composer.h"
+#include "mail/e-cid-request.h"
 #include "mail/e-http-request.h"
 
 #include <string.h>
@@ -37,7 +36,6 @@
 
 enum {
        PROP_0,
-       PROP_WEB_EXTENSION, /* for test purposes */
        PROP_IS_MALFUNCTION,
        PROP_CAN_COPY,
        PROP_CAN_CUT,
@@ -61,13 +59,18 @@ enum {
        PROP_FONT_COLOR,
        PROP_FONT_NAME,
        PROP_FONT_SIZE,
-       PROP_INDENTED,
+       PROP_INDENT_LEVEL,
        PROP_ITALIC,
-       PROP_MONOSPACED,
        PROP_STRIKETHROUGH,
        PROP_SUBSCRIPT,
        PROP_SUPERSCRIPT,
-       PROP_UNDERLINE
+       PROP_UNDERLINE,
+
+       PROP_NORMAL_PARAGRAPH_WIDTH,
+       PROP_MAGIC_LINKS,
+       PROP_MAGIC_SMILEYS,
+       PROP_UNICODE_SMILEYS,
+       PROP_WRAP_QUOTED_TEXT_IN_REPLIES
 };
 
 struct _EWebKitEditorPrivate {
@@ -75,13 +78,6 @@ struct _EWebKitEditorPrivate {
        gpointer initialized_user_data;
 
        GCancellable *cancellable;
-       EWebExtensionContainer *container;
-       GDBusProxy *web_extension_proxy;
-       gint stamp; /* Changed only in the main thread, doesn't need locking */
-       guint web_extension_selection_changed_cb_id;
-       guint web_extension_content_changed_cb_id;
-       guint web_extension_undo_redo_state_changed_cb_id;
-       guint web_extension_user_changed_default_colors_cb_id;
 
        gboolean html_mode;
        gboolean changed;
@@ -91,28 +87,39 @@ struct _EWebKitEditorPrivate {
        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;
-       gboolean suppress_color_changes;
-
        guint32 style_flags;
-       gboolean is_indented;
+       guint32 temporary_style_flags; /* that's for collapsed selection, format changes only after something 
is typed */
+       gint indent_level;
 
        GdkRGBA *background_color;
        GdkRGBA *font_color;
+       GdkRGBA *body_fg_color;
+       GdkRGBA *body_bg_color;
+       GdkRGBA *body_link_color;
+       GdkRGBA *body_vlink_color;
+
+       GdkRGBA theme_bgcolor;
+       GdkRGBA theme_fgcolor;
+       GdkRGBA theme_link_color;
+       GdkRGBA theme_vlink_color;
 
        gchar *font_name;
+       gchar *body_font_name;
 
        guint font_size;
+       gint normal_paragraph_width;
+       gboolean magic_links;
+       gboolean magic_smileys;
+       gboolean unicode_smileys;
+       gboolean wrap_quoted_text_in_replies;
 
        EContentEditorBlockFormat block_format;
        EContentEditorAlignment alignment;
 
+       /* For context menu */
+       gchar *context_menu_caret_word;
+       guint32 context_menu_node_flags; /* bit-or of EContentEditorNodeFlags */
+
        gchar *current_user_stylesheet;
 
        WebKitLoadEvent webkit_load_event;
@@ -154,9 +161,18 @@ struct _EWebKitEditorPrivate {
 };
 
 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 enum {
+       E_WEBKIT_EDITOR_STYLE_NONE              = 0,
+       E_WEBKIT_EDITOR_STYLE_IS_BOLD           = 1 << 0,
+       E_WEBKIT_EDITOR_STYLE_IS_ITALIC         = 1 << 1,
+       E_WEBKIT_EDITOR_STYLE_IS_UNDERLINE      = 1 << 2,
+       E_WEBKIT_EDITOR_STYLE_IS_STRIKETHROUGH  = 1 << 3,
+       E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT      = 1 << 4,
+       E_WEBKIT_EDITOR_STYLE_IS_SUPERSCRIPT    = 1 << 5
+} EWebKitEditorStyleFlags;
+
 typedef void (*PostReloadOperationFunc) (EWebKitEditor *wk_editor, gpointer data, 
EContentEditorInsertContentFlags flags);
 
 typedef struct {
@@ -166,27 +182,436 @@ typedef struct {
        GDestroyNotify data_free_func;
 } PostReloadOperation;
 
-static void e_webkit_editor_set_web_extension_proxy (EWebKitEditor *wk_editor, GDBusProxy *proxy);
 static void e_webkit_editor_content_editor_init (EContentEditorInterface *iface);
+static void e_webkit_editor_cid_resolver_init (ECidResolverInterface *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)
+       G_IMPLEMENT_INTERFACE (E_TYPE_CID_RESOLVER, e_webkit_editor_cid_resolver_init));
+
+typedef struct _EWebKitEditorFlagClass {
+       GObjectClass parent_class;
+} EWebKitEditorFlagClass;
+
+typedef struct _EWebKitEditorFlag {
+       GObject parent;
+       gboolean is_set;
+} EWebKitEditorFlag;
+
+GType e_webkit_editor_flag_get_type (void);
+
+G_DEFINE_TYPE (EWebKitEditorFlag, e_webkit_editor_flag, G_TYPE_OBJECT)
+
+enum {
+       E_WEBKIT_EDITOR_FLAG_FLAGGED,
+       E_WEBKIT_EDITOR_FLAG_LAST_SIGNAL
+};
+
+static guint e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_LAST_SIGNAL];
+
+static void
+e_webkit_editor_flag_class_init (EWebKitEditorFlagClass *klass)
+{
+       e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_FLAGGED] = g_signal_new (
+               "flagged",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               0,
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+}
+
+static void
+e_webkit_editor_flag_init (EWebKitEditorFlag *flag)
+{
+       flag->is_set = FALSE;
+}
+
+static void
+e_webkit_editor_flag_set (EWebKitEditorFlag *flag)
+{
+       flag->is_set = TRUE;
+
+       g_signal_emit (flag, e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_FLAGGED], 0, NULL);
+}
+
+static JSCValue * /* transfer full */
+webkit_editor_call_jsc_sync (EWebKitEditor *wk_editor,
+                            const gchar *script_format,
+                            ...) G_GNUC_PRINTF (2, 3);
+
+typedef struct _JSCCallData {
+       EWebKitEditorFlag *flag;
+       gchar *script;
+       JSCValue *result;
+} JSCCallData;
+
+static void
+webkit_editor_jsc_call_done_cb (GObject *source,
+                               GAsyncResult *result,
+                               gpointer user_data)
+{
+       WebKitJavascriptResult *js_result;
+       JSCCallData *jcd = user_data;
+       GError *error = NULL;
+
+       js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source), result, &error);
+
+       if (error) {
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+                   (!g_error_matches (error, WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED) 
||
+                    /* WebKit can return empty error message, thus ignore those. */
+                    (error->message && *(error->message))))
+                       g_warning ("Failed to call '%s' function: %s:%d: %s", jcd->script, g_quark_to_string 
(error->domain), error->code, error->message);
+               g_clear_error (&error);
+       }
+
+       if (js_result) {
+               JSCException *exception;
+               JSCValue *value;
+
+               value = webkit_javascript_result_get_js_value (js_result);
+               exception = jsc_context_get_exception (jsc_value_get_context (value));
+
+               if (exception) {
+                       g_warning ("Failed to call '%s': %s", jcd->script, jsc_exception_get_message 
(exception));
+                       jsc_context_clear_exception (jsc_value_get_context (value));
+               } else if (!jsc_value_is_null (value) && !jsc_value_is_undefined (value)) {
+                       jcd->result = g_object_ref (value);
+               }
+
+               webkit_javascript_result_unref (js_result);
+       }
+
+       e_webkit_editor_flag_set (jcd->flag);
+}
+
+static JSCValue * /* transfer full */
+webkit_editor_call_jsc_sync (EWebKitEditor *wk_editor,
+                            const gchar *script_format,
+                            ...)
+{
+       JSCCallData jcd;
+       va_list va;
+
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+       g_return_val_if_fail (script_format != NULL, NULL);
+
+       va_start (va, script_format);
+       jcd.script = e_web_view_jsc_vprintf_script (script_format, va);
+       va_end (va);
+
+       jcd.flag = g_object_new (e_webkit_editor_flag_get_type (), NULL);
+       jcd.result = NULL;
+
+       webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (wk_editor), jcd.script, wk_editor->priv->cancellable,
+               webkit_editor_jsc_call_done_cb, &jcd);
+
+       if (!jcd.flag->is_set) {
+               GMainLoop *loop;
+               gulong handler_id;
+
+               loop = g_main_loop_new (NULL, FALSE);
+
+               handler_id = g_signal_connect_swapped (jcd.flag, "flagged", G_CALLBACK (g_main_loop_quit), 
loop);
+
+               g_main_loop_run (loop);
+               g_main_loop_unref (loop);
+
+               g_signal_handler_disconnect (jcd.flag, handler_id);
+       }
+
+       g_clear_object (&jcd.flag);
+       g_free (jcd.script);
+
+       return jcd.result;
+}
+
+static gboolean
+webkit_editor_extract_and_free_jsc_boolean (JSCValue *jsc_value,
+                                           gboolean default_value)
+{
+       gboolean value;
+
+       if (jsc_value && jsc_value_is_boolean (jsc_value))
+               value = jsc_value_to_boolean (jsc_value);
+       else
+               value = default_value;
+
+       g_clear_object (&jsc_value);
+
+       return value;
+}
+
+static gint32
+webkit_editor_extract_and_free_jsc_int32 (JSCValue *jsc_value,
+                                         gint32 default_value)
+{
+       gint32 value;
+
+       if (jsc_value && jsc_value_is_number (jsc_value))
+               value = jsc_value_to_int32 (jsc_value);
+       else
+               value = default_value;
+
+       g_clear_object (&jsc_value);
+
+       return value;
+}
+
+static gchar *
+webkit_editor_extract_and_free_jsc_string (JSCValue *jsc_value,
+                                          const gchar *default_value)
+{
+       gchar *value;
+
+       if (jsc_value && jsc_value_is_string (jsc_value))
+               value = jsc_value_to_string (jsc_value);
+       else
+               value = g_strdup (default_value);
+
+       g_clear_object (&jsc_value);
+
+       return value;
+}
+
+static const gchar *
+webkit_editor_utils_int_to_string (gchar *inout_buff,
+                                  gulong buff_len,
+                                  gint value)
+{
+       g_snprintf (inout_buff, buff_len, "%d", value);
+
+       return inout_buff;
+}
+
+static const gchar *
+webkit_editor_utils_int_with_unit_to_string (gchar *inout_buff,
+                                            gulong buff_len,
+                                            gint value,
+                                            EContentEditorUnit unit)
+{
+       if (unit == E_CONTENT_EDITOR_UNIT_AUTO)
+               g_snprintf (inout_buff, buff_len, "auto");
+       else
+               g_snprintf (inout_buff, buff_len, "%d%s",
+                       value,
+                       (unit == E_CONTENT_EDITOR_UNIT_PIXEL) ? "px" : "%");
+
+       return inout_buff;
+}
+
+static const gchar *
+webkit_editor_utils_color_to_string (gchar *inout_buff,
+                                    gulong buff_len,
+                                    const GdkRGBA *color)
+{
+       if (color && color->alpha > 1e-9)
+               g_snprintf (inout_buff, buff_len, "#%06x", e_rgba_to_value (color));
+       else if (buff_len)
+               inout_buff[0] = '\0';
+
+       return inout_buff;
+}
+
+static void
+webkit_editor_dialog_utils_set_attribute (EWebKitEditor *wk_editor,
+                                         const gchar *selector,
+                                         const gchar *name,
+                                         const gchar *value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (name != NULL);
+
+       if (value) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.DialogUtilsSetAttribute(%s, %s, %s);",
+                       selector, name, value);
+       } else {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.DialogUtilsSetAttribute(%s, %s, null);",
+                       selector, name);
+       }
+}
+
+static void
+webkit_editor_dialog_utils_set_attribute_int (EWebKitEditor *wk_editor,
+                                             const gchar *selector,
+                                             const gchar *name,
+                                             gint value)
+{
+       gchar str_value[64];
+
+       webkit_editor_dialog_utils_set_attribute (wk_editor, selector, name,
+               webkit_editor_utils_int_to_string (str_value, sizeof (str_value), value));
+}
+
+static void
+webkit_editor_dialog_utils_set_attribute_with_unit (EWebKitEditor *wk_editor,
+                                                   const gchar *selector,
+                                                   const gchar *name,
+                                                   gint value,
+                                                   EContentEditorUnit unit)
+{
+       gchar str_value[64];
+
+       webkit_editor_dialog_utils_set_attribute (wk_editor, selector, name,
+               webkit_editor_utils_int_with_unit_to_string (str_value, sizeof (str_value), value, unit));
+}
+
+static void
+webkit_editor_dialog_utils_set_attribute_color (EWebKitEditor *wk_editor,
+                                               const gchar *selector,
+                                               const gchar *name,
+                                               const GdkRGBA *color)
+{
+       gchar str_value[64];
+
+       webkit_editor_dialog_utils_set_attribute (wk_editor, selector, name,
+               webkit_editor_utils_color_to_string (str_value, sizeof (str_value), color));
+}
+
+static void
+webkit_editor_dialog_utils_set_table_attribute (EWebKitEditor *wk_editor,
+                                               EContentEditorScope scope,
+                                               const gchar *name,
+                                               const gchar *value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (name != NULL);
+
+       if (value) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.DialogUtilsTableSetAttribute(%d, %s, %s);",
+                       scope, name, value);
+       } else {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.DialogUtilsTableSetAttribute(%d, %s, null);",
+                       scope, name);
+       }
+}
+
+static gchar *
+webkit_editor_dialog_utils_get_attribute (EWebKitEditor *wk_editor,
+                                         const gchar *selector,
+                                         const gchar *name)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+       g_return_val_if_fail (name != NULL, NULL);
+
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (wk_editor,
+                       "EvoEditor.DialogUtilsGetAttribute(%s, %s);",
+                       selector, name),
+               NULL);
+}
+
+static gint
+webkit_editor_dialog_utils_get_attribute_int (EWebKitEditor *wk_editor,
+                                             const gchar *selector,
+                                             const gchar *name,
+                                             gint default_value)
+{
+       gchar *attr;
+       gint value;
+
+       attr = webkit_editor_dialog_utils_get_attribute (wk_editor, selector, name);
+
+       if (attr && *attr)
+               value = atoi (attr);
+       else
+               value = default_value;
+
+       g_free (attr);
+
+       return value;
+}
+
+static gint
+webkit_editor_dialog_utils_get_attribute_with_unit (EWebKitEditor *wk_editor,
+                                                   const gchar *selector,
+                                                   const gchar *name,
+                                                   gint default_value,
+                                                   EContentEditorUnit *out_unit)
+{
+       gint result;
+       gchar *value;
 
-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));
+       *out_unit = E_CONTENT_EDITOR_UNIT_AUTO;
 
-static gint16
-e_webkit_editor_three_state_to_int16 (EThreeState value)
+       if (!wk_editor->priv->html_mode)
+               return default_value;
+
+       value = webkit_editor_dialog_utils_get_attribute (wk_editor, selector, name);
+
+       if (value && *value) {
+               result = atoi (value);
+
+               if (strstr (value, "%"))
+                       *out_unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE;
+               else if (g_ascii_strncasecmp (value, "auto", 4) != 0)
+                       *out_unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+       } else {
+               result = default_value;
+       }
+
+       g_free (value);
+
+       return result;
+}
+
+static void
+webkit_editor_dialog_utils_get_attribute_color (EWebKitEditor *wk_editor,
+                                               const gchar *selector,
+                                               const gchar *name,
+                                               GdkRGBA *out_color)
+{
+       gchar *value;
+
+       value = webkit_editor_dialog_utils_get_attribute (wk_editor, selector, name);
+
+       if (!value || !*value || !gdk_rgba_parse (out_color, value))
+               *out_color = transparent;
+
+       g_free (value);
+}
+
+static gboolean
+webkit_editor_dialog_utils_has_attribute (EWebKitEditor *wk_editor,
+                                         const gchar *name)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+       g_return_val_if_fail (name != NULL, FALSE);
+
+       return webkit_editor_extract_and_free_jsc_boolean (
+               webkit_editor_call_jsc_sync (wk_editor,
+                       "EvoEditor.DialogUtilsHasAttribute(%s);",
+                       name),
+               FALSE);
+}
+
+static gboolean
+e_webkit_editor_three_state_to_bool (EThreeState value,
+                                    const gchar *mail_key)
 {
+       gboolean res = FALSE;
+
        if (value == E_THREE_STATE_ON)
-               return 1;
+               return TRUE;
 
        if (value == E_THREE_STATE_OFF)
-               return 0;
+               return FALSE;
 
-       return -1;
+       if (mail_key && *mail_key) {
+               GSettings *settings;
+
+               settings = e_util_ref_settings ("org.gnome.evolution.mail");
+               res = g_settings_get_boolean (settings, mail_key);
+               g_clear_object (&settings);
+       }
+
+       return res;
 }
 
 EWebKitEditor *
@@ -215,21 +640,6 @@ webkit_editor_get_last_error (EWebKitEditor *wk_editor)
        return wk_editor->priv->last_error;
 }
 
-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)
 {
@@ -238,21 +648,6 @@ webkit_editor_can_paste (EWebKitEditor *wk_editor)
        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)
 {
@@ -261,26 +656,6 @@ webkit_editor_can_cut (EWebKitEditor *wk_editor)
        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_is_malfunction (EWebKitEditor *wk_editor)
 {
@@ -351,470 +726,373 @@ webkit_editor_set_can_redo (EWebKitEditor *wk_editor,
 }
 
 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)
+content_changed_cb (WebKitUserContentManager *manager,
+                   WebKitJavascriptResult *js_result,
+                   gpointer user_data)
 {
-       if (g_strcmp0 (signal_name, "ContentChanged") != 0)
-               return;
-
-       if (parameters) {
-               guint64 page_id = 0;
+       EWebKitEditor *wk_editor = user_data;
 
-               g_variant_get (parameters, "(t)", &page_id);
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-               if (page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor)))
-                       webkit_editor_set_changed (wk_editor, TRUE);
-       }
+       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)
+context_menu_requested_cb (WebKitUserContentManager *manager,
+                          WebKitJavascriptResult *js_result,
+                          gpointer user_data)
 {
-       guint64 page_id = 0;
-       gchar *font_color = NULL;
-       guint32 alignment, block_format, style_flags, font_size;
-       gboolean is_indented;
+       EWebKitEditor *wk_editor = user_data;
+       JSCValue *jsc_params;
 
-       if (g_strcmp0 (signal_name, "SelectionChanged") != 0)
-               return;
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (js_result != NULL);
 
-       if (!parameters)
-               return;
+       jsc_params = webkit_javascript_result_get_js_value (js_result);
+       g_return_if_fail (jsc_value_is_object (jsc_params));
 
-       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;
-       }
+       g_clear_pointer (&wk_editor->priv->context_menu_caret_word, g_free);
 
-       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;
+       wk_editor->priv->context_menu_node_flags = e_web_view_jsc_get_object_property_int32 (jsc_params, 
"nodeFlags", 0);
+       wk_editor->priv->context_menu_caret_word = e_web_view_jsc_get_object_property_string (jsc_params, 
"caretWord", NULL);
+}
 
-       if (wk_editor->priv->html_mode) {
-               GdkRGBA color;
+static gboolean
+webkit_editor_update_color_value (JSCValue *jsc_params,
+                                 const gchar *param_name,
+                                 GdkRGBA **out_rgba)
+{
+       JSCValue *jsc_value;
+       GdkRGBA color;
+       gboolean res = FALSE;
 
-               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_return_val_if_fail (jsc_params != NULL, FALSE);
+       g_return_val_if_fail (out_rgba != NULL, FALSE);
 
-       g_object_notify (G_OBJECT (wk_editor), "can-undo");
-       g_object_notify (G_OBJECT (wk_editor), "can-redo");
+       jsc_value = jsc_value_object_get_property (jsc_params, param_name);
+       if (jsc_value && jsc_value_is_string (jsc_value)) {
+               gchar *value;
 
-       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");
+               value = jsc_value_to_string (jsc_value);
 
-       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");
-       }
+               if (value && *value && gdk_rgba_parse (&color, value)) {
+                       if (!(*out_rgba) || !gdk_rgba_equal (&color, *out_rgba)) {
+                               if (*out_rgba)
+                                       gdk_rgba_free (*out_rgba);
+                               *out_rgba = gdk_rgba_copy (&color);
 
-       g_object_thaw_notify (G_OBJECT (wk_editor));
-}
+                               res = TRUE;
+                       }
+               } else {
+                       if (*out_rgba) {
+                               gdk_rgba_free (*out_rgba);
+                               res = TRUE;
+                       }
 
-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;
+                       *out_rgba = NULL;
+               }
 
-       if (g_strcmp0 (signal_name, "UndoRedoStateChanged") != 0)
-               return;
+               g_free (value);
+       }
 
-       g_variant_get (parameters, "(tbb)", &page_id, &can_undo, &can_redo);
+       g_clear_object (&jsc_value);
 
-       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);
-       }
+       return res;
 }
 
+static void webkit_editor_update_styles (EContentEditor *editor);
+static void webkit_editor_style_updated (EWebKitEditor *wk_editor, gboolean force);
+
 static void
-web_extension_user_changed_default_colors_cb (GDBusConnection *connection,
-                                              const gchar *sender_name,
-                                              const gchar *object_path,
-                                              const gchar *interface_name,
-                                              const gchar *signal_name,
-                                              GVariant *parameters,
-                                              EWebKitEditor *wk_editor)
+formatting_changed_cb (WebKitUserContentManager *manager,
+                      WebKitJavascriptResult *js_result,
+                      gpointer user_data)
 {
-       if (g_strcmp0 (signal_name, "UserChangedDefaultColors") != 0)
-               return;
+       EWebKitEditor *wk_editor = user_data;
+       JSCValue *jsc_params, *jsc_value;
+       GObject *object;
+       gboolean changed, forced = FALSE;
 
-       if (parameters)
-               g_variant_get (parameters, "(b)", &wk_editor->priv->suppress_color_changes);
-}
-
-static void
-dispatch_pending_operations (EWebKitEditor *wk_editor)
-{
-       if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED ||
-           !wk_editor->priv->web_extension_proxy)
-               return;
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       /* 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)) {
+       jsc_params = webkit_javascript_result_get_js_value (js_result);
+       g_return_if_fail (jsc_value_is_object (jsc_params));
 
-               PostReloadOperation *op;
+       object = G_OBJECT (wk_editor);
 
-               op = g_queue_pop_head (wk_editor->priv->post_reload_operations);
+       g_object_freeze_notify (object);
 
-               op->func (wk_editor, op->data, op->flags);
+       jsc_value = jsc_value_object_get_property (jsc_params, "forced");
+       if (jsc_value && jsc_value_is_boolean (jsc_value)) {
+               forced = jsc_value_to_boolean (jsc_value);
+       }
+       g_clear_object (&jsc_value);
 
-               if (op->data_free_func)
-                       op->data_free_func (op->data);
-               g_free (op);
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "mode");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-               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);
+               if ((value ? 1 : 0) != (wk_editor->priv->html_mode ? 1 : 0)) {
+                       wk_editor->priv->html_mode = value;
+                       changed = TRUE;
                }
-
-               g_queue_clear (wk_editor->priv->post_reload_operations);
        }
-}
+       g_clear_object (&jsc_value);
 
-static void
-e_webkit_editor_page_proxy_changed_cb (EWebExtensionContainer *container,
-                                      guint64 page_id,
-                                      gint stamp,
-                                      GDBusProxy *proxy,
-                                      gpointer user_data)
-{
-       EWebKitEditor *wk_editor = user_data;
-
-       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       if (changed) {
+               /* Update fonts - in plain text we only want monospaced */
+               webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor));
+               webkit_editor_style_updated (wk_editor, FALSE);
 
-       if (stamp == wk_editor->priv->stamp &&
-           page_id == webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor))) {
-               e_webkit_editor_set_web_extension_proxy (wk_editor, proxy);
+               g_object_notify (object, "html-mode");
+       }
 
-               if (proxy) {
-                       dispatch_pending_operations (wk_editor);
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "alignment");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-                       if (wk_editor->priv->emit_load_finished_when_extension_is_ready) {
-                               e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor));
+               if (value != wk_editor->priv->alignment) {
+                       wk_editor->priv->alignment = value;
+                       changed = TRUE;
+               }
+       }
+       g_clear_object (&jsc_value);
 
-                               wk_editor->priv->emit_load_finished_when_extension_is_ready = FALSE;
-                       }
+       if (changed || forced)
+               g_object_notify (object, "alignment");
 
-                       g_object_notify (G_OBJECT (wk_editor), "web-extension");
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "blockFormat");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-                       if (wk_editor->priv->initialized_callback) {
-                               EContentEditorInitializedCallback initialized_callback;
-                               gpointer initialized_user_data;
+               if (value != wk_editor->priv->block_format) {
+                       wk_editor->priv->block_format = value;
+                       changed = TRUE;
+               }
+       }
+       g_clear_object (&jsc_value);
 
-                               initialized_callback = wk_editor->priv->initialized_callback;
-                               initialized_user_data = wk_editor->priv->initialized_user_data;
+       if (changed || forced)
+               g_object_notify (object, "block-format");
 
-                               wk_editor->priv->initialized_callback = NULL;
-                               wk_editor->priv->initialized_user_data = NULL;
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "indentLevel");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-                               initialized_callback (E_CONTENT_EDITOR (wk_editor), initialized_user_data);
-                       }
+               if (value != wk_editor->priv->indent_level) {
+                       wk_editor->priv->indent_level = value;
+                       changed = TRUE;
                }
        }
-}
+       g_clear_object (&jsc_value);
 
-static void
-e_webkit_editor_set_web_extension_proxy (EWebKitEditor *wk_editor,
-                                        GDBusProxy *proxy)
-{
-       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       if (changed || forced)
+               g_object_notify (object, "indent-level");
 
-       if (wk_editor->priv->web_extension_proxy) {
-               GDBusConnection *connection;
+       #define update_style_flag(_flag, _set) \
+               changed = (wk_editor->priv->style_flags & (_flag)) != ((_set) ? (_flag) : 0); \
+               wk_editor->priv->style_flags = (wk_editor->priv->style_flags & ~(_flag)) | ((_set) ? (_flag) 
: 0);
 
-               connection = g_dbus_proxy_get_connection (wk_editor->priv->web_extension_proxy);
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "bold");
+       if (jsc_value && jsc_value_is_boolean (jsc_value)) {
+               gboolean value = jsc_value_to_boolean (jsc_value);
 
-               if (connection && g_dbus_connection_is_closed (connection))
-                       connection = NULL;
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_BOLD, value);
+       }
+       g_clear_object (&jsc_value);
 
-               if (wk_editor->priv->web_extension_content_changed_cb_id) {
-                       if (connection)
-                               g_dbus_connection_signal_unsubscribe (connection, 
wk_editor->priv->web_extension_content_changed_cb_id);
-                       wk_editor->priv->web_extension_content_changed_cb_id = 0;
-               }
+       if (changed || forced)
+               g_object_notify (G_OBJECT (wk_editor), "bold");
 
-               if (wk_editor->priv->web_extension_selection_changed_cb_id) {
-                       if (connection)
-                               g_dbus_connection_signal_unsubscribe (connection, 
wk_editor->priv->web_extension_selection_changed_cb_id);
-                       wk_editor->priv->web_extension_selection_changed_cb_id = 0;
-               }
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "italic");
+       if (jsc_value && jsc_value_is_boolean (jsc_value)) {
+               gboolean value = jsc_value_to_boolean (jsc_value);
 
-               if (wk_editor->priv->web_extension_undo_redo_state_changed_cb_id) {
-                       if (connection)
-                               g_dbus_connection_signal_unsubscribe (connection, 
wk_editor->priv->web_extension_undo_redo_state_changed_cb_id);
-                       wk_editor->priv->web_extension_undo_redo_state_changed_cb_id = 0;
-               }
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_ITALIC, value);
+       }
+       g_clear_object (&jsc_value);
 
-               if (wk_editor->priv->web_extension_user_changed_default_colors_cb_id) {
-                       if (connection)
-                               g_dbus_connection_signal_unsubscribe (connection, 
wk_editor->priv->web_extension_user_changed_default_colors_cb_id);
-                       wk_editor->priv->web_extension_user_changed_default_colors_cb_id = 0;
-               }
+       if (changed || forced)
+               g_object_notify (G_OBJECT (wk_editor), "italic");
+
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "underline");
+       if (jsc_value && jsc_value_is_boolean (jsc_value)) {
+               gboolean value = jsc_value_to_boolean (jsc_value);
 
-               g_clear_object (&wk_editor->priv->web_extension_proxy);
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_UNDERLINE, value);
        }
+       g_clear_object (&jsc_value);
+
+       if (changed || forced)
+               g_object_notify (G_OBJECT (wk_editor), "underline");
 
-       if (proxy) {
-               wk_editor->priv->web_extension_proxy = g_object_ref (proxy);
-
-               wk_editor->priv->web_extension_selection_changed_cb_id =
-                       g_dbus_connection_signal_subscribe (
-                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension_proxy),
-                               g_dbus_proxy_get_name (wk_editor->priv->web_extension_proxy),
-                               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);
-
-               wk_editor->priv->web_extension_content_changed_cb_id =
-                       g_dbus_connection_signal_subscribe (
-                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension_proxy),
-                               g_dbus_proxy_get_name (wk_editor->priv->web_extension_proxy),
-                               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);
-
-               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_proxy),
-                               g_dbus_proxy_get_name (wk_editor->priv->web_extension_proxy),
-                               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);
-
-               wk_editor->priv->web_extension_user_changed_default_colors_cb_id =
-                       g_dbus_connection_signal_subscribe (
-                               g_dbus_proxy_get_connection (wk_editor->priv->web_extension_proxy),
-                               g_dbus_proxy_get_name (wk_editor->priv->web_extension_proxy),
-                               E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
-                               "UserChangedDefaultColors",
-                               E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
-                               NULL,
-                               G_DBUS_SIGNAL_FLAGS_NONE,
-                               (GDBusSignalCallback) web_extension_user_changed_default_colors_cb,
-                               wk_editor,
-                               NULL);
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "strikethrough");
+       if (jsc_value && jsc_value_is_boolean (jsc_value)) {
+               gboolean value = jsc_value_to_boolean (jsc_value);
+
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_STRIKETHROUGH, value);
        }
-}
+       g_clear_object (&jsc_value);
 
-static guint64
-current_page_id (EWebKitEditor *wk_editor)
-{
-       return webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (wk_editor));
-}
+       if (changed || forced)
+               g_object_notify (G_OBJECT (wk_editor), "strikethrough");
 
-static void
-webkit_editor_call_simple_extension_function_sync (EWebKitEditor *wk_editor,
-                                                  const gchar *method_name)
-{
-       GVariant *result;
+       jsc_value = jsc_value_object_get_property (jsc_params, "script");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
+               changed = FALSE;
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT, value < 0);
+
+               if (changed || forced)
+                       g_object_notify (object, "subscript");
+
+               changed = FALSE;
+               update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_SUPERSCRIPT, value > 0);
+
+               if (changed || forced)
+                       g_object_notify (object, "superscript");
+       } else if (forced) {
+               g_object_notify (object, "subscript");
+               g_object_notify (object, "superscript");
        }
+       g_clear_object (&jsc_value);
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               method_name,
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       wk_editor->priv->temporary_style_flags = wk_editor->priv->style_flags;
 
-       if (result)
-               g_variant_unref (result);
-}
+       #undef update_style_flag
 
-static void
-webkit_editor_call_simple_extension_function (EWebKitEditor *wk_editor,
-                                              const gchar *method_name)
-{
-       guint64 page_id;
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "fontSize");
+       if (jsc_value && jsc_value_is_number (jsc_value)) {
+               gint value = jsc_value_to_int32 (jsc_value);
 
-       page_id = current_page_id (wk_editor);
+               if (value != wk_editor->priv->font_size) {
+                       wk_editor->priv->font_size = value;
+                       changed = TRUE;
+               }
+       }
+       g_clear_object (&jsc_value);
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               method_name, g_variant_new ("(t)", page_id));
-}
+       if (changed || forced)
+               g_object_notify (object, "font-size");
 
-static GVariant *
-webkit_editor_get_element_attribute (EWebKitEditor *wk_editor,
-                                     const gchar *selector,
-                                     const gchar *attribute)
-{
-       GVariant *result;
+       changed = FALSE;
+       jsc_value = jsc_value_object_get_property (jsc_params, "fontFamily");
+       if (jsc_value && jsc_value_is_string (jsc_value)) {
+               gchar *value = jsc_value_to_string (jsc_value);
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
+               if (g_strcmp0 (value, wk_editor->priv->font_name) != 0) {
+                       g_free (wk_editor->priv->font_name);
+                       wk_editor->priv->font_name = value;
+                       changed = TRUE;
+               } else {
+                       g_free (value);
+               }
        }
+       g_clear_object (&jsc_value);
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ElementGetAttributeBySelector",
-               g_variant_new ("(tss)", current_page_id (wk_editor), selector, attribute),
-               NULL);
+       if (changed || forced)
+               g_object_notify (object, "font-name");
 
-       return result;
-}
+       jsc_value = jsc_value_object_get_property (jsc_params, "bodyFontFamily");
+       if (jsc_value && jsc_value_is_string (jsc_value)) {
+               gchar *value = jsc_value_to_string (jsc_value);
 
-static void
-webkit_editor_set_element_attribute (EWebKitEditor *wk_editor,
-                                     const gchar *selector,
-                                     const gchar *attribute,
-                                     const gchar *value)
-{
-       guint64 page_id;
+               if (g_strcmp0 (value, wk_editor->priv->body_font_name) != 0) {
+                       g_free (wk_editor->priv->body_font_name);
+                       wk_editor->priv->body_font_name = value;
+               } else {
+                       g_free (value);
+               }
+       }
+       g_clear_object (&jsc_value);
+
+       if (webkit_editor_update_color_value (jsc_params, "fgColor", &wk_editor->priv->font_color) || forced)
+               g_object_notify (object, "font-color");
+
+       if (webkit_editor_update_color_value (jsc_params, "bgColor", &wk_editor->priv->background_color) || 
forced)
+               g_object_notify (object, "background-color");
 
-       page_id = current_page_id (wk_editor);
+       webkit_editor_update_color_value (jsc_params, "bodyFgColor", &wk_editor->priv->body_fg_color);
+       webkit_editor_update_color_value (jsc_params, "bodyBgColor", &wk_editor->priv->body_bg_color);
+       webkit_editor_update_color_value (jsc_params, "bodyLinkColor", &wk_editor->priv->body_link_color);
+       webkit_editor_update_color_value (jsc_params, "bodyVlinkColor", &wk_editor->priv->body_vlink_color);
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               "ElementSetAttributeBySelector", g_variant_new ("(tsss)", page_id, selector, attribute, 
value));
+       g_object_thaw_notify (object);
 }
 
 static void
-webkit_editor_remove_element_attribute (EWebKitEditor *wk_editor,
-                                        const gchar *selector,
-                                        const gchar *attribute)
+selection_changed_cb (WebKitUserContentManager *manager,
+                     WebKitJavascriptResult *js_result,
+                     gpointer user_data)
 {
-       guint64 page_id;
+       EWebKitEditor *wk_editor = user_data;
+       WebKitEditorState *editor_state;
+       JSCValue *jsc_value;
+       gboolean is_collapsed;
 
-       page_id = current_page_id (wk_editor);
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               "ElementRemoveAttributeBySelector", g_variant_new ("(tss)", page_id, selector, attribute));
-}
+       jsc_value = webkit_javascript_result_get_js_value (js_result);
+       is_collapsed = jsc_value_is_boolean (jsc_value) && jsc_value_to_boolean (jsc_value);
 
-static void
-webkit_editor_set_format_boolean (EWebKitEditor *wk_editor,
-                                  const gchar *format_dom_function,
-                                  gboolean format_value)
-{
-       guint64 page_id;
+       editor_state = webkit_web_view_get_editor_state (WEBKIT_WEB_VIEW (wk_editor));
 
-       page_id = current_page_id (wk_editor);
+       if (editor_state) {
+               GObject *object = G_OBJECT (wk_editor);
+               gboolean value;
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               format_dom_function, g_variant_new ("(tb)", page_id, format_value));
-}
+               #define check_and_set_prop(_prop_var, _prop_name, _val) \
+                       value = _val; \
+                       if (_prop_var != value) { \
+                               _prop_var = value; \
+                               g_object_notify (object, _prop_name); \
+                       }
 
-static void
-webkit_editor_set_format_int (EWebKitEditor *wk_editor,
-                              const gchar *format_dom_function,
-                              gint32 format_value)
-{
-       guint64 page_id;
+               g_object_freeze_notify (object);
+
+               check_and_set_prop (wk_editor->priv->can_copy, "can-copy", !is_collapsed);
+               check_and_set_prop (wk_editor->priv->can_cut, "can-cut", !is_collapsed);
+               check_and_set_prop (wk_editor->priv->can_paste, "can-paste", 
webkit_editor_state_is_paste_available (editor_state));
 
-       page_id = current_page_id (wk_editor);
+               g_object_thaw_notify (object);
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               format_dom_function, g_variant_new ("(ti)", page_id, format_value));
+               #undef set_prop
+       }
 }
 
 static void
-webkit_editor_set_format_string (EWebKitEditor *wk_editor,
-                                 const gchar *format_name,
-                                 const gchar *format_dom_function,
-                                 const gchar *format_value)
+undu_redo_state_changed_cb (WebKitUserContentManager *manager,
+                           WebKitJavascriptResult *js_result,
+                           gpointer user_data)
 {
-       guint64 page_id;
-
-       if (!wk_editor->priv->html_mode)
-               return;
+       EWebKitEditor *wk_editor = user_data;
+       JSCValue *jsc_value;
+       JSCValue *jsc_params;
+       gint32 state;
 
-       webkit_editor_set_changed (wk_editor, TRUE);
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (js_result != NULL);
 
-       page_id = current_page_id (wk_editor);
+       jsc_params = webkit_javascript_result_get_js_value (js_result);
+       g_return_if_fail (jsc_value_is_object (jsc_params));
 
-       e_web_extension_container_call_simple (wk_editor->priv->container, page_id, wk_editor->priv->stamp,
-               format_dom_function, g_variant_new ("(ts)", page_id, format_value));
+       jsc_value = jsc_value_object_get_property (jsc_params, "state");
+       g_return_if_fail (jsc_value_is_number (jsc_value));
+       state = jsc_value_to_int32 (jsc_value);
+       g_clear_object (&jsc_value);
 
-       g_object_notify (G_OBJECT (wk_editor), format_name);
+       webkit_editor_set_can_undo (wk_editor, (state & E_UNDO_REDO_STATE_CAN_UNDO) != 0);
+       webkit_editor_set_can_redo (wk_editor, (state & E_UNDO_REDO_STATE_CAN_REDO) != 0);
 }
 
 static void
@@ -864,7 +1142,7 @@ webkit_editor_initialize (EContentEditor *content_editor,
 
        wk_editor = E_WEBKIT_EDITOR (content_editor);
 
-       if (wk_editor->priv->web_extension_proxy) {
+       if (wk_editor->priv->webkit_load_event == WEBKIT_LOAD_FINISHED) {
                callback (content_editor, user_data);
        } else {
                g_return_if_fail (wk_editor->priv->initialized_callback == NULL);
@@ -1022,36 +1300,6 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  outline: 1px dotted red;\n"
                "}\n");
 
-       g_string_append (
-               stylesheet,
-               "body[data-evo-plain-text] "
-               "{\n"
-               "  font-family: Monospace; \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,
-               "[data-evo-paragraph] "
-               "{\n"
-               "  white-space: pre-wrap; \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 "
@@ -1059,7 +1307,7 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  border-collapse: collapse;\n"
                "  width: %dch;\n"
                "}\n",
-               g_settings_get_int (wk_editor->priv->mail_settings, "composer-word-wrap-length"));
+               wk_editor->priv->normal_paragraph_width);
 
        g_string_append (
                stylesheet,
@@ -1068,49 +1316,213 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  vertical-align: top;\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);
+       if (wk_editor->priv->html_mode) {
+               g_string_append (
+                       stylesheet,
+                       "body ul > li.-x-evo-align-center,ol > li.-x-evo-align-center "
+                       "{\n"
+                       "  list-style-position: inside;\n"
+                       "}\n");
 
-       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 ul > li.-x-evo-align-right, ol > li.-x-evo-align-right "
+                       "{\n"
+                       "  list-style-position: inside;\n"
+                       "}\n");
 
-       g_string_append (
-               stylesheet,
-               "body[data-evo-plain-text] ul > li::before "
-               "{\n"
-               "  content: \"*"UNICODE_NBSP"\";\n"
-               "}\n");
+               g_string_append (
+                       stylesheet,
+                       "body "
+                       "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[data-evo-plain-text] ul.-x-evo-indented "
-               "{\n"
-               "  -webkit-padding-start: %dch; \n"
-               "}\n", SPACES_PER_LIST_LEVEL);
+               g_string_append_printf (
+                       stylesheet,
+                       "body "
+                       "blockquote[type=cite] "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (1));
 
-       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_printf (
+                       stylesheet,
+                       "body "
+                       "blockquote[type=cite] "
+                       "blockquote[type=cite] "
+                       "{\n"
+                       "  border-color: %s;\n"
+                       "}\n",
+                       e_web_view_get_citation_color_for_level (2));
 
-       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,
+                       "body "
+                       "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 "
+                       "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 "
+                       "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));
+       } else {
+               g_string_append (
+                       stylesheet,
+                       "body "
+                       "{\n"
+                       "  font-family: Monospace; \n"
+                       "}\n");
+
+               g_string_append_printf (
+                       stylesheet,
+                       "body ul "
+                       "{\n"
+                       "  list-style: outside none;\n"
+                       "  -webkit-padding-start: %dch; \n"
+                       "}\n", SPACES_PER_LIST_LEVEL);
+
+               g_string_append_printf (
+                       stylesheet,
+                       "body ul > li "
+                       "{\n"
+                       "  list-style-position: outside;\n"
+                       "  text-indent: -%dch;\n"
+                       "}\n", SPACES_PER_LIST_LEVEL - 1);
+
+               g_string_append (
+                       stylesheet,
+                       "body ul > li::before "
+                       "{\n"
+                       "  content: \"*" UNICODE_NBSP "\";\n"
+                       "}\n");
+
+               g_string_append (
+                       stylesheet,
+                       "body ul ul > li::before, "
+                       "body ol ul > li::before "
+                       "{\n"
+                       "  content: \"-" UNICODE_NBSP "\";\n"
+                       "}\n");
+
+               g_string_append (
+                       stylesheet,
+                       "body ul ul ul > li::before, "
+                       "body ol ul ul > li::before, "
+                       "body ul ol ul > li::before, "
+                       "body ol ol ul > li::before "
+                       "{\n"
+                       "  content: \"+" UNICODE_NBSP "\";\n"
+                       "}\n");
+
+               g_string_append (
+                       stylesheet,
+                       "body ul ul ul ul > li::before, "
+                       "body ol ul ul ul > li::before, "
+                       "body ul ol ul ul > li::before, "
+                       "body ul ul ol ul > li::before, "
+                       "body ol ol ul ul > li::before, "
+                       "body ol ul ol ul > li::before, "
+                       "body ul ol ol ul > li::before, "
+                       "body ol ol ol ul > li::before "
+                       "{\n"
+                       "  content: \"*" UNICODE_NBSP "\";\n"
+                       "}\n");
+
+               g_string_append (
+                       stylesheet,
+                       "body div "
+                       "{\n"
+                       "  word-wrap: break-word; \n"
+                       "  word-break: break-word; \n"
+                       "  white-space: pre-wrap; \n"
+                       "}\n");
+
+               g_string_append (
+                       stylesheet,
+                       ".-x-evo-quoted { -webkit-user-select: none; }\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_printf (
                stylesheet,
@@ -1119,48 +1531,20 @@ webkit_editor_update_styles (EContentEditor *editor)
                "  -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 "
+               "ol,ul "
                "{\n"
-               "  text-align: center; \n"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
                "}\n");
 
        g_string_append (
                stylesheet,
-               ".-x-evo-align-right "
+               "blockquote "
                "{\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"
+               "  -webkit-margin-before: 0em; \n"
+               "  -webkit-margin-after: 0em; \n"
                "}\n");
 
        if (wk_editor->priv->html_mode) {
@@ -1208,123 +1592,6 @@ webkit_editor_update_styles (EContentEditor *editor)
 
        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 (wk_editor->priv->visually_wrap_long_lines) {
                g_string_append (
                        stylesheet,
@@ -1378,200 +1645,166 @@ webkit_editor_update_styles (EContentEditor *editor)
 }
 
 static void
-webkit_editor_page_set_text_color (EContentEditor *editor,
-                                   const GdkRGBA *value)
+webkit_editor_add_color_style (GString *css,
+                              const gchar *selector,
+                              const gchar *property,
+                              const GdkRGBA *value)
 {
-       EWebKitEditor *wk_editor;
-       gchar *color;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
+       g_return_if_fail (css != NULL);
+       g_return_if_fail (selector != NULL);
+       g_return_if_fail (property != NULL);
 
-       webkit_editor_set_element_attribute (wk_editor, "body", "text", color);
+       if (!value || value->alpha <= 1e-9)
+               return;
 
-       g_free (color);
+       g_string_append_printf (css, "%s { %s : #%06x; }\n", selector, property, e_rgba_to_value (value));
 }
 
 static void
-webkit_editor_page_get_text_color (EContentEditor *editor,
-                                   GdkRGBA *color)
+webkit_editor_set_page_color_attribute (EContentEditor *editor,
+                                       GString *script, /* serves two purposes, also says whether write to 
body or not */
+                                       const gchar *attr_name,
+                                       const GdkRGBA *value)
 {
-       EWebKitEditor *wk_editor;
-       GVariant *result;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (!wk_editor->priv->html_mode)
-               goto theme;
+       if (value && value->alpha > 1e-9) {
+               gchar color[64];
 
-       result = webkit_editor_get_element_attribute (wk_editor, "body", "text");
-       if (result) {
-               const gchar *value;
+               webkit_editor_utils_color_to_string (color, sizeof (color), value);
 
-               g_variant_get (result, "(&s)", &value);
-               if (!value || !*value || !gdk_rgba_parse (color, value)) {
-                       g_variant_unref (result);
-                       goto theme;
+               if (script) {
+                       e_web_view_jsc_printf_script_gstring (script,
+                               "document.documentElement.setAttribute(%s, %s);\n",
+                               attr_name,
+                               color);
+               } else {
+                       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                               "EvoEditor.SetBodyAttribute(%s, %s);",
+                               attr_name,
+                               color);
                }
-               g_variant_unref (result);
-               return;
+       } else if (script) {
+               e_web_view_jsc_printf_script_gstring (script,
+                       "document.documentElement.removeAttribute(%s);\n",
+                       attr_name);
+       } else {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.SetBodyAttribute(%s, null);",
+                       attr_name);
        }
-
- 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)
+webkit_editor_page_set_text_color (EContentEditor *editor,
+                                   const GdkRGBA *value)
 {
-       EWebKitEditor *wk_editor;
-       gchar *color;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       webkit_editor_set_page_color_attribute (editor, NULL, "text", value);
+}
 
-       if (value->alpha != 0.0)
-               color = g_strdup_printf ("#%06x", e_rgba_to_value (value));
-       else
-               color = g_strdup ("");
+static void
+webkit_editor_page_get_text_color (EContentEditor *editor,
+                                   GdkRGBA *color)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_set_element_attribute (wk_editor, "body", "bgcolor", color);
+       if (wk_editor->priv->html_mode &&
+           wk_editor->priv->body_fg_color) {
+               *color = *wk_editor->priv->body_fg_color;
+       } else {
+               e_utils_get_theme_color (GTK_WIDGET (wk_editor), "theme_text_color", 
E_UTILS_DEFAULT_THEME_TEXT_COLOR, color);
+       }
+}
 
-       g_free (color);
+static void
+webkit_editor_page_set_background_color (EContentEditor *editor,
+                                         const GdkRGBA *value)
+{
+       webkit_editor_set_page_color_attribute (editor, NULL, "bgcolor", value);
 }
 
 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;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       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;
+       if (wk_editor->priv->html_mode &&
+           wk_editor->priv->body_bg_color) {
+               *color = *wk_editor->priv->body_bg_color;
+       } else {
+               e_utils_get_theme_color (GTK_WIDGET (wk_editor), "theme_base_color", 
E_UTILS_DEFAULT_THEME_BASE_COLOR, color);
        }
-
- 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);
+       webkit_editor_set_page_color_attribute (editor, NULL, "link", value);
 }
 
 static void
 webkit_editor_page_get_link_color (EContentEditor *editor,
                                    GdkRGBA *color)
 {
-       EWebKitEditor *wk_editor;
-       GVariant *result;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *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;
+       if (wk_editor->priv->html_mode &&
+           wk_editor->priv->body_link_color) {
+               *color = *wk_editor->priv->body_link_color;
+       } else {
+               color->alpha = 1;
+               color->red = 0;
+               color->green = 0;
+               color->blue = 1;
        }
-
- 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);
+       webkit_editor_set_page_color_attribute (editor, NULL, "vlink", value);
 }
 
 static void
 webkit_editor_page_get_visited_link_color (EContentEditor *editor,
                                            GdkRGBA *color)
 {
-       EWebKitEditor *wk_editor;
-       GVariant *result;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       if (wk_editor->priv->html_mode &&
+           wk_editor->priv->body_vlink_color) {
+               *color = *wk_editor->priv->body_vlink_color;
+       } else {
+               color->alpha = 1;
+               color->red = 1;
+               color->green = 0;
+               color->blue = 0;
+       }
+}
 
-       if (!wk_editor->priv->html_mode)
-               goto theme;
+static void
+webkit_editor_page_set_font_name (EContentEditor *editor,
+                                 const gchar *value)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       result = webkit_editor_get_element_attribute (wk_editor, "body", "vlink");
-       if (result) {
-               const gchar *value;
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.SetBodyFontName(%s);",
+               value ? 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;
-       }
+static const gchar *
+webkit_editor_page_get_font_name (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
+
+       if (!wk_editor->priv->html_mode)
+               return NULL;
 
- theme:
-       color->alpha = 1;
-       color->red = 1;
-       color->green = 0;
-       color->blue = 0;
+       return wk_editor->priv->body_font_name;
 }
 
 static void
@@ -1611,56 +1844,95 @@ get_color_from_context (GtkStyleContext *context,
 }
 
 static void
-webkit_editor_style_updated_cb (EWebKitEditor *wk_editor)
+webkit_editor_style_updated (EWebKitEditor *wk_editor,
+                            gboolean force)
 {
-       GdkRGBA rgba;
+       EContentEditor *cnt_editor;
+       GdkRGBA bgcolor, fgcolor, link_color, vlink_color;
        GtkStateFlags state_flags;
        GtkStyleContext *style_context;
+       GString *css, *script;
        gboolean backdrop;
+       gboolean inherit_theme_colors;
 
-       /* If the user set the colors in Page dialog, this callback is useless. */
-       if (wk_editor->priv->suppress_color_changes)
-               return;
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       cnt_editor = E_CONTENT_EDITOR (wk_editor);
 
+       inherit_theme_colors = g_settings_get_boolean (wk_editor->priv->mail_settings, 
"composer-inherit-theme-colors");
        state_flags = gtk_widget_get_state_flags (GTK_WIDGET (wk_editor));
        style_context = gtk_widget_get_style_context (GTK_WIDGET (wk_editor));
        backdrop = (state_flags & GTK_STATE_FLAG_BACKDROP) != 0;
 
-       if (wk_editor->priv->html_mode && !g_settings_get_boolean (wk_editor->priv->mail_settings, 
"composer-inherit-theme-colors")) {
+       if (wk_editor->priv->html_mode && !inherit_theme_colors) {
                /* Default to white background when not inheriting theme colors */
-               rgba.red = 1.0;
-               rgba.green = 1.0;
-               rgba.blue = 1.0;
-               rgba.alpha = 1.0;
+               bgcolor.red = 1.0;
+               bgcolor.green = 1.0;
+               bgcolor.blue = 1.0;
+               bgcolor.alpha = 1.0;
        } else if (!gtk_style_context_lookup_color (
                        style_context,
                        backdrop ? "theme_unfocused_base_color" : "theme_base_color",
-                       &rgba)) {
-               gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_BASE_COLOR);
+                       &bgcolor)) {
+               gdk_rgba_parse (&bgcolor, E_UTILS_DEFAULT_THEME_BASE_COLOR);
        }
 
-       webkit_editor_page_set_background_color (E_CONTENT_EDITOR (wk_editor), &rgba);
-
-       if (wk_editor->priv->html_mode && !g_settings_get_boolean (wk_editor->priv->mail_settings, 
"composer-inherit-theme-colors")) {
+       if (wk_editor->priv->html_mode && !inherit_theme_colors) {
                /* Default to black text color when not inheriting theme colors */
-               rgba.red = 0.0;
-               rgba.green = 0.0;
-               rgba.blue = 0.0;
-               rgba.alpha = 1.0;
+               fgcolor.red = 0.0;
+               fgcolor.green = 0.0;
+               fgcolor.blue = 0.0;
+               fgcolor.alpha = 1.0;
        } else if (!gtk_style_context_lookup_color (
                        style_context,
                        backdrop ? "theme_unfocused_fg_color" : "theme_fg_color",
-                       &rgba)) {
-               gdk_rgba_parse (&rgba, E_UTILS_DEFAULT_THEME_FG_COLOR);
+                       &fgcolor)) {
+               gdk_rgba_parse (&fgcolor, E_UTILS_DEFAULT_THEME_FG_COLOR);
        }
 
-       webkit_editor_page_set_text_color (E_CONTENT_EDITOR (wk_editor), &rgba);
+       get_color_from_context (style_context, "link-color", &link_color);
+       get_color_from_context (style_context, "visited-link-color", &vlink_color);
+
+       if (!force &&
+           gdk_rgba_equal (&bgcolor, &wk_editor->priv->theme_bgcolor) &&
+           gdk_rgba_equal (&fgcolor, &wk_editor->priv->theme_fgcolor) &&
+           gdk_rgba_equal (&link_color, &wk_editor->priv->theme_link_color) &&
+           gdk_rgba_equal (&vlink_color, &wk_editor->priv->theme_vlink_color))
+               return;
+
+       wk_editor->priv->theme_bgcolor = bgcolor;
+       wk_editor->priv->theme_fgcolor = fgcolor;
+       wk_editor->priv->theme_link_color = link_color;
+       wk_editor->priv->theme_vlink_color = vlink_color;
+
+       css = g_string_sized_new (160);
+       script = g_string_sized_new (256);
+
+       webkit_editor_set_page_color_attribute (cnt_editor, script, "x-evo-bgcolor", &bgcolor);
+       webkit_editor_set_page_color_attribute (cnt_editor, script, "x-evo-text", &fgcolor);
+       webkit_editor_set_page_color_attribute (cnt_editor, script, "x-evo-link", &link_color);
+       webkit_editor_set_page_color_attribute (cnt_editor, script, "x-evo-vlink", &vlink_color);
+
+       webkit_editor_add_color_style (css, "html", "background-color", &bgcolor);
+       webkit_editor_add_color_style (css, "html", "color", &fgcolor);
+       webkit_editor_add_color_style (css, "a", "color", &link_color);
+       webkit_editor_add_color_style (css, "a:visited", "color", &vlink_color);
+
+       e_web_view_jsc_printf_script_gstring (script,
+               "EvoEditor.UpdateThemeStyleSheet(%s);",
+               css->str);
+
+       e_web_view_jsc_run_script_take (WEBKIT_WEB_VIEW (wk_editor),
+               g_string_free (script, FALSE),
+               wk_editor->priv->cancellable);
 
-       get_color_from_context (style_context, "link-color", &rgba);
-       webkit_editor_page_set_link_color (E_CONTENT_EDITOR (wk_editor), &rgba);
+       g_string_free (css, TRUE);
+}
 
-       get_color_from_context (style_context, "visited-link-color", &rgba);
-       webkit_editor_page_set_visited_link_color (E_CONTENT_EDITOR (wk_editor), &rgba);
+static void
+webkit_editor_style_updated_cb (EWebKitEditor *wk_editor)
+{
+       webkit_editor_style_updated (wk_editor, FALSE);
 }
 
 static gboolean
@@ -1698,90 +1970,23 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
        if (html_mode == wk_editor->priv->html_mode)
                return;
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMCheckIfConversionNeeded",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               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;
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "SetEditorHTMLMode",
-               g_variant_new ("(tbb)", current_page_id (wk_editor), html_mode, convert),
-               wk_editor->priv->cancellable);
+       if (html_mode) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.SetMode(EvoEditor.MODE_HTML);");
+       } else {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.SetMode(EvoEditor.MODE_PLAIN_TEXT);");
+       }
 
-       /* Update fonts - in plain text we only want monospaced */
        webkit_editor_update_styles (E_CONTENT_EDITOR (wk_editor));
-       webkit_editor_style_updated_cb (wk_editor);
-
-       g_object_notify (G_OBJECT (wk_editor), "html-mode");
-}
-
-static void
-set_convert_in_situ (EWebKitEditor *wk_editor,
-                     gboolean value)
-{
-       GVariant *result;
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "SetConvertInSitu",
-               g_variant_new ("(tbnn)", current_page_id (wk_editor), value,
-                       e_webkit_editor_three_state_to_int16 (e_content_editor_get_start_bottom 
(E_CONTENT_EDITOR (wk_editor))),
-                       e_webkit_editor_three_state_to_int16 (e_content_editor_get_top_signature 
(E_CONTENT_EDITOR (wk_editor)))),
-               NULL);
-
-       if (result)
-               g_variant_unref (result);
-}
-
-static void
-e_webkit_editor_load_data (EWebKitEditor *wk_editor,
-                          const gchar *html)
-{
-       gchar *uri_with_stamp;
-
-       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
-
-       if (!html)
-               html = "";
-
-       /* Make WebKit think we are displaying a local file, so that it
-        * does not block loading resources from file:// protocol */
-       uri_with_stamp = g_strdup_printf ("file:///?evo-stamp=%d", wk_editor->priv->stamp);
-
-       webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), html, uri_with_stamp);
-
-       g_free (uri_with_stamp);
+       webkit_editor_style_updated (wk_editor, FALSE);
 }
 
 static void
@@ -1797,8 +2002,7 @@ webkit_editor_insert_content (EContentEditor *editor,
         * 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) {
+       if (wk_editor->priv->webkit_load_event != WEBKIT_LOAD_FINISHED) {
                webkit_editor_queue_post_reload_operation (
                        wk_editor,
                        (PostReloadOperationFunc) webkit_editor_insert_content,
@@ -1808,51 +2012,17 @@ webkit_editor_insert_content (EContentEditor *editor,
                return;
        }
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               /* 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 */
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMConvertAndInsertHTMLIntoSelection",
-                       g_variant_new (
-                               "(tsb)",
-                               current_page_id (wk_editor),
-                               content,
-                               (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML)),
-                       wk_editor->priv->cancellable);
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.InsertContent(%s, %x, %x);",
+                       content, (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0, FALSE);
        } 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;
-                       e_webkit_editor_load_data (wk_editor, content);
-                       return;
-               }
-
-               if (strstr (content, "data-evo-draft") && !(wk_editor->priv->html_mode)) {
-                       set_convert_in_situ (wk_editor, TRUE);
-                       wk_editor->priv->reload_in_progress = TRUE;
-                       e_webkit_editor_load_data (wk_editor, content);
+                       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                               "EvoEditor.LoadHTML(%s);", content);
                        return;
                }
 
@@ -1861,200 +2031,222 @@ webkit_editor_insert_content (EContentEditor *editor,
                        if (strstr (content, "<!-- text/html -->") &&
                            !strstr (content, "<!-- disable-format-prompt -->")) {
                                if (!show_lose_formatting_dialog (wk_editor)) {
-                                       set_convert_in_situ (wk_editor, FALSE);
-                                       wk_editor->priv->reload_in_progress = TRUE;
                                        webkit_editor_set_html_mode (wk_editor, TRUE);
-                                       e_webkit_editor_load_data (wk_editor, content);
+                                       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), 
wk_editor->priv->cancellable,
+                                               "EvoEditor.LoadHTML(%s);", content);
                                        return;
                                }
                        }
-                       set_convert_in_situ (wk_editor, TRUE);
                }
 
-               wk_editor->priv->reload_in_progress = TRUE;
-               e_webkit_editor_load_data (wk_editor, content);
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.LoadHTML(%s);", content);
        } else if ((flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
                   (flags & E_CONTENT_EDITOR_INSERT_TEXT_PLAIN)) {
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMConvertContent",
-                       g_variant_new ("(tsnn)", current_page_id (wk_editor), content,
-                               e_webkit_editor_three_state_to_int16 (e_content_editor_get_start_bottom 
(editor)),
-                               e_webkit_editor_three_state_to_int16 (e_content_editor_get_top_signature 
(editor))),
-                       wk_editor->priv->cancellable);
-       } 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 */
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMConvertAndInsertHTMLIntoSelection",
-                       g_variant_new (
-                               "(tsb)", current_page_id (wk_editor), content, TRUE),
-                       wk_editor->priv->cancellable);
-       } else if ((flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT) &&
-                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
-               /* e_html_editor_view_paste_clipboard_quoted */
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMQuoteAndInsertTextIntoSelection",
-                       g_variant_new (
-                               "(tsb)", current_page_id (wk_editor), content, (flags & 
E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0),
-                       wk_editor->priv->cancellable);
-       } else if (!(flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
-                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
-               /* e_html_editor_view_insert_html */
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMInsertHTML",
-                       g_variant_new (
-                               "(ts)", current_page_id (wk_editor), content),
-                       wk_editor->priv->cancellable);
-       } else
-               g_warning ("Unsupported flags combination (%d) in (%s)", flags, G_STRFUNC);
-}
+               gchar *html, **lines;
+               gint ii;
 
-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;
+               lines = g_strsplit (content ? content : "", "\n", -1);
 
-       base64_encoded_data = strstr (element_src, ";base64,");
-       if (!base64_encoded_data)
-               goto out;
+               for (ii = 0; lines && lines[ii]; ii++) {
+                       gchar *line = lines[ii];
+                       gint len = strlen (line);
 
-       mime_type = g_strndup (
-               element_src + 5,
-               base64_encoded_data - (strstr (element_src, "data:") + 5));
+                       if (len > 0 && line[len - 1] == '\r') {
+                               line[len - 1] = 0;
+                               len--;
+                       }
 
-       /* Move to actual data */
-       base64_encoded_data += 8;
+                       if (len)
+                               lines[ii] = g_markup_printf_escaped ("<div>%s</div>", line);
+                       else
+                               lines[ii] = g_strdup ("<div><br></div>");
 
-       base64_decoded_data = g_base64_decode (base64_encoded_data, &decoded_size);
+                       g_free (line);
+               }
 
-       stream = camel_stream_mem_new ();
-       size = camel_stream_write (
-               stream, (gchar *) base64_decoded_data, decoded_size, NULL, NULL);
+               html = g_strjoinv ("", lines);
 
-       if (size == -1)
-               goto out;
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.LoadHTML(%s);", html);
 
-       wrapper = camel_data_wrapper_new ();
-       camel_data_wrapper_construct_from_stream_sync (
-               wrapper, stream, NULL, NULL);
-       g_object_unref (stream);
+               g_strfreev (lines);
+               g_free (html);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
+                 !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL) &&
+                 !(flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT)) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.InsertContent(%s, %x, %x);",
+                       content, TRUE, FALSE);
+       } else if ((flags & E_CONTENT_EDITOR_INSERT_QUOTE_CONTENT) &&
+                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.InsertContent(%s, %x, %x);",
+                       content, (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0, TRUE);
+       } else if (!(flags & E_CONTENT_EDITOR_INSERT_CONVERT) &&
+                  !(flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)) {
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.InsertContent(%s, %x, %x);",
+                       content, (flags & E_CONTENT_EDITOR_INSERT_TEXT_HTML) != 0, FALSE);
+       } else {
+               g_warning ("%s: Unsupported flags combination (0x%x)", G_STRFUNC, flags);
+       }
 
-       camel_data_wrapper_set_mime_type (wrapper, mime_type);
+       if (flags & E_CONTENT_EDITOR_INSERT_REPLACE_ALL)
+               webkit_editor_style_updated (wk_editor, TRUE);
+}
 
-       part = camel_mime_part_new ();
-       camel_medium_set_content (CAMEL_MEDIUM (part), wrapper);
-       g_object_unref (wrapper);
+static void
+webkit_editor_get_content (EContentEditor *editor,
+                          guint32 flags, /* bit-or of EContentEditorGetContentFlags */
+                          const gchar *inline_images_from_domain,
+                           GCancellable *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data)
+{
+       gchar *script, *cid_uid_prefix;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+
+       cid_uid_prefix = camel_header_msgid_generate (inline_images_from_domain ? inline_images_from_domain : 
"");
+       script = e_web_view_jsc_printf_script ("EvoEditor.GetContent(%d, %s)", flags, cid_uid_prefix);
 
-       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);
+       webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (editor), script, cancellable, callback, user_data);
 
-       return part;
+       g_free (cid_uid_prefix);
+       g_free (script);
 }
 
-static GSList *
-webkit_editor_get_parts_for_inline_images (GVariant *images)
+static EContentEditorContentHash *
+webkit_editor_get_content_finish (EContentEditor *editor,
+                                 GAsyncResult *result,
+                                 GError **error)
 {
-       const gchar *element_src, *name, *id;
-       GVariantIter *iter;
-       GSList *parts = NULL;
+       WebKitJavascriptResult *js_result;
+       EContentEditorContentHash *content_hash = NULL;
+       GError *local_error = 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;
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (editor), NULL);
+       g_return_val_if_fail (result != NULL, NULL);
 
-                       part = create_part_for_inline_image_from_element_data (
-                               element_src, name, id);
-                       parts = g_slist_prepend (parts, part);
-               }
-               g_variant_iter_free (iter);
-       }
+       js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (editor), result, &local_error);
 
-       return parts ? g_slist_reverse (parts) : NULL;
-}
+       if (local_error) {
+               g_propagate_error (error, local_error);
 
-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;
-       GError *local_error = NULL;
+               if (js_result)
+                       webkit_javascript_result_unref (js_result);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy)
                return NULL;
+       }
 
-       if ((flags & E_CONTENT_EDITOR_GET_TEXT_HTML) &&
-           !(flags & E_CONTENT_EDITOR_GET_PROCESSED) &&
-            !(flags & E_CONTENT_EDITOR_GET_BODY))
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMEmbedStyleSheet",
-                       g_variant_new (
-                               "(ts)",
-                               current_page_id (wk_editor),
-                               wk_editor->priv->current_user_stylesheet),
-                       wk_editor->priv->cancellable);
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetContent",
-               g_variant_new (
-                       "(tsi)",
-                       current_page_id (wk_editor),
-                       inline_images_from_domain ? inline_images_from_domain : "",
-                       (gint32) flags),
-               wk_editor->priv->cancellable,
-               &local_error);
-
-       webkit_editor_set_last_error (wk_editor, local_error);
-       g_clear_error (&local_error);
-
-       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 (js_result) {
+               JSCException *exception;
+               JSCValue *value;
 
-       if (result) {
-               GVariant *images = NULL;
-               gchar *value = NULL;
+               value = webkit_javascript_result_get_js_value (js_result);
+               exception = jsc_context_get_exception (jsc_value_get_context (value));
+
+               if (exception) {
+                       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EvoEditor.GetContent() call 
failed: %s", jsc_exception_get_message (exception));
+                       jsc_context_clear_exception (jsc_value_get_context (value));
+                       webkit_javascript_result_unref (js_result);
+                       return NULL;
+               }
+
+               if (jsc_value_is_object (value)) {
+                       struct _formats {
+                               const gchar *name;
+                               guint32 flags;
+                       } formats[] = {
+                               { "raw-body-html", E_CONTENT_EDITOR_GET_RAW_BODY_HTML },
+                               { "raw-body-plain", E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN },
+                               { "raw-body-stripped", E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED },
+                               { "raw-draft", E_CONTENT_EDITOR_GET_RAW_DRAFT },
+                               { "to-send-html", E_CONTENT_EDITOR_GET_TO_SEND_HTML },
+                               { "to-send-plain", E_CONTENT_EDITOR_GET_TO_SEND_PLAIN }
+                       };
+                       JSCValue *images_value;
+                       gint ii;
+
+                       content_hash = e_content_editor_util_new_content_hash ();
+
+                       for (ii = 0; ii < G_N_ELEMENTS (formats); ii++) {
+                               gchar *cnt;
+
+                               cnt = e_web_view_jsc_get_object_property_string (value, formats[ii].name, 
NULL);
+                               if (cnt)
+                                       e_content_editor_util_take_content_data (content_hash, 
formats[ii].flags, cnt, g_free);
+                       }
+
+                       images_value = jsc_value_object_get_property (value, "images");
+
+                       if (images_value) {
+                               if (jsc_value_is_array (images_value)) {
+                                       GSList *image_parts = NULL;
+                                       gint length;
+
+                                       length = e_web_view_jsc_get_object_property_int32 (images_value, 
"length", 0);
+
+                                       for (ii = 0; ii < length; ii++) {
+                                               JSCValue *item_value;
+
+                                               item_value = jsc_value_object_get_property_at_index 
(images_value, ii);
+
+                                               if (!item_value ||
+                                                   jsc_value_is_null (item_value) ||
+                                                   jsc_value_is_undefined (item_value)) {
+                                                       g_warn_if_reached ();
+                                                       g_clear_object (&item_value);
+                                                       break;
+                                               }
+
+                                               if (jsc_value_is_object (item_value)) {
+                                                       gchar *src, *cid, *name;
+
+                                                       src = e_web_view_jsc_get_object_property_string 
(item_value, "src", NULL);
+                                                       cid = e_web_view_jsc_get_object_property_string 
(item_value, "cid", NULL);
+                                                       name = e_web_view_jsc_get_object_property_string 
(item_value, "name", NULL);
+
+                                                       if (src && *src && cid && *cid) {
+                                                               CamelMimePart *part = NULL;
+
+                                                               if (g_ascii_strncasecmp (src, "cid:", 4) == 0)
+                                                                       part = 
e_content_editor_emit_ref_mime_part (editor, src);
 
-               g_variant_get (result, "(sv)", &value, &images);
-               if (inline_images_parts)
-                       *inline_images_parts = webkit_editor_get_parts_for_inline_images (images);
+                                                               if (!part) {
+                                                                       part = 
e_content_editor_util_create_data_mimepart (src, cid, TRUE, name, NULL,
+                                                                               E_WEBKIT_EDITOR 
(editor)->priv->cancellable);
+                                                               }
 
-               if (images)
-                       g_variant_unref (images);
+                                                               if (part)
+                                                                       image_parts = g_slist_prepend 
(image_parts, part);
+                                                       }
 
-               g_variant_unref (result);
+                                                       g_free (name);
+                                                       g_free (src);
+                                                       g_free (cid);
+                                               }
 
-               return value;
+                                               g_clear_object (&item_value);
+                                       }
+
+                                       if (image_parts)
+                                               e_content_editor_util_take_content_data_images (content_hash, 
g_slist_reverse (image_parts));
+                               } else if (!jsc_value_is_undefined (images_value) && !jsc_value_is_null 
(images_value)) {
+                                       g_warn_if_reached ();
+                               }
+
+                               g_clear_object (&images_value);
+                       }
+               } else {
+                       g_warn_if_reached ();
+               }
+
+               webkit_javascript_result_unref (js_result);
        }
 
-       return NULL;
+       return content_hash;
 }
 
 static gboolean
@@ -2070,9 +2262,12 @@ webkit_editor_undo (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, "DOMUndo");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoUndoRedo.Undo();");
 }
 
 static gboolean
@@ -2092,33 +2287,25 @@ webkit_editor_redo (EContentEditor *editor)
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (wk_editor, "DOMRedo");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoUndoRedo.Redo();");
 }
 
 static void
 webkit_editor_move_caret_on_coordinates (EContentEditor *editor,
-                                         gint x,
-                                         gint y,
-                                         gboolean cancel_if_not_collapsed)
+                                        gint xx,
+                                        gint yy,
+                                        gboolean cancel_if_not_collapsed)
 {
        EWebKitEditor *wk_editor;
-       GVariant *result;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMMoveSelectionOnPoint",
-               g_variant_new (
-                       "(tiib)", current_page_id (wk_editor), x, y, cancel_if_not_collapsed),
-               NULL);
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (result)
-               g_variant_unref (result);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.MoveSelectionToPoint(%d, %d, %x);",
+               xx, yy, cancel_if_not_collapsed);
 }
 
 static void
@@ -2126,77 +2313,36 @@ webkit_editor_insert_emoticon (EContentEditor *editor,
                                EEmoticon *emoticon)
 {
        EWebKitEditor *wk_editor;
+       GSettings *settings;
+       const gchar *text;
+       gchar *image_uri = NULL;
+       gint width = 0, height = 0;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMInsertSmiley",
-               g_variant_new (
-                       "(ts)", current_page_id (wk_editor), e_emoticon_get_name (emoticon)),
-               wk_editor->priv->cancellable);
-}
-
-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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       dw = camel_medium_get_content (CAMEL_MEDIUM (part));
-       g_return_if_fail (dw);
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+       g_return_if_fail (emoticon != NULL);
 
-       stream = camel_stream_mem_new ();
-       camel_data_wrapper_decode_to_stream_sync (dw, stream, NULL, NULL);
-       camel_stream_close (stream, NULL, NULL);
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
 
-       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream));
+       if (g_settings_get_boolean (settings, "composer-unicode-smileys")) {
+               text = emoticon->unicode_character;
+       } else {
+               text = emoticon->text_face;
+               image_uri = e_emoticon_get_uri (emoticon);
 
-       if (!byte_array->data) {
-               g_object_unref (stream);
-               return;
+               if (image_uri) {
+                       width = 16;
+                       height = 16;
+               }
        }
 
-       base64_encoded = g_base64_encode ((const guchar *) byte_array->data, byte_array->len);
-
-       mime_type = camel_data_wrapper_get_mime_type (dw);
-       name = camel_mime_part_get_filename (part);
-       /* Insert file name before new src */
-       src = g_strconcat (name ? name : "", 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);
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMAddNewInlineImageIntoList",
-               g_variant_new ("(tsss)", current_page_id (wk_editor), name ? name : "", cid_uri, src),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.InsertEmoticon(%s, %s, %d, %d);",
+               text, image_uri, width, height);
 
-       g_free (base64_encoded);
-       g_free (mime_type);
-       g_free (cid_uri);
-       g_free (src);
-       g_object_unref (stream);
+       g_clear_object (&settings);
+       g_free (image_uri);
 }
 
 static void
@@ -2204,6 +2350,8 @@ webkit_editor_select_all (EContentEditor *editor)
 {
        EWebKitEditor *wk_editor;
 
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
+
        wk_editor = E_WEBKIT_EDITOR (editor);
 
        webkit_web_view_execute_editing_command (
@@ -2215,17 +2363,20 @@ webkit_editor_selection_wrap (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, "DOMSelectionWrap");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.WrapSelection();");
 }
 
 static gboolean
-webkit_editor_selection_is_indented (EWebKitEditor *wk_editor)
+webkit_editor_get_indent_level (EWebKitEditor *wk_editor)
 {
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
 
-       return wk_editor->priv->is_indented;
+       return wk_editor->priv->indent_level;
 }
 
 static void
@@ -2235,8 +2386,8 @@ webkit_editor_selection_indent (EContentEditor *editor)
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "DOMSelectionIndent");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.Indent(true);");
 }
 
 static void
@@ -2246,37 +2397,20 @@ webkit_editor_selection_unindent (EContentEditor *editor)
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "DOMSelectionUnindent");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.Indent(false);");
 }
 
 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);
+       webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (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);
+       webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (editor), WEBKIT_EDITING_COMMAND_COPY);
 }
 
 static ESpellChecker *
@@ -2290,28 +2424,9 @@ webkit_editor_get_spell_checker (EWebKitEditor *wk_editor)
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetCaretWord",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), "EvoEditor.GetCaretWord();"),
                NULL);
-
-       if (result) {
-               g_variant_get (result, "(s)", &ret_val);
-               g_variant_unref (result);
-       }
-
-       return ret_val;
 }
 
 static void
@@ -2337,6 +2452,10 @@ webkit_editor_set_start_bottom (EWebKitEditor *wk_editor,
 
        wk_editor->priv->start_bottom = value;
 
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.START_BOTTOM = %x;",
+               e_webkit_editor_three_state_to_bool (value, "composer-reply-start-bottom"));
+
        g_object_notify (G_OBJECT (wk_editor), "start-bottom");
 }
 
@@ -2359,6 +2478,10 @@ webkit_editor_set_top_signature (EWebKitEditor *wk_editor,
 
        wk_editor->priv->top_signature = value;
 
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.TOP_SIGNATURE = %x;",
+               e_webkit_editor_three_state_to_bool (value, "composer-top-signature"));
+
        g_object_notify (G_OBJECT (wk_editor), "top-signature");
 }
 
@@ -2383,9 +2506,6 @@ webkit_editor_set_spell_check_enabled (EWebKitEditor *wk_editor,
 
        wk_editor->priv->spell_check_enabled = enable;
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, enable ? "DOMForceSpellCheck" : "DOMTurnSpellCheckOff");
-
        web_context = webkit_web_view_get_context (WEBKIT_WEB_VIEW (wk_editor));
        webkit_web_context_set_spell_checking_enabled (web_context, enable);
 
@@ -2441,51 +2561,29 @@ webkit_editor_set_editable (EWebKitEditor *wk_editor,
        return webkit_web_view_set_editable (WEBKIT_WEB_VIEW (wk_editor), editable);
 }
 
-static gchar *
-webkit_editor_get_current_signature_uid (EContentEditor *editor)
+static gboolean
+webkit_editor_is_ready (EContentEditor *editor)
 {
        EWebKitEditor *wk_editor;
-       gchar *ret_val= NULL;
-       GVariant *result;
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetActiveSignatureUid",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(s)", &ret_val);
-               g_variant_unref (result);
-       }
-
-       return ret_val;
+       /* Editor is ready just in case that the web view is not loading. */
+       return wk_editor->priv->webkit_load_event == WEBKIT_LOAD_FINISHED &&
+               !webkit_web_view_is_loading (WEBKIT_WEB_VIEW (wk_editor));
 }
 
-static gboolean
-webkit_editor_is_ready (EContentEditor *editor)
+static gchar *
+webkit_editor_get_current_signature_uid (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (editor), NULL);
 
-       /* Editor is ready just in case that the web view is not loading, there
-        * is no reload in progress and there is no pending post reload operation
-        * and the web extension for the editor is created. */
-       return !webkit_web_view_is_loading (WEBKIT_WEB_VIEW (wk_editor)) &&
-               !wk_editor->priv->reload_in_progress &&
-               wk_editor->priv->web_extension_proxy &&
-               (!wk_editor->priv->post_reload_operations || g_queue_is_empty 
(wk_editor->priv->post_reload_operations));
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), "EvoEditor.GetCurrentSignatureUid();"),
+               NULL);
 }
 
-static char *
+static gchar *
 webkit_editor_insert_signature (EContentEditor *editor,
                                 const gchar *content,
                                 gboolean is_html,
@@ -2494,101 +2592,42 @@ webkit_editor_insert_signature (EContentEditor *editor,
                                 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMInsertSignature",
-               g_variant_new (
-                       "(tsbsbbbnn)",
-                       current_page_id (wk_editor),
-                       content ? content : "",
-                       is_html,
-                       signature_id,
-                       *set_signature_from_message,
-                       *check_if_signature_is_changed,
-                       *ignore_next_signature_change,
-                       e_webkit_editor_three_state_to_int16 (e_content_editor_get_start_bottom (editor)),
-                       e_webkit_editor_three_state_to_int16 (e_content_editor_get_top_signature (editor))),
-               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;
+       JSCValue *jsc_value;
+       gchar *res = NULL, *tmp = NULL;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (editor), NULL);
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetCaretPosition",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       if (!is_html && content && *content) {
+               tmp = camel_text_to_html (content, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
 
-       if (result) {
-               g_variant_get (result, "(u)", &ret_val);
-               g_variant_unref (result);
+               if (tmp)
+                       content = tmp;
        }
 
-       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);
+       jsc_value = webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor),
+               "EvoEditor.InsertSignature(%s, %x, %s, %x, %x, %x, %x, %x, %x);",
+               content ? content : "",
+               is_html,
+               signature_id,
+               *set_signature_from_message,
+               *check_if_signature_is_changed,
+               *ignore_next_signature_change,
+               e_webkit_editor_three_state_to_bool (e_content_editor_get_start_bottom (editor), 
"composer-reply-start-bottom"),
+               e_webkit_editor_three_state_to_bool (e_content_editor_get_top_signature (editor), 
"composer-top-signature"),
+               !e_webkit_editor_three_state_to_bool (E_THREE_STATE_INCONSISTENT, 
"composer-no-signature-delim"));
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
+       g_free (tmp);
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetCaretOffset",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       if (jsc_value) {
+               *set_signature_from_message = e_web_view_jsc_get_object_property_boolean (jsc_value, 
"fromMessage", FALSE);
+               *check_if_signature_is_changed = e_web_view_jsc_get_object_property_boolean (jsc_value, 
"checkChanged", FALSE);
+               *ignore_next_signature_change = e_web_view_jsc_get_object_property_boolean (jsc_value, 
"ignoreNextChange", FALSE);
+               res = e_web_view_jsc_get_object_property_string (jsc_value, "newUid", NULL);
 
-       if (result) {
-               g_variant_get (result, "(u)", &ret_val);
-               g_variant_unref (result);
+               g_clear_object (&jsc_value);
        }
 
-       return ret_val;
+       return res;
 }
 
 static void
@@ -2596,18 +2635,12 @@ webkit_editor_clear_undo_redo_history (EContentEditor *editor)
 {
        EWebKitEditor *wk_editor;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMClearUndoRedoHistory",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoUndoRedo.Clear();");
 }
 
 static void
@@ -2615,22 +2648,13 @@ webkit_editor_replace_caret_word (EContentEditor *editor,
                                   const gchar *replacement)
 {
        EWebKitEditor *wk_editor;
-       GVariant *result;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMReplaceCaretWord",
-               g_variant_new ("(ts)", current_page_id (wk_editor), replacement),
-               wk_editor->priv->cancellable);
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (result)
-               g_variant_unref (result);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.ReplaceCaretWord(%s);", replacement);
 }
 
 static void
@@ -2683,22 +2707,13 @@ webkit_editor_replace (EContentEditor *editor,
                        const gchar *replacement)
 {
        EWebKitEditor *wk_editor;
-       GVariant *result;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMSelectionReplace",
-               g_variant_new ("(ts)", current_page_id (wk_editor), replacement),
-               wk_editor->priv->cancellable);
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (result)
-               g_variant_unref (result);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.ReplaceSelection(%s);", replacement);
 }
 
 static gboolean
@@ -2723,11 +2738,8 @@ webkit_find_controller_found_text_cb (WebKitFindController *find_controller,
                /* Repeatedly search for 'word', then replace selection by
                 * 'replacement'. Repeat until there's at least one occurrence of
                 * 'word' in the document */
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "DOMSelectionReplace",
-                       g_variant_new ("(ts)", current_page_id (wk_editor), wk_editor->priv->replace_with),
-                       wk_editor->priv->cancellable);
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.ReplaceSelection(%s);", wk_editor->priv->replace_with);
 
                g_idle_add ((GSourceFunc) search_next_on_idle, wk_editor);
        } else {
@@ -2744,25 +2756,8 @@ webkit_find_controller_failed_to_find_text_cb (WebKitFindController *find_contro
        if (wk_editor->priv->performing_replace_all) {
                guint replaced_count = wk_editor->priv->replaced_count;
 
-               if (replaced_count > 0) {
-                       if (!wk_editor->priv->web_extension_proxy) {
-                               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-                       } else {
-                               GVariant *result;
-
-                               result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-                                       wk_editor->priv->web_extension_proxy,
-                                       "DOMInsertReplaceAllHistoryEvent",
-                                       g_variant_new ("(tss)",
-                                               current_page_id (wk_editor),
-                                               webkit_find_controller_get_search_text (find_controller),
-                                               wk_editor->priv->replace_with),
-                                       NULL);
-
-                               if (result)
-                                       g_variant_unref (result);
-                       }
-               }
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, %s);", "ReplaceAll");
 
                webkit_editor_finish_search (wk_editor);
                e_content_editor_emit_replace_all_done (E_CONTENT_EDITOR (wk_editor), replaced_count);
@@ -2857,6 +2852,9 @@ webkit_editor_replace_all (EContentEditor *editor,
        wk_editor->priv->performing_replace_all = TRUE;
        wk_editor->priv->replaced_count = 0;
 
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, %s);", "ReplaceAll");
+
        webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), 
"MoveToBeginningOfDocumentAndModifySelection");
 
        webkit_find_controller_search (wk_editor->priv->find_controller, find_text, wk_options, G_MAXUINT);
@@ -2867,10 +2865,12 @@ webkit_editor_selection_save (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, "DOMSaveSelection");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.StoreSelection();");
 }
 
 static void
@@ -2878,210 +2878,186 @@ webkit_editor_selection_restore (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, "DOMRestoreSelection");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.RestoreSelection();");
 }
 
 static void
-webkit_editor_delete_cell_contents (EContentEditor *editor)
+webkit_editor_on_dialog_open (EContentEditor *editor,
+                             const gchar *name)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.OnDialogOpen(%s);", name);
+
+       if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_SPELLCHECK) == 0) {
+               gchar **strv;
+
+               strv = e_spell_checker_list_active_languages (wk_editor->priv->spell_checker, NULL);
+
+               if (strv) {
+                       gint ii, len = 0;
+                       gchar *langs, *ptr;
+
+                       for (ii = 0; strv[ii]; ii++) {
+                               len += strlen (strv[ii]) + 1;
+                       }
+
+                       len++;
+
+                       langs = g_slice_alloc0 (len);
+                       ptr = langs;
+
+                       for (ii = 0; strv[ii]; ii++) {
+                               strcpy (ptr, strv[ii]);
+                               ptr += strlen (strv[ii]);
+                               if (strv[ii + 1]) {
+                                       *ptr = '|';
+                                       ptr++;
+                               }
+                       }
+
+                       *ptr = '\0';
+
+                       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                               "EvoEditor.SetSpellCheckLanguages(%s);", langs);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogDeleteCellContents");
+                       g_slice_free1 (len, langs);
+                       g_strfreev (strv);
+               }
+       }
 }
 
 static void
-webkit_editor_delete_column (EContentEditor *editor)
+webkit_editor_on_dialog_close (EContentEditor *editor,
+                              const gchar *name)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.OnDialogClose(%s);", name);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogDeleteColumn");
+       if (g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_SPELLCHECK) == 0 ||
+           g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_FIND) == 0 ||
+           g_strcmp0 (name, E_CONTENT_EDITOR_DIALOG_REPLACE) == 0)
+               webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
 }
 
 static void
-webkit_editor_delete_row (EContentEditor *editor)
+webkit_editor_delete_cell_contents (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableDeleteCellContent();");
+}
+
+static void
+webkit_editor_delete_column (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogDeleteRow");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableDeleteColumn();");
 }
 
 static void
-webkit_editor_delete_table (EContentEditor *editor)
+webkit_editor_delete_row (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableDeleteRow();");
+}
+
+static void
+webkit_editor_delete_table (EContentEditor *editor)
+{
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogDeleteTable");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableDelete();");
 }
 
 static void
 webkit_editor_insert_column_after (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogInsertColumnAfter");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableInsert(%s, %d);", "column", +1);
 }
 
 static void
 webkit_editor_insert_column_before (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogInsertColumnBefore");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableInsert(%s, %d);", "column", -1);
 }
 
-
 static void
 webkit_editor_insert_row_above (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogInsertRowAbove");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableInsert(%s, %d);", "row", -1);
 }
 
 static void
 webkit_editor_insert_row_below (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorDialogInsertRowBelow");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableInsert(%s, %d);", "row", +1);
 }
 
-static gboolean
-webkit_editor_on_h_rule_dialog_open (EContentEditor *editor)
+static void
+webkit_editor_h_rule_set_align (EContentEditor *editor,
+                                const gchar *value)
 {
-       EWebKitEditor *wk_editor;
-       gboolean value = FALSE;
-       GVariant *result;
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "align", value);
+}
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return FALSE;
-       }
+static gchar *
+webkit_editor_h_rule_get_align (EContentEditor *editor)
+{
+       gchar *value;
 
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorHRuleDialogFindHRule",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       value = webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "align");
 
-       if (result) {
-               g_variant_get (result, "(b)", &value);
-               g_variant_unref (result);
+       if (!value || !*value) {
+               g_free (value);
+               value = g_strdup ("center");
        }
 
        return value;
 }
 
 static void
-webkit_editor_on_h_rule_dialog_close (EContentEditor *editor)
+webkit_editor_h_rule_set_size (EContentEditor *editor,
+                               gint value)
 {
-       EWebKitEditor *wk_editor;
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "size", value);
+}
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+static gint
+webkit_editor_h_rule_get_size (EContentEditor *editor)
+{
+       gint size;
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorHRuleDialogOnClose");
-}
+       size = webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "size", 2);
 
-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);
-       }
+       if (!size)
+               size = 2;
 
        return size;
 }
@@ -3091,45 +3067,20 @@ 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);
+       webkit_editor_dialog_utils_set_attribute_with_unit (E_WEBKIT_EDITOR (editor), NULL, "width", value, 
unit);
 }
 
 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);
+       gint value;
 
-       *unit = E_CONTENT_EDITOR_UNIT_PIXEL;
+       value = webkit_editor_dialog_utils_get_attribute_with_unit (E_WEBKIT_EDITOR (editor), NULL, "width", 
0, unit);
 
-       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);
+       if (!value && *unit == E_CONTENT_EDITOR_UNIT_AUTO) {
+               *unit = E_CONTENT_EDITOR_UNIT_PERCENTAGE;
+               value = 100;
        }
 
        return value;
@@ -3139,89 +3090,42 @@ 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_proxy) {
-               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");
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "noshade", value ? "" : 
NULL);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return FALSE;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ElementHasAttribute",
-               g_variant_new ("(tss)", current_page_id (wk_editor), "-x-evo-current-hr", "noshade"),
-               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");
+       return webkit_editor_dialog_utils_has_attribute (E_WEBKIT_EDITOR (editor), "noshade");
 }
 
 static void
-webkit_editor_on_image_dialog_close (EContentEditor *editor)
+webkit_editor_insert_image (EContentEditor *editor,
+                            const gchar *image_uri)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
+       gint width = -1, height = -1;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       g_return_if_fail (image_uri != NULL);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorImageDialogSaveHistoryOnExit");
-}
+       if (g_ascii_strncasecmp (image_uri, "file://", 7) == 0) {
+               gchar *filename;
 
-static void
-webkit_editor_insert_image (EContentEditor *editor,
-                            const gchar *image_uri)
-{
-       EWebKitEditor *wk_editor;
+               filename = g_filename_from_uri (image_uri, NULL, NULL);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+               if (filename) {
+                       if (!gdk_pixbuf_get_file_info (filename, &width, &height)) {
+                               width = -1;
+                               height = -1;
+                       }
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
+                       g_free (filename);
+               }
        }
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMSelectionInsertImage",
-               g_variant_new ("(ts)", current_page_id (wk_editor), image_uri),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.InsertImage(%s, %d, %d);",
+               image_uri, width, height);
 }
 
 static void
@@ -3229,565 +3133,205 @@ webkit_editor_replace_image_src (EWebKitEditor *wk_editor,
                                  const gchar *selector,
                                  const gchar *image_uri)
 {
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMReplaceImageSrc",
-               g_variant_new ("(tss)", current_page_id (wk_editor), selector, image_uri),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.ReplaceImageSrc(%s, %s);",
+               selector,
+               image_uri);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "src", 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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "src");
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "alt");
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorImageDialogSetElementUrl",
-               g_variant_new ("(ts)", current_page_id (wk_editor), value),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsSetImageUrl(%s);",
+               value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorImageDialogGetElementUrl",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), "EvoEditor.DialogUtilsGetImageUrl();"),
                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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementSetVSpace",
-               g_variant_new (
-                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "vspace", value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetVSpace",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "vspace", 0);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementSetHSpace",
-               g_variant_new (
-                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "hspace", value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetHSpace",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "hspace", 0);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "border", value);
 }
 
 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;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "border", 0);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "align");
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetNaturalWidth",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsGetImageWidth(true);"),
+               0);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetNaturalHeight",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsGetImageHeight(true);"),
+               0);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementSetHeight",
-               g_variant_new (
-                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "height", value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementSetWidth",
-               g_variant_new (
-                       "(tsi)", current_page_id (wk_editor), "-x-evo-current-img", value),
-               wk_editor->priv->cancellable);
-}
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "width", value);
+}
 
 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");
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "style", value ? "height: 
auto;" : NULL);
 }
 
 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");
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "style", value ? "width: 
auto;" : NULL);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetWidth",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsGetImageWidth(false);"),
+               0);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ImageElementGetHeight",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-img"),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(i)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsGetImageHeight(false);"),
+               0);
 }
 
 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);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorLinkDialogOnClose");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.Unlink();");
 }
 
 static void
-webkit_editor_link_set_values (EContentEditor *editor,
-                               const gchar *href,
-                               const gchar *text)
+webkit_editor_link_set_properties (EContentEditor *editor,
+                                  const gchar *href,
+                                  const gchar *text)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorLinkDialogOk",
-               g_variant_new ("(tss)", current_page_id (wk_editor), href, text),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.LinkSetProperties(%s, %s);",
+               href, text);
 }
 
 static void
-webkit_editor_link_get_values (EContentEditor *editor,
-                               gchar **href,
-                               gchar **text)
+webkit_editor_link_get_properties (EContentEditor *editor,
+                                  gchar **href,
+                                  gchar **text)
 {
-       EWebKitEditor *wk_editor;
-       GVariant *result;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
+       JSCValue *result;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorLinkDialogShow",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       result = webkit_editor_call_jsc_sync (wk_editor, "EvoEditor.LinkGetProperties();");
 
        if (result) {
-               g_variant_get (result, "(ss)", href, text);
-               g_variant_unref (result);
+               *href = e_web_view_jsc_get_object_property_string (result, "href", NULL);
+               *text = e_web_view_jsc_get_object_property_string (result, "text", NULL);
+
+               g_clear_object (&result);
        } else {
                *href = NULL;
                *text = NULL;
@@ -3800,8 +3344,9 @@ webkit_editor_set_alignment (EWebKitEditor *wk_editor,
 {
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       webkit_editor_set_format_int (
-               wk_editor, "DOMSelectionSetAlignment", (gint32) value);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.SetAlignment(%d);",
+               value);
 }
 
 static EContentEditorAlignment
@@ -3818,8 +3363,9 @@ webkit_editor_set_block_format (EWebKitEditor *wk_editor,
 {
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       webkit_editor_set_format_int (
-               wk_editor, "DOMSelectionSetBlockFormat", (gint32) value);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.SetBlockFormat(%d);",
+               value);
 }
 
 static EContentEditorBlockFormat
@@ -3834,27 +3380,25 @@ static void
 webkit_editor_set_background_color (EWebKitEditor *wk_editor,
                                     const GdkRGBA *value)
 {
-       gchar *color;
+       gchar color[64];
 
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       if (gdk_rgba_equal (value, wk_editor->priv->background_color))
+       if ((!value && !wk_editor->priv->background_color) ||
+           (value && wk_editor->priv->background_color && 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);
+       if (value && value->alpha > 1e-9) {
+               webkit_editor_utils_color_to_string (color, sizeof (color), value);
+               g_clear_pointer (&wk_editor->priv->background_color, gdk_rgba_free);
+               wk_editor->priv->background_color = gdk_rgba_copy (value);
+       } else {
+               g_snprintf (color, sizeof (color), "inherit");
+               g_clear_pointer (&wk_editor->priv->background_color, gdk_rgba_free);
+               wk_editor->priv->background_color = NULL;
+       }
 
-       g_free (color);
+       webkit_web_view_execute_editing_command_with_argument (WEBKIT_WEB_VIEW (wk_editor), "BackColor", 
color);
 }
 
 static const GdkRGBA *
@@ -3862,13 +3406,8 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
-       if (!wk_editor->priv->html_mode || !wk_editor->priv->background_color)
-               return &white;
+       if (!wk_editor->priv->background_color)
+               return &transparent;
 
        return wk_editor->priv->background_color;
 }
@@ -3879,10 +3418,9 @@ webkit_editor_set_font_name (EWebKitEditor *wk_editor,
 {
        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);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.SetFontName(%s);",
+               value ? value : "");
 }
 
 static const gchar *
@@ -3890,6 +3428,9 @@ webkit_editor_get_font_name (EWebKitEditor *wk_editor)
 {
        g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
 
+       if (!wk_editor->priv->html_mode)
+               return NULL;
+
        return wk_editor->priv->font_name;
 }
 
@@ -3897,27 +3438,18 @@ static void
 webkit_editor_set_font_color (EWebKitEditor *wk_editor,
                               const GdkRGBA *value)
 {
-       gchar *color;
+       gchar color[64];
 
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       if (gdk_rgba_equal (value, wk_editor->priv->font_color))
+       if ((!value && !wk_editor->priv->font_color) ||
+           (value && wk_editor->priv->font_color && 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);
+       webkit_editor_utils_color_to_string (color, sizeof (color), value);
 
-       wk_editor->priv->font_color = gdk_rgba_copy (value);
-
-       webkit_editor_set_format_string (
-               wk_editor,
-               "font-color",
-               "DOMSelectionSetFontColor",
-               color);
-
-       g_free (color);
+       webkit_web_view_execute_editing_command_with_argument (WEBKIT_WEB_VIEW (wk_editor), "ForeColor",
+               webkit_editor_utils_color_to_string (color, sizeof (color), value));
 }
 
 static const GdkRGBA *
@@ -3925,11 +3457,6 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return NULL;
-       }
-
        if (!wk_editor->priv->html_mode || !wk_editor->priv->font_color)
                return &black;
 
@@ -3940,15 +3467,21 @@ static void
 webkit_editor_set_font_size (EWebKitEditor *wk_editor,
                              gint value)
 {
+       gchar sz[2] = { 0, 0 };
+
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
        if (wk_editor->priv->font_size == value)
                return;
 
-       wk_editor->priv->font_size = value;
+       if (value >= 1 && value <= 7) {
+               sz[0] = '0' + value;
+       } else {
+               g_warn_if_reached ();
+               return;
+       }
 
-       webkit_editor_set_format_int (
-               wk_editor, "DOMSelectionSetFontSize", value);
+       webkit_web_view_execute_editing_command_with_argument (WEBKIT_WEB_VIEW (wk_editor), "FontSize", sz);
 }
 
 static gint
@@ -3961,123 +3494,60 @@ webkit_editor_get_font_size (EWebKitEditor *wk_editor)
 
 static void
 webkit_editor_set_style_flag (EWebKitEditor *wk_editor,
-                             EContentEditorStyleFlags flag,
-                             gboolean do_set,
-                             const gchar *dom_function_name)
+                             EWebKitEditorStyleFlags flag,
+                             gboolean do_set)
 {
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
 
-       if (((wk_editor->priv->style_flags & flag) != 0 ? 1 : 0) == (do_set ? 1 : 0))
+       if (((wk_editor->priv->temporary_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);
+       switch (flag) {
+       case E_WEBKIT_EDITOR_STYLE_NONE:
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_BOLD:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Bold");
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_ITALIC:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Italic");
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_UNDERLINE:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Underline");
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_STRIKETHROUGH:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Strikethrough");
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Subscript");
+               break;
+       case E_WEBKIT_EDITOR_STYLE_IS_SUPERSCRIPT:
+               webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Superscript");
+               break;
+       }
+
+       wk_editor->priv->temporary_style_flags = (wk_editor->priv->temporary_style_flags & (~flag)) | (do_set 
? flag : 0);
 }
 
 static gboolean
 webkit_editor_get_style_flag (EWebKitEditor *wk_editor,
-                             EContentEditorStyleFlags flag)
+                             EWebKitEditorStyleFlags 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_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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), "body", "background");
 }
 
 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 {
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "RemoveImageAttributesFromElementBySelector",
-                       g_variant_new ("(ts)", current_page_id (wk_editor), "body"),
-                       wk_editor->priv->cancellable);
-       }
-}
-
-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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogMarkCurrentCellElement",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
-               wk_editor->priv->cancellable);
-}
-
-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");
+       webkit_editor_replace_image_src (E_WEBKIT_EDITOR (editor), "body", uri);
 }
 
 static void
@@ -4085,45 +3555,13 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementVAlign",
-               g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "valign", value && 
*value ? value : 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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "valign");
 }
 
 static void
@@ -4131,45 +3569,13 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementAlign",
-               g_variant_new ("(tsi)", current_page_id (wk_editor), value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "align", value && 
*value ? value : 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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "align");
 }
 
 static void
@@ -4177,53 +3583,19 @@ 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementNoWrap",
-               g_variant_new ("(tbi)", current_page_id (wk_editor), !value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "nowrap", !value ? 
"" : 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;
+       gchar *nowrap;
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return FALSE;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "TableCellElementGetNoWrap",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
-               NULL);
+       nowrap = webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "nowrap");
+       value = !nowrap;
 
-       if (result) {
-               g_variant_get (result, "(b)", &value);
-               value = !value;
-               g_variant_unref (result);
-       }
+       g_free (nowrap);
 
        return value;
 }
@@ -4235,205 +3607,54 @@ webkit_editor_cell_set_header_style (EContentEditor *editor,
 {
        EWebKitEditor *wk_editor;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (editor));
 
-       if (!wk_editor->priv->html_mode)
-               return;
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementHeaderStyle",
-               g_variant_new ("(tbi)", current_page_id (wk_editor), value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableSetHeader(%d, %x);",
+               scope, value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return FALSE;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "ElementGetTagName",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
-               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;
+       return webkit_editor_extract_and_free_jsc_boolean (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor),
+                       "EvoEditor.DialogUtilsTableGetCellIsHeader();"),
+               FALSE);
 }
 
 static gint
 webkit_editor_cell_get_width (EContentEditor *editor,
                               EContentEditorUnit *unit)
 {
-       EWebKitEditor *wk_editor;
-       gint value = 0;
-       GVariant *result;
+       return webkit_editor_dialog_utils_get_attribute_with_unit (E_WEBKIT_EDITOR (editor), NULL, "width", 
0, unit);
+}
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
+static gint
+webkit_editor_cell_get_row_span (EContentEditor *editor)
+{
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "rowspan", 0);
+}
 
-       *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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "TableCellElementGetRowSpan",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
-               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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "TableCellElementGetColSpan",
-               g_variant_new ("(ts)", current_page_id (wk_editor), "-x-evo-current-cell"),
-               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)
+{
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "colspan", 0);
+}
 
 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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "background");
 }
 
 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;
+       webkit_editor_dialog_utils_get_attribute_color (E_WEBKIT_EDITOR (editor), NULL, "bgcolor", color);
 }
 
 static void
@@ -4441,23 +3662,10 @@ webkit_editor_cell_set_row_span (EContentEditor *editor,
                                  gint value,
                                  EContentEditorScope scope)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       gchar str_value[64];
 
-       if (!wk_editor->priv->html_mode)
-               return;
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementRowSpan",
-               g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "rowspan",
+               webkit_editor_utils_int_to_string (str_value, sizeof (str_value), value));
 }
 
 static void
@@ -4465,23 +3673,10 @@ 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;
+       gchar str_value[64];
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementColSpan",
-               g_variant_new ("(tii)", current_page_id (wk_editor), value, (gint32) scope),
-               wk_editor->priv->cancellable);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "colspan",
+               webkit_editor_utils_int_to_string (str_value, sizeof (str_value), value));
 }
 
 static void
@@ -4490,34 +3685,10 @@ webkit_editor_cell_set_width (EContentEditor *editor,
                               EContentEditorUnit unit,
                               EContentEditorScope scope)
 {
-       EWebKitEditor *wk_editor;
-       gchar *width;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->html_mode)
-               return;
+       gchar str_value[64];
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               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" : "%");
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementWidth",
-               g_variant_new ("(tsi)", current_page_id (wk_editor), width, (gint32) scope),
-               wk_editor->priv->cancellable);
-
-       g_free (width);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "width",
+               webkit_editor_utils_int_with_unit_to_string (str_value, sizeof (str_value), value, unit));
 }
 
 static void
@@ -4525,163 +3696,55 @@ webkit_editor_cell_set_background_color (EContentEditor *editor,
                                          const GdkRGBA *value,
                                          EContentEditorScope scope)
 {
-       EWebKitEditor *wk_editor;
-       gchar *color;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       gchar str_value[64];
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               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 ("");
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorCellDialogSetElementBgColor",
-               g_variant_new ("(tsi)", current_page_id (wk_editor), color, (gint32) scope),
-               wk_editor->priv->cancellable);
-
-       g_free (color);
+       webkit_editor_dialog_utils_set_table_attribute (E_WEBKIT_EDITOR (editor), scope, "bgcolor",
+               webkit_editor_utils_color_to_string (str_value, sizeof (str_value), value));
 }
 
 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_proxy) {
-               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 {
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "RemoveImageAttributesFromElementBySelector",
-                       g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-cell"),
-                       wk_editor->priv->cancellable);
-       }
+       webkit_editor_replace_image_src (E_WEBKIT_EDITOR (editor), NULL, uri);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorTableDialogSetRowCount",
-               g_variant_new ("(tu)", current_page_id (wk_editor), value),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableSetRowCount(%d);",
+               value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorTableDialogGetRowCount",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(u)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsTableGetRowCount();"),
+               0);
 }
 
 static void
 webkit_editor_table_set_column_count (EContentEditor *editor,
                                       guint value)
 {
-       EWebKitEditor *wk_editor;
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->html_mode)
-               return;
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return;
-       }
-
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorTableDialogSetColumnCount",
-               g_variant_new ("(tu)", current_page_id (wk_editor), value),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.DialogUtilsTableSetColumnCount(%d);",
+               value);
 }
 
 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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return 0;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorTableDialogGetColumnCount",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
-
-       if (result) {
-               g_variant_get (result, "(u)", &value);
-               g_variant_unref (result);
-       }
-
-       return value;
+       return webkit_editor_extract_and_free_jsc_int32 (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.DialogUtilsTableGetColumnCount();"),
+               0);
 }
 
 static void
@@ -4689,465 +3752,111 @@ 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_proxy) {
-               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);
+       webkit_editor_dialog_utils_set_attribute_with_unit (E_WEBKIT_EDITOR (editor), NULL, "width", value, 
unit);
 }
 
 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;
+       return webkit_editor_dialog_utils_get_attribute_with_unit (E_WEBKIT_EDITOR (editor), NULL, "width", 
0, unit);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute (E_WEBKIT_EDITOR (editor), NULL, "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;
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "align");
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "cellpadding", value);
 }
 
 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;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "cellpadding", 
0);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "cellspacing", value);
 }
 
 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;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "cellspacing", 
0);
 }
 
 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);
+       webkit_editor_dialog_utils_set_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "border", value);
 }
 
 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;
+       return webkit_editor_dialog_utils_get_attribute_int (E_WEBKIT_EDITOR (editor), NULL, "border", 0);
 }
 
 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;
+       webkit_editor_dialog_utils_get_attribute_color (E_WEBKIT_EDITOR (editor), NULL, "bgcolor", color);
 }
 
 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_proxy) {
-               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_proxy) {
-               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 {
-               e_util_invoke_g_dbus_proxy_call_with_error_check (
-                       wk_editor->priv->web_extension_proxy,
-                       "RemoveImageAttributesFromElementBySelector",
-                       g_variant_new ("(ts)", current_page_id (wk_editor), "#-x-evo-current-table"),
-                       wk_editor->priv->cancellable);
-       }
-}
-
-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_proxy) {
-               g_warning ("EHTMLEditorWebExtension not ready at %s!", G_STRFUNC);
-               return FALSE;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorTableDialogShow",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               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_proxy) {
-               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 = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               dom_function,
-               g_variant_new (
-                       "(ts^as)", current_page_id (wk_editor), word ? word : "", active_languages),
-               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)
-{
+       webkit_editor_dialog_utils_set_attribute_color (E_WEBKIT_EDITOR (editor), NULL, "bgcolor", value);
 }
 
-static void
-webkit_editor_on_replace_dialog_close (EContentEditor *editor)
+static gchar *
+webkit_editor_table_get_background_image_uri (EContentEditor *editor)
 {
-       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+       return webkit_editor_dialog_utils_get_attribute (E_WEBKIT_EDITOR (editor), NULL, "background");
 }
 
 static void
-webkit_editor_on_find_dialog_open (EContentEditor *editor)
+webkit_editor_table_set_background_image_uri (EContentEditor *editor,
+                                              const gchar *uri)
 {
+       webkit_editor_replace_image_src (E_WEBKIT_EDITOR (editor), NULL, uri);
 }
 
-static void
-webkit_editor_on_find_dialog_close (EContentEditor *editor)
+static gchar *
+webkit_editor_spell_check_next_word (EContentEditor *editor,
+                                     const gchar *word)
 {
-       webkit_editor_finish_search (E_WEBKIT_EDITOR (editor));
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.SpellCheckContinue(%x,%x);", word && *word, TRUE),
+               NULL);
 }
 
-static GDBusProxy *
-webkit_editor_get_web_extension (EWebKitEditor *wk_editor)
+static gchar *
+webkit_editor_spell_check_prev_word (EContentEditor *editor,
+                                     const gchar *word)
 {
-       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
-
-       return wk_editor->priv->web_extension_proxy;
+       return webkit_editor_extract_and_free_jsc_string (
+               webkit_editor_call_jsc_sync (E_WEBKIT_EDITOR (editor), 
"EvoEditor.SpellCheckContinue(%x,%x);", word && *word, FALSE),
+               NULL);
 }
 
 static void
@@ -5211,6 +3920,131 @@ webkit_editor_process_uri_request_cb (WebKitURISchemeRequest *request,
                webkit_editor_uri_request_done_cb, g_object_ref (request));
 }
 
+static void
+webkit_editor_set_normal_paragraph_width (EWebKitEditor *wk_editor,
+                                         gint value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if (wk_editor->priv->normal_paragraph_width != value) {
+               wk_editor->priv->normal_paragraph_width = value;
+
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.SetNormalParagraphWidth(%d);",
+                       value);
+
+               g_object_notify (G_OBJECT (wk_editor), "normal-paragraph-width");
+       }
+}
+
+static gint
+webkit_editor_get_normal_paragraph_width (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), -1);
+
+       return wk_editor->priv->normal_paragraph_width;
+}
+
+static void
+webkit_editor_set_magic_links (EWebKitEditor *wk_editor,
+                              gboolean value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->magic_links ? 1 : 0) != (value ? 1 : 0)) {
+               wk_editor->priv->magic_links = value;
+
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.MAGIC_LINKS = %x;",
+                       value);
+
+               g_object_notify (G_OBJECT (wk_editor), "magic-links");
+       }
+}
+
+static gboolean
+webkit_editor_get_magic_links (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->magic_links;
+}
+
+static void
+webkit_editor_set_magic_smileys (EWebKitEditor *wk_editor,
+                                gboolean value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->magic_smileys ? 1 : 0) != (value ? 1 : 0)) {
+               wk_editor->priv->magic_smileys = value;
+
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.MAGIC_SMILEYS = %x;",
+                       value);
+
+               g_object_notify (G_OBJECT (wk_editor), "magic-smileys");
+       }
+}
+
+static gboolean
+webkit_editor_get_magic_smileys (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->magic_smileys;
+}
+
+static void
+webkit_editor_set_unicode_smileys (EWebKitEditor *wk_editor,
+                                  gboolean value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->unicode_smileys ? 1 : 0) != (value ? 1 : 0)) {
+               wk_editor->priv->unicode_smileys = value;
+
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.UNICODE_SMILEYS = %x;",
+                       value);
+
+               g_object_notify (G_OBJECT (wk_editor), "unicode-smileys");
+       }
+}
+
+static gboolean
+webkit_editor_get_unicode_smileys (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->unicode_smileys;
+}
+
+static void
+webkit_editor_set_wrap_quoted_text_in_replies (EWebKitEditor *wk_editor,
+                                              gboolean value)
+{
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+
+       if ((wk_editor->priv->wrap_quoted_text_in_replies ? 1 : 0) != (value ? 1 : 0)) {
+               wk_editor->priv->wrap_quoted_text_in_replies = value;
+
+               e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+                       "EvoEditor.WRAP_QUOTED_TEXT_IN_REPLIES = %x;",
+                       value);
+
+               g_object_notify (G_OBJECT (wk_editor), "wrap-quoted-text-in-replies");
+       }
+}
+
+static gboolean
+webkit_editor_get_wrap_quoted_text_in_replies (EWebKitEditor *wk_editor)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
+
+       return wk_editor->priv->wrap_quoted_text_in_replies;
+}
+
 static void
 e_webkit_editor_initialize_web_extensions_cb (WebKitWebContext *web_context,
                                              gpointer user_data)
@@ -5218,13 +4052,8 @@ e_webkit_editor_initialize_web_extensions_cb (WebKitWebContext *web_context,
        EWebKitEditor *wk_editor = user_data;
 
        g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
-       g_return_if_fail (wk_editor->priv->container);
 
        webkit_web_context_set_web_extensions_directory (web_context, 
EVOLUTION_WEB_EXTENSIONS_WEBKIT_EDITOR_DIR);
-       webkit_web_context_set_web_extensions_initialization_user_data (web_context,
-               g_variant_new ("(ss)",
-                       e_web_extension_container_get_server_guid (wk_editor->priv->container),
-                       e_web_extension_container_get_server_address (wk_editor->priv->container)));
 }
 
 static void
@@ -5236,6 +4065,8 @@ webkit_editor_constructed (GObject *object)
        WebKitWebContext *web_context;
        WebKitSettings *web_settings;
        WebKitWebView *web_view;
+       WebKitUserContentManager *manager;
+       GSettings *settings;
 
        wk_editor = E_WEBKIT_EDITOR (object);
        web_view = WEBKIT_WEB_VIEW (wk_editor);
@@ -5249,6 +4080,25 @@ webkit_editor_constructed (GObject *object)
 
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->constructed (object);
 
+       manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (wk_editor));
+
+       g_signal_connect_object (manager, "script-message-received::contentChanged",
+               G_CALLBACK (content_changed_cb), wk_editor, 0);
+       g_signal_connect_object (manager, "script-message-received::contextMenuRequested",
+               G_CALLBACK (context_menu_requested_cb), wk_editor, 0);
+       g_signal_connect_object (manager, "script-message-received::formattingChanged",
+               G_CALLBACK (formatting_changed_cb), wk_editor, 0);
+       g_signal_connect_object (manager, "script-message-received::selectionChanged",
+               G_CALLBACK (selection_changed_cb), wk_editor, 0);
+       g_signal_connect_object (manager, "script-message-received::undoRedoStateChanged",
+               G_CALLBACK (undu_redo_state_changed_cb), wk_editor, 0);
+
+       webkit_user_content_manager_register_script_message_handler (manager, "contentChanged");
+       webkit_user_content_manager_register_script_message_handler (manager, "contextMenuRequested");
+       webkit_user_content_manager_register_script_message_handler (manager, "formattingChanged");
+       webkit_user_content_manager_register_script_message_handler (manager, "selectionChanged");
+       webkit_user_content_manager_register_script_message_handler (manager, "undoRedoStateChanged");
+
        /* Give spell check languages to WebKit */
        languages = e_spell_checker_list_active_languages (wk_editor->priv->spell_checker, NULL);
 
@@ -5256,6 +4106,16 @@ webkit_editor_constructed (GObject *object)
        webkit_web_context_set_spell_checking_languages (web_context, (const gchar * const *) languages);
        g_strfreev (languages);
 
+       content_request = e_cid_request_new ();
+       webkit_web_context_register_uri_scheme (web_context, "cid", webkit_editor_process_uri_request_cb,
+               g_object_ref (content_request), g_object_unref);
+       g_object_unref (content_request);
+
+       content_request = e_file_request_new ();
+       webkit_web_context_register_uri_scheme (web_context, "evo-file", webkit_editor_process_uri_request_cb,
+               g_object_ref (content_request), g_object_unref);
+       g_object_unref (content_request);
+
        content_request = e_http_request_new ();
        webkit_web_context_register_uri_scheme (web_context, "evo-http", webkit_editor_process_uri_request_cb,
                g_object_ref (content_request), g_object_unref);
@@ -5267,9 +4127,39 @@ webkit_editor_constructed (GObject *object)
 
        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_write_console_messages_to_stdout (web_settings, 
e_util_get_webkit_developer_mode_enabled ());
        webkit_settings_set_enable_developer_extras (web_settings, e_util_get_webkit_developer_mode_enabled 
());
 
-       e_webkit_editor_load_data (wk_editor, "");
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+
+       g_settings_bind (
+               settings, "composer-word-wrap-length",
+               wk_editor, "normal-paragraph-width",
+               G_SETTINGS_BIND_GET);
+
+       g_settings_bind (
+               settings, "composer-magic-links",
+               wk_editor, "magic-links",
+               G_SETTINGS_BIND_GET);
+
+       g_settings_bind (
+               settings, "composer-magic-smileys",
+               wk_editor, "magic-smileys",
+               G_SETTINGS_BIND_GET);
+
+       g_settings_bind (
+               settings, "composer-unicode-smileys",
+               wk_editor, "unicode-smileys",
+               G_SETTINGS_BIND_GET);
+
+       g_settings_bind (
+               settings, "composer-wrap-quoted-text-in-replies",
+               wk_editor, "wrap-quoted-text-in-replies",
+               G_SETTINGS_BIND_GET);
+
+       g_object_unref (settings);
+
+       webkit_web_view_load_html (WEBKIT_WEB_VIEW (wk_editor), "", "evo-file:///");
 }
 
 static GObjectConstructParam*
@@ -5364,11 +4254,6 @@ webkit_editor_dispose (GObject *object)
                priv->mail_settings = NULL;
        }
 
-       e_webkit_editor_set_web_extension_proxy (E_WEBKIT_EDITOR (object), NULL);
-
-       if (priv->container && priv->stamp)
-               e_web_extension_container_forget_stamp (priv->container, priv->stamp);
-
        if (priv->owner_change_clipboard_cb_id > 0) {
                g_signal_handler_disconnect (
                        gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
@@ -5385,8 +4270,6 @@ webkit_editor_dispose (GObject *object)
 
        webkit_editor_finish_search (E_WEBKIT_EDITOR (object));
 
-       g_clear_object (&priv->container);
-
        /* Chain up to parent's dispose() method. */
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->dispose (object);
 }
@@ -5410,15 +4293,12 @@ webkit_editor_finalize (GObject *object)
                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_pointer (&priv->background_color, gdk_rgba_free);
+       g_clear_pointer (&priv->font_color, gdk_rgba_free);
+       g_clear_pointer (&priv->body_fg_color, gdk_rgba_free);
+       g_clear_pointer (&priv->body_bg_color, gdk_rgba_free);
+       g_clear_pointer (&priv->body_link_color, gdk_rgba_free);
+       g_clear_pointer (&priv->body_vlink_color, gdk_rgba_free);
 
        g_free (priv->last_hover_uri);
        priv->last_hover_uri = NULL;
@@ -5427,7 +4307,9 @@ webkit_editor_finalize (GObject *object)
        g_clear_object (&priv->cancellable);
        g_clear_error (&priv->last_error);
 
+       g_free (priv->body_font_name);
        g_free (priv->font_name);
+       g_free (priv->context_menu_caret_word);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->finalize (object);
@@ -5458,6 +4340,36 @@ webkit_editor_set_property (GObject *object,
                                g_value_get_boolean (value));
                        return;
 
+               case PROP_NORMAL_PARAGRAPH_WIDTH:
+                       webkit_editor_set_normal_paragraph_width (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_int (value));
+                       return;
+
+               case PROP_MAGIC_LINKS:
+                       webkit_editor_set_magic_links (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_MAGIC_SMILEYS:
+                       webkit_editor_set_magic_smileys (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_UNICODE_SMILEYS:
+                       webkit_editor_set_unicode_smileys (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
+               case PROP_WRAP_QUOTED_TEXT_IN_REPLIES:
+                       webkit_editor_set_wrap_quoted_text_in_replies (
+                               E_WEBKIT_EDITOR (object),
+                               g_value_get_boolean (value));
+                       return;
+
                case PROP_ALIGNMENT:
                        webkit_editor_set_alignment (
                                E_WEBKIT_EDITOR (object),
@@ -5473,9 +4385,8 @@ webkit_editor_set_property (GObject *object,
                case PROP_BOLD:
                        webkit_editor_set_style_flag (
                                E_WEBKIT_EDITOR (object),
-                               E_CONTENT_EDITOR_STYLE_IS_BOLD,
-                               g_value_get_boolean (value),
-                               "DOMSelectionSetBold");
+                               E_WEBKIT_EDITOR_STYLE_IS_BOLD,
+                               g_value_get_boolean (value));
                        return;
 
                case PROP_FONT_COLOR:
@@ -5505,49 +4416,36 @@ webkit_editor_set_property (GObject *object,
                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");
+                               E_WEBKIT_EDITOR_STYLE_IS_ITALIC,
+                               g_value_get_boolean (value));
                        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");
+                               E_WEBKIT_EDITOR_STYLE_IS_STRIKETHROUGH,
+                               g_value_get_boolean (value));
                        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");
+                               E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT,
+                               g_value_get_boolean (value));
                        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");
+                               E_WEBKIT_EDITOR_STYLE_IS_SUPERSCRIPT,
+                               g_value_get_boolean (value));
                        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");
+                               E_WEBKIT_EDITOR_STYLE_IS_UNDERLINE,
+                               g_value_get_boolean (value));
                        return;
 
                case PROP_START_BOTTOM:
@@ -5591,12 +4489,6 @@ webkit_editor_get_property (GObject *object,
                             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_IS_MALFUNCTION:
                        g_value_set_boolean (
                                value, webkit_editor_is_malfunction (
@@ -5651,6 +4543,31 @@ webkit_editor_get_property (GObject *object,
                                E_WEBKIT_EDITOR (object)));
                        return;
 
+               case PROP_NORMAL_PARAGRAPH_WIDTH:
+                       g_value_set_int (value,
+                               webkit_editor_get_normal_paragraph_width (E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_MAGIC_LINKS:
+                       g_value_set_boolean (value,
+                               webkit_editor_get_magic_links (E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_MAGIC_SMILEYS:
+                       g_value_set_boolean (value,
+                               webkit_editor_get_magic_smileys (E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_UNICODE_SMILEYS:
+                       g_value_set_boolean (value,
+                               webkit_editor_get_unicode_smileys (E_WEBKIT_EDITOR (object)));
+                       return;
+
+               case PROP_WRAP_QUOTED_TEXT_IN_REPLIES:
+                       g_value_set_boolean (value,
+                               webkit_editor_get_wrap_quoted_text_in_replies (E_WEBKIT_EDITOR (object)));
+                       return;
+
                case PROP_ALIGNMENT:
                        g_value_set_enum (
                                value,
@@ -5677,7 +4594,7 @@ webkit_editor_get_property (GObject *object,
                                value,
                                webkit_editor_get_style_flag (
                                        E_WEBKIT_EDITOR (object),
-                                       E_CONTENT_EDITOR_STYLE_IS_BOLD));
+                                       E_WEBKIT_EDITOR_STYLE_IS_BOLD));
                        return;
 
                case PROP_FONT_COLOR:
@@ -5701,10 +4618,10 @@ webkit_editor_get_property (GObject *object,
                                        E_WEBKIT_EDITOR (object)));
                        return;
 
-               case PROP_INDENTED:
-                       g_value_set_boolean (
+               case PROP_INDENT_LEVEL:
+                       g_value_set_int (
                                value,
-                               webkit_editor_selection_is_indented (
+                               webkit_editor_get_indent_level (
                                        E_WEBKIT_EDITOR (object)));
                        return;
 
@@ -5713,15 +4630,7 @@ webkit_editor_get_property (GObject *object,
                                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));
+                                       E_WEBKIT_EDITOR_STYLE_IS_ITALIC));
                        return;
 
                case PROP_STRIKETHROUGH:
@@ -5729,7 +4638,7 @@ webkit_editor_get_property (GObject *object,
                                value,
                                webkit_editor_get_style_flag (
                                        E_WEBKIT_EDITOR (object),
-                                       E_CONTENT_EDITOR_STYLE_IS_STRIKETHROUGH));
+                                       E_WEBKIT_EDITOR_STYLE_IS_STRIKETHROUGH));
                        return;
 
                case PROP_SUBSCRIPT:
@@ -5737,7 +4646,7 @@ webkit_editor_get_property (GObject *object,
                                value,
                                webkit_editor_get_style_flag (
                                        E_WEBKIT_EDITOR (object),
-                                       E_CONTENT_EDITOR_STYLE_IS_SUBSCRIPT));
+                                       E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT));
                        return;
 
                case PROP_SUPERSCRIPT:
@@ -5745,7 +4654,7 @@ webkit_editor_get_property (GObject *object,
                                value,
                                webkit_editor_get_style_flag (
                                        E_WEBKIT_EDITOR (object),
-                                       E_CONTENT_EDITOR_STYLE_IS_SUPERSCRIPT));
+                                       E_WEBKIT_EDITOR_STYLE_IS_SUPERSCRIPT));
                        return;
 
                case PROP_UNDERLINE:
@@ -5753,7 +4662,7 @@ webkit_editor_get_property (GObject *object,
                                value,
                                webkit_editor_get_style_flag (
                                        E_WEBKIT_EDITOR (object),
-                                       E_CONTENT_EDITOR_STYLE_IS_UNDERLINE));
+                                       E_WEBKIT_EDITOR_STYLE_IS_UNDERLINE));
                        return;
 
                case PROP_START_BOTTOM:
@@ -5855,99 +4764,91 @@ webkit_editor_style_settings_changed_cb (GSettings *settings,
                else
                        g_hash_table_remove (wk_editor->priv->old_settings, key);
 
-               webkit_editor_style_updated_cb (wk_editor);
+               webkit_editor_style_updated (wk_editor, FALSE);
        } else if (new_value) {
                g_variant_unref (new_value);
        }
 }
 
 static void
-webkit_editor_load_changed_cb (EWebKitEditor *wk_editor,
-                               WebKitLoadEvent load_event)
+webkit_editor_can_paste_cb (GObject *source_object,
+                           GAsyncResult *result,
+                           gpointer user_data)
 {
-       wk_editor->priv->webkit_load_event = load_event;
+       EWebKitEditor *wk_editor;
+       gboolean can;
 
-       if (load_event != WEBKIT_LOAD_FINISHED)
-               return;
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (source_object));
 
-       wk_editor->priv->reload_in_progress = FALSE;
+       wk_editor = E_WEBKIT_EDITOR (source_object);
 
-       if (webkit_editor_is_ready (E_CONTENT_EDITOR (wk_editor))) {
-               e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor));
-               webkit_editor_style_updated_cb (wk_editor);
-       } else
-               wk_editor->priv->emit_load_finished_when_extension_is_ready = TRUE;
+       can = webkit_web_view_can_execute_editing_command_finish (WEBKIT_WEB_VIEW (wk_editor), result, NULL);
 
-       dispatch_pending_operations (wk_editor);
+       if (wk_editor->priv->can_paste != can) {
+               wk_editor->priv->can_paste = can;
+               g_object_notify (G_OBJECT (wk_editor), "can-paste");
+       }
 }
 
 static void
-webkit_editor_clipboard_owner_change_cb (GtkClipboard *clipboard,
-                                         GdkEventOwnerChange *event,
-                                         EWebKitEditor *wk_editor)
+webkit_editor_load_changed_cb (EWebKitEditor *wk_editor,
+                               WebKitLoadEvent load_event)
 {
-       if (!E_IS_WEBKIT_EDITOR (wk_editor))
-               return;
+       wk_editor->priv->webkit_load_event = load_event;
 
-       if (!wk_editor->priv->web_extension_proxy)
+       if (load_event != WEBKIT_LOAD_FINISHED ||
+           !webkit_editor_is_ready (E_CONTENT_EDITOR (wk_editor)))
                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;
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.START_BOTTOM = %x;\n"
+               "EvoEditor.TOP_SIGNATURE = %x;",
+               e_webkit_editor_three_state_to_bool (wk_editor->priv->start_bottom, 
"composer-reply-start-bottom"),
+               e_webkit_editor_three_state_to_bool (wk_editor->priv->top_signature, 
"composer-top-signature"));
 
-       if (wk_editor->priv->copy_paste_clipboard_in_view == 
wk_editor->priv->pasting_from_itself_extension_value)
-               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)) {
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "SetPastingContentFromItself",
-               g_variant_new (
-                       "(tb)",
-                       current_page_id (wk_editor),
-                       wk_editor->priv->copy_paste_clipboard_in_view),
-               wk_editor->priv->cancellable);
+               PostReloadOperation *op;
 
-       wk_editor->priv->copy_cut_actions_triggered = FALSE;
+               op = g_queue_pop_head (wk_editor->priv->post_reload_operations);
 
-       wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view;
-}
+               op->func (wk_editor, op->data, op->flags);
 
-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_proxy)
-               return;
+               if (op->data_free_func)
+                       op->data_free_func (op->data);
+               g_free (op);
 
-       if (!event->owner || !wk_editor->priv->can_copy)
-               wk_editor->priv->copy_paste_clipboard_in_view = FALSE;
+               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);
+               }
 
-       if (wk_editor->priv->copy_paste_clipboard_in_view == 
wk_editor->priv->pasting_from_itself_extension_value)
-               return;
+               g_queue_clear (wk_editor->priv->post_reload_operations);
+       }
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "SetPastingContentFromItself",
-               g_variant_new (
-                       "(tb)",
-                       current_page_id (wk_editor),
-                       wk_editor->priv->copy_paste_clipboard_in_view),
-               wk_editor->priv->cancellable);
+       webkit_editor_style_updated (wk_editor, FALSE);
 
-       wk_editor->priv->pasting_from_itself_extension_value = wk_editor->priv->copy_paste_clipboard_in_view;
-}
+       if (wk_editor->priv->initialized_callback) {
+               EContentEditorInitializedCallback initialized_callback;
+               gpointer initialized_user_data;
 
-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;
+               initialized_callback = wk_editor->priv->initialized_callback;
+               initialized_user_data = wk_editor->priv->initialized_user_data;
+
+               wk_editor->priv->initialized_callback = NULL;
+               wk_editor->priv->initialized_user_data = NULL;
+
+               initialized_callback (E_CONTENT_EDITOR (wk_editor), initialized_user_data);
+       }
+
+       webkit_web_view_can_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor),
+               WEBKIT_EDITING_COMMAND_PASTE, NULL, webkit_editor_can_paste_cb, NULL);
+
+       e_content_editor_emit_load_finished (E_CONTENT_EDITOR (wk_editor));
 }
 
 static void
@@ -5975,8 +4876,7 @@ webkit_editor_paste_clipboard_targets_cb (GtkClipboard *clipboard,
         * with SRCSET attribute in clipboard correctly). And if this fails the
         * source application can cancel the content and we could not fallback
         * to at least some content. */
-       if (wk_editor->priv->html_mode ||
-           webkit_editor_paste_prefer_text_html (wk_editor)) {
+       if (wk_editor->priv->html_mode) {
                if (e_targets_include_html (targets, n_targets)) {
                        content = e_clipboard_wait_for_html (clipboard);
                        is_html = TRUE;
@@ -6045,10 +4945,6 @@ webkit_editor_paste_primary (EContentEditor *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);
@@ -6069,8 +4965,6 @@ webkit_editor_paste (EContentEditor *editor)
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       wk_editor->priv->pasting_primary_clipboard = FALSE;
-
        clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
        if (gtk_clipboard_wait_for_targets (clipboard, &targets, &n_targets)) {
@@ -6100,18 +4994,17 @@ webkit_editor_context_menu_cb (EWebKitEditor *wk_editor,
                                GdkEvent *event,
                                WebKitHitTestResult *hit_test_result)
 {
-       GVariant *result;
-       EContentEditorNodeFlags flags = 0;
-       gboolean handled;
-
-       webkit_context_menu_remove_all (context_menu);
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
 
-       if ((result = webkit_context_menu_get_user_data (context_menu)))
-               flags = g_variant_get_int32 (result);
+       e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor),
+               wk_editor->priv->context_menu_node_flags,
+               wk_editor->priv->context_menu_caret_word,
+               event);
 
-       handled = e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor), flags, event);
+       wk_editor->priv->context_menu_node_flags = E_CONTENT_EDITOR_NODE_UNKNOWN;
+       g_clear_pointer (&wk_editor->priv->context_menu_caret_word, g_free);
 
-       return handled;
+       return TRUE;
 }
 
 static void
@@ -6165,8 +5058,6 @@ webkit_editor_drag_data_received_cb (GtkWidget *widget,
                } else {
                        GTK_WIDGET_CLASS (e_webkit_editor_parent_class)->drag_leave(widget, context, time);
                        g_signal_stop_emission_by_name (widget, "drag-data-received");
-                       if (!is_move)
-                               webkit_editor_call_simple_extension_function (wk_editor, 
"DOMLastDropOperationDidCopy");
                        e_content_editor_emit_drop_handled (E_CONTENT_EDITOR (widget));
                }
                return;
@@ -6337,6 +5228,15 @@ paste_primary_clipboard_quoted (EContentEditor *editor)
        }
 }
 
+static CamelMimePart *
+e_webkit_editor_cid_resolver_ref_part (ECidResolver *resolver,
+                                      const gchar *cid_uri)
+{
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (resolver), NULL);
+
+       return e_content_editor_emit_ref_mime_part (E_CONTENT_EDITOR (resolver), cid_uri);
+}
+
 static gboolean
 webkit_editor_button_press_event (GtkWidget *widget,
                                   GdkEventButton *event)
@@ -6465,17 +5365,6 @@ e_webkit_editor_class_init (EWebKitEditorClass *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_IS_MALFUNCTION, "is-malfunction");
        g_object_class_override_property (
@@ -6509,11 +5398,9 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
        g_object_class_override_property (
                object_class, PROP_FONT_SIZE, "font-size");
        g_object_class_override_property (
-               object_class, PROP_INDENTED, "indented");
+               object_class, PROP_INDENT_LEVEL, "indent-level");
        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 (
@@ -6534,6 +5421,69 @@ e_webkit_editor_class_init (EWebKitEditorClass *class)
                object_class, PROP_LAST_ERROR, "last-error");
        g_object_class_override_property (
                object_class, PROP_SPELL_CHECKER, "spell-checker");
+
+       g_object_class_install_property (
+               object_class,
+               PROP_NORMAL_PARAGRAPH_WIDTH,
+               g_param_spec_int (
+                       "normal-paragraph-width",
+                       NULL,
+                       NULL,
+                       G_MININT32,
+                       G_MAXINT32,
+                       71, /* Should be the same as e-editor.js:EvoEditor.NORMAL_PARAGRAPH_WIDTH and in the 
init()*/
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_MAGIC_LINKS,
+               g_param_spec_boolean (
+                       "magic-links",
+                       NULL,
+                       NULL,
+                       TRUE, /* Should be the same as e-editor.js:EvoEditor.MAGIC_LINKS and in the init() */
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_MAGIC_SMILEYS,
+               g_param_spec_boolean (
+                       "magic-smileys",
+                       NULL,
+                       NULL,
+                       FALSE, /* Should be the same as e-editor.js:EvoEditor.MAGIC_SMILEYS and in the init() 
*/
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_UNICODE_SMILEYS,
+               g_param_spec_boolean (
+                       "unicode-smileys",
+                       NULL,
+                       NULL,
+                       FALSE, /* Should be the same as e-editor.js:EvoEditor.UNICODE_SMILEYS and in the 
init() */
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
+
+
+       g_object_class_install_property (
+               object_class,
+               PROP_WRAP_QUOTED_TEXT_IN_REPLIES,
+               g_param_spec_boolean (
+                       "wrap-quoted-text-in-replies",
+                       NULL,
+                       NULL,
+                       TRUE, /* Should be the same as e-editor.js:EvoEditor.WRAP_QUOTED_TEXT_IN_REPLIES and 
in the init() */
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT |
+                       G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -6551,14 +5501,12 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
        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);
        wk_editor->priv->visually_wrap_long_lines = FALSE;
-       wk_editor->priv->container = e_web_extension_container_new 
(E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH, E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE);
 
-       /* Do not change assigned stamp on every content load. It can have issues like when
-          loading new content into already initialized editor. */
-       wk_editor->priv->stamp = e_web_extension_container_reserve_stamp (wk_editor->priv->container);
-
-       g_signal_connect_object (wk_editor->priv->container, "page-proxy-changed",
-               G_CALLBACK (e_webkit_editor_page_proxy_changed_cb), wk_editor, 0);
+       wk_editor->priv->normal_paragraph_width = 71;
+       wk_editor->priv->magic_links = TRUE;
+       wk_editor->priv->magic_smileys = FALSE;
+       wk_editor->priv->unicode_smileys = FALSE;
+       wk_editor->priv->wrap_quoted_text_in_replies = TRUE;
 
        g_signal_connect (
                wk_editor, "load-changed",
@@ -6604,14 +5552,6 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
                wk_editor, "state-flags-changed",
                G_CALLBACK (webkit_editor_style_updated_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",
@@ -6647,17 +5587,11 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
        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->suppress_color_changes = FALSE;
 
-       wk_editor->priv->font_color = gdk_rgba_copy (&black);
-       wk_editor->priv->background_color = gdk_rgba_copy (&white);
+       wk_editor->priv->font_color = NULL;
+       wk_editor->priv->background_color = NULL;
+       wk_editor->priv->body_font_name = NULL;
        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;
@@ -6665,11 +5599,6 @@ e_webkit_editor_init (EWebKitEditor *wk_editor)
 
        wk_editor->priv->start_bottom = E_THREE_STATE_INCONSISTENT;
        wk_editor->priv->top_signature = E_THREE_STATE_INCONSISTENT;
-
-       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;
-       wk_editor->priv->web_extension_user_changed_default_colors_cb_id = 0;
 }
 
 static void
@@ -6679,8 +5608,8 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        iface->update_styles = webkit_editor_update_styles;
        iface->insert_content = webkit_editor_insert_content;
        iface->get_content = webkit_editor_get_content;
+       iface->get_content_finish = webkit_editor_get_content_finish;
        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;
@@ -6691,13 +5620,11 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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;
@@ -6705,11 +5632,11 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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->on_dialog_open = webkit_editor_on_dialog_open;
+       iface->on_dialog_close = webkit_editor_on_dialog_close;
        iface->delete_cell_contents = webkit_editor_delete_cell_contents;
        iface->delete_column = webkit_editor_delete_column;
        iface->delete_row = webkit_editor_delete_row;
@@ -6718,8 +5645,6 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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;
@@ -6728,8 +5653,6 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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;
@@ -6752,10 +5675,8 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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->link_set_properties = webkit_editor_link_set_properties;
+       iface->link_get_properties = webkit_editor_link_get_properties;
        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;
@@ -6764,12 +5685,10 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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_font_name = webkit_editor_page_set_font_name;
+       iface->page_get_font_name = webkit_editor_page_get_font_name;
        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;
@@ -6806,14 +5725,13 @@ e_webkit_editor_content_editor_init (EContentEditorInterface *iface)
        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;
+}
+
+static void
+e_webkit_editor_cid_resolver_init (ECidResolverInterface *iface)
+{
+       iface->ref_part = e_webkit_editor_cid_resolver_ref_part;
+       /* iface->dup_mime_type = e_webkit_editor_cid_resolver_dup_mime_part; - not needed here */
 }
diff --git a/src/modules/webkit-editor/web-extension/CMakeLists.txt 
b/src/modules/webkit-editor/web-extension/CMakeLists.txt
index ed59c7ebc7..7ec514acc4 100644
--- a/src/modules/webkit-editor/web-extension/CMakeLists.txt
+++ b/src/modules/webkit-editor/web-extension/CMakeLists.txt
@@ -23,30 +23,28 @@ macro(add_webextension_editor_module _name _sourcesvar _depsvar _defsvar _cflags
 endmacro(add_webextension_editor_module)
 
 set(extra_deps
+       evolution-util
        evolution-mail
 )
 set(sources
-       e-composer-dom-functions.c
-       e-composer-dom-functions.h
-       e-dialogs-dom-functions.c
-       e-dialogs-dom-functions.h
-       e-dom-utils.c
-       e-dom-utils.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
 )
-set(extra_defines)
-set(extra_cflags)
-set(extra_incdirs)
-set(extra_ldflags)
+set(extra_defines
+       -DEVOLUTION_WEBKITDATADIR=\"${webkitdatadir}\"
+       -DEVOLUTION_SOURCE_WEBKITDATADIR=\"${CMAKE_SOURCE_DIR}/data/webkit\"
+)
+
+set(extra_cflags
+       ${EVOLUTION_DATA_SERVER_CFLAGS}
+)
+set(extra_incdirs
+       ${EVOLUTION_DATA_SERVER_INCLUDE_DIRS}
+)
+set(extra_ldflags
+       ${EVOLUTION_DATA_SERVER_LDFLAGS}
+)
 
 add_webextension_editor_module(module-webkit-editor-webextension
        sources
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension-main.c 
b/src/modules/webkit-editor/web-extension/e-editor-web-extension-main.c
index a10c233f47..4acccdd9f1 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension-main.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension-main.c
@@ -18,39 +18,8 @@
 
 #include "evolution-config.h"
 
-#include <camel/camel.h>
-
-#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
-#include <e-util/e-util.h>
-#undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
-
 #include "e-editor-web-extension.h"
 
-static void
-connected_to_server_cb (GObject *source_object,
-                       GAsyncResult *result,
-                       gpointer user_data)
-{
-       EEditorWebExtension *extension = user_data;
-       GDBusConnection *connection;
-       GError *error = NULL;
-
-       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
-
-       connection = e_web_extension_container_utils_connect_to_server_finish (result, &error);
-       if (!connection) {
-               g_warning ("%d %s: Failed to connect to the UI D-Bus server: %s", getpid (), G_STRFUNC,
-                       error ? error->message : "Unknown error");
-               g_clear_error (&error);
-               return;
-       }
-
-       e_editor_web_extension_dbus_register (extension, connection);
-
-       g_object_unref (connection);
-       g_object_unref (extension);
-}
-
 /* Forward declaration */
 G_MODULE_EXPORT void webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension,
                                                                     GVariant *user_data);
@@ -60,21 +29,7 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *wk_extension
                                                GVariant *user_data)
 {
        EEditorWebExtension *extension;
-       const gchar *guid = NULL, *server_address = NULL;
-
-       g_return_if_fail (user_data != NULL);
-
-       g_variant_get (user_data, "(&s&s)", &guid, &server_address);
-
-       if (!server_address) {
-               g_warning ("%d %s: The UI process didn't provide server address", getpid (), G_STRFUNC);
-               return;
-       }
-
-       camel_debug_init ();
 
        extension = e_editor_web_extension_get_default ();
        e_editor_web_extension_initialize (extension, wk_extension);
-
-       e_web_extension_container_utils_connect_to_server (server_address, NULL, connected_to_server_cb, 
g_object_ref (extension));
 }
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c 
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
index 05a1d4ac8d..07667f87d7 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.c
@@ -18,2435 +18,508 @@
 
 #include "evolution-config.h"
 
-#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 <webkitdom/webkitdom.h>
+#include <libedataserver/libedataserver.h>
 
-#define E_UTIL_INCLUDE_WITHOUT_WEBKIT
-#include "mail/e-http-request.h"
+#define E_UTIL_INCLUDE_WITHOUT_WEBKIT 1
+#include "e-util/e-util.h"
 #undef E_UTIL_INCLUDE_WITHOUT_WEBKIT
 
-//#include "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;
-
-       GSList *pages; /* EEditorPage * */
+       ESpellChecker *spell_checker;
 };
 
-static CamelDataCache *emd_global_http_cache = NULL;
-
-static const gchar *introspection_xml =
-"<node>"
-"  <interface name='" E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE "'>"
-"    <signal name='ExtensionObjectReady'>"
-"    </signal>"
-"    <method name='GetExtensionHandlesPages'>"
-"      <arg type='at' name='array' direction='out'/>"
-"    </method>"
-"    <signal name='ExtensionHandlesPage'>"
-"      <arg type='t' name='page_id' direction='out'/>"
-"      <arg type='i' name='stamp' direction='out'/>"
-"    </signal>"
-"<!-- ********************************************************* -->"
-"<!--                          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>"
-"    <signal name='UserChangedDefaultColors'>"
-"      <arg type='b' name='suppress_color_changes' 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'/>"
-"      <arg type='n' name='start_at_bottom' direction='in'/>"
-"      <arg type='n' name='top_signature' 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'/>"
-"      <arg type='n' name='start_at_bottom' direction='in'/>"
-"      <arg type='n' name='top_signature' 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='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>"
-"    <method name='DOMLastDropOperationDidCopy'>"
-"      <arg type='t' name='page_id' 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='DOMInsertReplaceAllHistoryEvent'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"      <arg type='s' name='search_text' direction='in'/>"
-"      <arg type='s' name='replacement' 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>"
-"    <method name='DOMReplaceCaretWord'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"      <arg type='s' name='replacement' direction='in'/>"
-"    </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='n' name='start_at_bottom' direction='in'/>"
-"      <arg type='n' name='top_signature' 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='DOMGetActiveSignatureUid'>"
-"      <arg type='t' name='page_id' direction='in'/>"
-"      <arg type='s' name='uid' direction='out'/>"
-"    </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_DEFINE_TYPE_WITH_PRIVATE (EEditorWebExtension, e_editor_web_extension, G_TYPE_OBJECT)
+
+static void
+e_editor_web_extension_dispose (GObject *object)
 {
-       GSList *link;
+       EEditorWebExtension *extension = E_EDITOR_WEB_EXTENSION (object);
 
-       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+       g_clear_object (&extension->priv->wk_extension);
+       g_clear_object (&extension->priv->spell_checker);
 
-       for (link = extension->priv->pages; link; link = g_slist_next (link)) {
-               EEditorPage *page = link->data;
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object);
+}
 
-               if (page && e_editor_page_get_page_id (page) == page_id)
-                       return page;
-       }
+static void
+e_editor_web_extension_class_init (EEditorWebExtensionClass *class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (class);
 
-       return NULL;
+       object_class->dispose = e_editor_web_extension_dispose;
 }
 
-static EEditorPage *
-get_editor_page_or_return_dbus_error (GDBusMethodInvocation *invocation,
-                                      EEditorWebExtension *extension,
-                                      guint64 page_id)
+static void
+e_editor_web_extension_init (EEditorWebExtension *extension)
 {
-       WebKitWebPage *web_page;
-       EEditorPage *editor_page;
+       extension->priv = e_editor_web_extension_get_instance_private (extension);
+       extension->priv->spell_checker = NULL;
+}
 
-       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+static gpointer
+e_editor_web_extension_create_instance (gpointer data)
+{
+       return g_object_new (E_TYPE_EDITOR_WEB_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);
+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));
+}
 
-               return NULL;
-       }
+static gboolean
+use_sources_js_file (void)
+{
+       static gint res = -1;
 
-       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);
-       }
+       if (res == -1)
+               res = g_strcmp0 (g_getenv ("E_HTML_EDITOR_TEST_SOURCES"), "1") == 0 ? 1 : 0;
 
-       return editor_page;
+       return res;
 }
 
 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)
+load_javascript_file (JSCContext *jsc_context,
+                     const gchar *js_filename)
 {
-       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 (camel_debug ("webkit:editor"))
-               printf ("EEditorWebExtension - %s - %s\n", G_STRFUNC, method_name);
-
-       if (g_strcmp0 (method_name, "GetExtensionHandlesPages") == 0) {
-               GVariantBuilder *builder;
-               GSList *link;
+       JSCValue *result;
+       JSCException *exception;
+       gchar *content, *filename = NULL, *resource_uri;
+       gsize length = 0;
+       GError *error = NULL;
 
-               builder = g_variant_builder_new (G_VARIANT_TYPE ("at"));
+       g_return_if_fail (jsc_context != NULL);
 
-               for (link = extension->priv->pages; link; link = g_slist_next (link)) {
-                       EEditorPage *page = link->data;
+       if (use_sources_js_file ()) {
+               filename = g_build_filename (EVOLUTION_SOURCE_WEBKITDATADIR, js_filename, NULL);
 
-                       if (page) {
-                               g_variant_builder_add (builder, "t", e_editor_page_get_page_id (page));
-                               g_variant_builder_add (builder, "t", (guint64) e_editor_page_get_stamp 
(page));
-                       }
-               }
-
-               g_dbus_method_invocation_return_value (invocation,
-                       g_variant_new ("(at)", builder));
-
-               g_variant_builder_unref (builder);
-       } else 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);
-               }
+               if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+                       g_warning ("Cannot find '%s', using installed file '%s/%s' instead", filename, 
EVOLUTION_WEBKITDATADIR, js_filename);
 
-               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_clear_pointer (&filename, g_free);
                }
+       }
 
-               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;
+       if (!filename)
+               filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
 
-               g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html);
+       if (!g_file_get_contents (filename, &content, &length, &error)) {
+               g_warning ("Failed to load '%s': %s", filename, error ? error->message : "Unknown error");
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+               g_clear_error (&error);
+               g_free (filename);
 
-               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;
+               return;
+       }
 
-               g_variant_get (parameters, "(t&sb)", &page_id, &text, &is_html);
+       resource_uri = g_strconcat ("resource:///", js_filename, NULL);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       result = jsc_context_evaluate_with_source_uri (jsc_context, content, length, resource_uri, 1);
 
-               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_free (resource_uri);
 
-               g_variant_get (parameters, "(t&s)", &page_id, &style_sheet_content);
+       exception = jsc_context_get_exception (jsc_context);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       if (exception) {
+               g_warning ("Failed to call script '%s': %d:%d: %s",
+                       filename,
+                       jsc_exception_get_line_number (exception),
+                       jsc_exception_get_column_number (exception),
+                       jsc_exception_get_message (exception));
 
-               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);
+               jsc_context_clear_exception (jsc_context);
+       }
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       g_clear_object (&result);
+       g_free (filename);
+       g_free (content);
+}
 
-               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;
+static void
+evo_editor_find_pattern (const gchar *text,
+                        const gchar *pattern,
+                        gint *out_start,
+                        gint *out_end)
+{
+       GRegex *regex;
 
-               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_return_if_fail (out_start != NULL);
+       g_return_if_fail (out_end != NULL);
 
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "SetEditorHTMLMode") == 0) {
-               gboolean html_mode = FALSE;
-               gboolean convert = FALSE;
+       *out_start = -1;
+       *out_end = -1;
 
-               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;
+       regex = g_regex_new (pattern, 0, 0, NULL);
+       if (regex) {
+               GMatchInfo *match_info = NULL;
+               gint start = -1, end = -1;
 
-               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;
-               gint16 start_at_bottom = -1, top_signature = -1;
-
-               g_variant_get (parameters, "(tbnn)", &page_id, &value, &start_at_bottom, &top_signature);
-
-               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, start_at_bottom, top_signature);
-
-               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 (g_regex_match_all (regex, text, G_REGEX_MATCH_NOTEMPTY, &match_info) &&
+                   g_match_info_fetch_pos (match_info, 0, &start, &end) &&
+                   start >= 0 && end >= 0) {
+                       *out_start = start;
+                       *out_end = end;
                }
 
-               if ((flags & E_CONTENT_EDITOR_GET_INLINE_IMAGES) && from_domain && *from_domain && 
inline_images)
-                       e_editor_dom_restore_images (editor_page, inline_images);
-
-               /* 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);
-       } 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;
-               gint64 start_at_bottom = -1, top_signature = -1;
-
-               g_variant_get (parameters, "(t&snn)", &page_id, &preferred_text, &start_at_bottom, 
&top_signature);
-
-               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, start_at_bottom, top_signature);
-               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);
+               if (match_info)
+                       g_match_info_free (match_info);
+               g_regex_unref (regex);
+       }
+}
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+/* Returns 'null', when no match for magicLinks in 'text' were found, otherwise
+   returns an array of 'object { text : string, [ href : string] };' with the text
+   split into parts, where those with also 'href' property defined are meant
+   to be anchors. */
+static JSCValue *
+evo_editor_jsc_split_text_with_links (const gchar *text,
+                                     JSCContext *jsc_context)
+{
+       // stephenhay from https://mathiasbynens.be/demo/url-regex
+       const gchar *URL_PATTERN = "((?:(?:(?:"
+                                  "news|telnet|nntp|file|https?|s?ftp|webcal|localhost|ssh"
+                                  ")\\:\\/\\/)|(?:www\\.|ftp\\.))[^\\s\\/\\$\\.\\?#].[^\\s]*+)";
+       // from camel-url-scanner.c
+       const gchar *URL_INVALID_TRAILING_CHARS = ",.:;?!-|}])\">";
+       // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
+       const gchar *EMAIL_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])?)*+";
+       JSCValue *array = NULL;
+       guint array_len = 0;
+       gboolean done = FALSE;
+
+       if (!text || !*text)
+               return jsc_value_new_null (jsc_context);
+
+       #define add_to_array(_obj) G_STMT_START { \
+               if (!array) \
+                       array = jsc_value_new_array (jsc_context, G_TYPE_NONE); \
+               jsc_value_object_set_property_at_index (array, array_len, _obj); \
+               array_len++; \
+               } G_STMT_END
+
+       while (!done) {
+               gboolean is_email;
+               gint start = -1, end = -1;
+
+               done = TRUE;
+
+               is_email = strchr (text, '@') && !strstr (text, "://");
+
+               evo_editor_find_pattern (text, is_email ? EMAIL_PATTERN : URL_PATTERN, &start, &end);
+
+               if (start >= 0 && end >= 0) {
+                       const gchar *url_end;
+
+                       url_end = text + end - 1;
+
+                       /* 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 > start && *url_end && strchr (URL_INVALID_TRAILING_CHARS, *url_end)) {
+                               gchar open_bracket = 0, close_bracket = *url_end;
+
+                               if (close_bracket == ')')
+                                       open_bracket = '(';
+                               else if (close_bracket == '}')
+                                       open_bracket = '{';
+                               else if (close_bracket == ']')
+                                       open_bracket = '[';
+                               else if (close_bracket == '>')
+                                       open_bracket = '<';
+
+                               if (open_bracket != 0) {
+                                       gint n_opened = 0, n_closed = 0;
+                                       const gchar *ptr;
+
+                                       for (ptr = text + start; ptr <= url_end; ptr++) {
+                                               if (*ptr == open_bracket)
+                                                       n_opened++;
+                                               else if (*ptr == close_bracket)
+                                                       n_closed++;
+                                       }
 
-               e_editor_dom_replace_image_src (editor_page, selector, uri);
+                                       /* The closing bracket can match one inside the URL,
+                                          thus keep it there. */
+                                       if (n_opened > 0 && n_opened - n_closed >= 0)
+                                               break;
+                               }
 
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMInsertSmiley") == 0) {
-               const gchar *smiley_name;
+                               url_end--;
+                               end--;
+                       }
 
-               g_variant_get (parameters, "(t&s)", &page_id, &smiley_name);
+                       if (end > start) {
+                               JSCValue *object, *string;
+                               gchar *url, *tmp;
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                               if (start > 0) {
+                                       tmp = g_strndup (text, start);
 
-               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;
+                                       object = jsc_value_new_object (jsc_context, NULL, NULL);
 
-               g_variant_get (parameters, "(tiib)", &page_id, &x, &y, &cancel_if_not_collapsed);
+                                       string = jsc_value_new_string (jsc_context, tmp);
+                                       jsc_value_object_set_property (object, "text", string);
+                                       g_clear_object (&string);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                                       add_to_array (object);
 
-               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_clear_object (&object);
+                                       g_free (tmp);
+                               }
 
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMSelectionIndent") == 0) {
-               g_variant_get (parameters, "(t)", &page_id);
+                               tmp = g_strndup (text + start, end - start);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                               if (is_email)
+                                       url = g_strconcat ("mailto:";, tmp, NULL);
+                               else if (g_str_has_prefix (tmp, "www."))
+                                       url = g_strconcat ("https://";, tmp, NULL);
+                               else
+                                       url = NULL;
 
-               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);
+                               object = jsc_value_new_object (jsc_context, NULL, NULL);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                               string = jsc_value_new_string (jsc_context, tmp);
+                               jsc_value_object_set_property (object, "text", string);
+                               g_clear_object (&string);
 
-               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);
+                               string = jsc_value_new_string (jsc_context, url ? url : tmp);
+                               jsc_value_object_set_property (object, "href", string);
+                               g_clear_object (&string);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                               add_to_array (object);
 
-               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_clear_object (&object);
+                               g_free (tmp);
+                               g_free (url);
 
-               g_variant_get (parameters, "(t&s)", &page_id, &uri);
+                               text = text + end;
+                               done = FALSE;
+                       }
+               }
+       }
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       if (array && *text) {
+               JSCValue *object, *string;
 
-               e_editor_dom_insert_image (editor_page, uri);
+               object = jsc_value_new_object (jsc_context, NULL, NULL);
 
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMInsertReplaceAllHistoryEvent") == 0) {
-               const gchar *replacement, *search_text;
+               string = jsc_value_new_string (jsc_context, text);
+               jsc_value_object_set_property (object, "text", string);
+               g_clear_object (&string);
 
-               g_variant_get (parameters, "(t&s&s)", &page_id, &search_text, &replacement);
+               add_to_array (object);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+               g_clear_object (&object);
+       }
 
-               e_editor_dom_insert_replace_all_history_event (editor_page, search_text, replacement);
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } else if (g_strcmp0 (method_name, "DOMSelectionReplace") == 0) {
-               const gchar *replacement;
+       #undef add_to_array
 
-               g_variant_get (parameters, "(t&s)", &page_id, &replacement);
+       return array ? array : jsc_value_new_null (jsc_context);
+}
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+/* Returns 'null' or an object { text : string, imageUri : string, width : nnn, height : nnn }
+   where only the 'text' is required, describing an emoticon. */
+static JSCValue *
+evo_editor_jsc_lookup_emoticon (const gchar *iconName,
+                               gboolean use_unicode_smileys,
+                               JSCContext *jsc_context)
+{
+       JSCValue *object = NULL;
 
-               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;
+       if (iconName && *iconName) {
+               const EEmoticon *emoticon;
 
-               g_variant_get (parameters, "(ti)", &page_id, &alignment);
+               emoticon = e_emoticon_chooser_lookup_emoticon (iconName);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+               if (emoticon) {
+                       JSCValue *value;
 
-               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;
+                       object = jsc_value_new_object (jsc_context, NULL, NULL);
 
-               g_variant_get (parameters, "(tb)", &page_id, &bold);
+                       if (use_unicode_smileys) {
+                               value = jsc_value_new_string (jsc_context, emoticon->unicode_character);
+                               jsc_value_object_set_property (object, "text", value);
+                               g_clear_object (&value);
+                       } else {
+                               gchar *image_uri;
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                               value = jsc_value_new_string (jsc_context, emoticon->text_face);
+                               jsc_value_object_set_property (object, "text", value);
+                               g_clear_object (&value);
 
-               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;
+                               image_uri = e_emoticon_get_uri ((EEmoticon *) emoticon);
 
-               g_variant_get (parameters, "(ti)", &page_id, &block_format);
+                               if (image_uri) {
+                                       value = jsc_value_new_string (jsc_context, image_uri);
+                                       jsc_value_object_set_property (object, "imageUri", value);
+                                       g_clear_object (&value);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+                                       value = jsc_value_new_number (jsc_context, 16);
+                                       jsc_value_object_set_property (object, "width", value);
+                                       g_clear_object (&value);
 
-               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;
+                                       value = jsc_value_new_number (jsc_context, 16);
+                                       jsc_value_object_set_property (object, "height", value);
+                                       g_clear_object (&value);
 
-               g_variant_get (parameters, "(t&s)", &page_id, &color);
+                                       g_free (image_uri);
+                               }
+                       }
+               }
+       }
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       return object ? object : jsc_value_new_null (jsc_context);
+}
 
-               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;
+static void
+evo_editor_jsc_set_spell_check_languages (const gchar *langs,
+                                         GWeakRef *wkrf_extension)
+{
+       EEditorWebExtension *extension;
+       gchar **strv;
 
-               g_variant_get (parameters, "(ti)", &page_id, &font_size);
+       g_return_if_fail (wkrf_extension != NULL);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       extension = g_weak_ref_get (wkrf_extension);
 
-               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;
+       if (!extension)
+               return;
 
-               g_variant_get (parameters, "(tb)", &page_id, &italic);
+       if (langs && *langs)
+               strv = g_strsplit (langs, "|", -1);
+       else
+               strv = NULL;
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       if (!extension->priv->spell_checker)
+               extension->priv->spell_checker = e_spell_checker_new ();
 
-               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;
+       e_spell_checker_set_active_languages (extension->priv->spell_checker, (const gchar * const *) strv);
 
-               g_variant_get (parameters, "(tb)", &page_id, &monospaced);
+       g_object_unref (extension);
+       g_strfreev (strv);
+}
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+/* Returns whether the 'word' is a properly spelled word. It checks
+   with languages previously set by EvoEditor.SetSpellCheckLanguages(). */
+static gboolean
+evo_editor_jsc_spell_check_word (const gchar *word,
+                                GWeakRef *wkrf_extension)
+{
+       EEditorWebExtension *extension;
+       gboolean is_correct;
 
-               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_return_val_if_fail (wkrf_extension != NULL, FALSE);
 
-               g_variant_get (parameters, "(tb)", &page_id, &strikethrough);
+       extension = g_weak_ref_get (wkrf_extension);
 
-               editor_page = get_editor_page_or_return_dbus_error (invocation, extension, page_id);
-               if (!editor_page)
-                       goto error;
+       if (!extension)
+               return TRUE;
 
-               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;
+       /* It should be created as part of EvoEditor.SetSpellCheckLanguages(). */
+       g_warn_if_fail (extension->priv->spell_checker != NULL);
 
-               g_variant_get (parameters, "(tb)", &page_id, &subscript);
+       if (!extension->priv->spell_checker)
+               extension->priv->spell_checker = e_spell_checker_new ();
 
-               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, "DOMReplaceCaretWord") == 0) {
-               const gchar *replacement = NULL;
-
-               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_replace_caret_word (editor_page, replacement);
-
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } 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;
-               gint16 start_at_bottom = -1, top_signature = -1;
-               const gchar *content, *signature_id;
-               gchar *new_signature_id = NULL;
-
-               g_variant_get (
-                       parameters,
-                       "(t&sb&sbbbnn)",
-                       &page_id,
-                       &content,
-                       &is_html,
-                       &signature_id,
-                       &set_signature_from_message,
-                       &check_if_signature_is_changed,
-                       &ignore_next_signature_change,
-                       &start_at_bottom,
-                       &top_signature);
-
-               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,
-                       start_at_bottom,
-                       top_signature);
-
-               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, "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, "DOMLastDropOperationDidCopy") == 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_last_drop_operation_did_copy (manager);
-
-               g_dbus_method_invocation_return_value (invocation, NULL);
-       } 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,
-                       g_variant_new ("(u)", value));
-       } 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,
-                       g_variant_new ("(u)", value));
-       } 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);
-       }
+       is_correct = e_spell_checker_check_word (extension->priv->spell_checker, word, -1);
 
-       return;
+       g_object_unref (extension);
 
- error:
-       g_warning ("Cannot obtain WebKitWebPage for %" G_GUINT64_FORMAT, page_id);
+       return is_correct;
 }
 
 static void
-web_page_gone_cb (gpointer user_data,
-                  GObject *gone_web_page)
+window_object_cleared_cb (WebKitScriptWorld *world,
+                         WebKitWebPage *page,
+                         WebKitFrame *frame,
+                         gpointer user_data)
 {
        EEditorWebExtension *extension = user_data;
-       GSList *link;
+       JSCContext *jsc_context;
+       JSCValue *jsc_editor;
 
        g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
 
-       for (link = extension->priv->pages; link; link = g_slist_next (link)) {
-               EEditorPage *editor_page = link->data;
-               WebKitWebPage *web_page = e_editor_page_get_web_page (editor_page);
-
-               if ((gpointer) web_page == gone_web_page) {
-                       extension->priv->pages = g_slist_remove (extension->priv->pages, editor_page);
-                       g_object_unref (editor_page);
-                       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;
-
-               g_clear_object (&extension->priv->dbus_connection);
-       }
-
-       g_slist_free_full (extension->priv->pages, g_object_unref);
-       extension->priv->pages = NULL;
+       /* Load the javascript files only to the main frame, not to the subframes */
+       if (!webkit_frame_is_main_frame (frame))
+               return;
 
-       g_clear_object (&extension->priv->wk_extension);
+       jsc_context = webkit_frame_get_js_context (frame);
 
-       /* Chain up to parent's dispose() method. */
-       G_OBJECT_CLASS (e_editor_web_extension_parent_class)->dispose (object);
-}
+       /* Read in order approximately as each other uses the previous */
+       load_javascript_file (jsc_context, "e-convert.js");
+       load_javascript_file (jsc_context, "e-selection.js");
+       load_javascript_file (jsc_context, "e-undo-redo.js");
+       load_javascript_file (jsc_context, "e-editor.js");
 
-static void
-e_editor_web_extension_class_init (EEditorWebExtensionClass *class)
-{
-       GObjectClass *object_class = G_OBJECT_CLASS (class);
+       jsc_editor = jsc_context_get_value (jsc_context, "EvoEditor");
 
-       object_class->dispose = e_editor_web_extension_dispose;
+       if (jsc_editor) {
+               JSCValue *jsc_function;
+               const gchar *func_name;
 
-       g_type_class_add_private (object_class, sizeof(EEditorWebExtensionPrivate));
-}
+               /* EvoEditor.splitTextWithLinks(text) */
+               func_name = "splitTextWithLinks";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_split_text_with_links), g_object_ref (jsc_context), 
g_object_unref,
+                       JSC_TYPE_VALUE, 1, G_TYPE_STRING);
 
-static void
-e_editor_web_extension_init (EEditorWebExtension *extension)
-{
-       extension->priv = E_EDITOR_WEB_EXTENSION_GET_PRIVATE (extension);
-}
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
 
-static gpointer
-e_editor_web_extension_create_instance (gpointer data)
-{
-       return g_object_new (E_TYPE_EDITOR_WEB_EXTENSION, NULL);
-}
+               g_clear_object (&jsc_function);
 
-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));
-}
+               /* EvoEditor.lookupEmoticon(iconName, useUnicodeSmileys) */
+               func_name = "lookupEmoticon";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_lookup_emoticon), g_object_ref (jsc_context), 
g_object_unref,
+                       JSC_TYPE_VALUE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
 
-static gboolean
-image_exists_in_cache (const gchar *image_uri)
-{
-       gchar *filename;
-       gchar *hash;
-       gboolean exists = FALSE;
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
 
-       if (!emd_global_http_cache)
-               return FALSE;
+               g_clear_object (&jsc_function);
 
-       hash = e_http_request_util_compute_uri_checksum (image_uri);
-       filename = camel_data_cache_get_filename (
-               emd_global_http_cache, "http", hash);
+               /* EvoEditor.SetSpellCheckLanguages(langs) */
+               func_name = "SetSpellCheckLanguages";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_set_spell_check_languages), e_weak_ref_new (extension), 
(GDestroyNotify) e_weak_ref_free,
+                       G_TYPE_NONE, 1, G_TYPE_STRING);
 
-       if (filename != NULL) {
-               struct stat st;
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
 
-               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_clear_object (&jsc_function);
 
-       g_free (hash);
+               /* EvoEditor.SpellCheckWord(word) */
+               func_name = "SpellCheckWord";
+               jsc_function = jsc_value_new_function (jsc_context, func_name,
+                       G_CALLBACK (evo_editor_jsc_spell_check_word), e_weak_ref_new (extension), 
(GDestroyNotify) e_weak_ref_free,
+                       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
 
-       return exists;
-}
+               jsc_value_object_set_property (jsc_editor, func_name, jsc_function);
 
-static gboolean
-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_val_if_fail (E_IS_EDITOR_PAGE (editor_page), FALSE);
-
-       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)) {
-               return FALSE;
+               g_clear_object (&jsc_function);
+               g_clear_object (&jsc_editor);
        }
 
-       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);
-
-       return TRUE;
+       g_clear_object (&jsc_context);
 }
 
 static gboolean
 web_page_send_request_cb (WebKitWebPage *web_page,
-                          WebKitURIRequest *request,
-                          WebKitURIResponse *redirected_response,
-                          EEditorWebExtension *extension)
+                         WebKitURIRequest *request,
+                         WebKitURIResponse *redirected_response,
+                         EEditorWebExtension *extension)
 {
-       const char *request_uri;
-       const char *page_uri;
-       gboolean uri_is_http;
+       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);
@@ -2455,136 +528,38 @@ web_page_send_request_cb (WebKitWebPage *web_page,
        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 (g_str_has_prefix (request_uri, "http:") ||
+           g_str_has_prefix (request_uri, "https:")) {
+               gchar *new_uri;
 
-       if (uri_is_http &&
-           !redirect_http_uri (extension, web_page, request))
-               return TRUE;
-
-       return FALSE;
-}
-
-static void
-web_page_document_loaded_cb (WebKitWebPage *web_page,
-                             gpointer user_data)
-{
-       WebKitDOMDocument *document;
-       WebKitDOMRange *range = NULL;
-       WebKitDOMDOMWindow *dom_window;
-       WebKitDOMDOMSelection *dom_selection;
-
-       g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
-
-       document = webkit_web_page_get_dom_document (web_page);
-       if (!document)
-               return;
+               new_uri = g_strconcat ("evo-", request_uri, NULL);
 
-       dom_window = webkit_dom_document_get_default_view (document);
-       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               webkit_uri_request_set_uri (request, new_uri);
 
-       /* Make sure there is a cursor located in the body after the document loads. */
-       if (!webkit_dom_dom_selection_get_anchor_node (dom_selection) &&
-           !webkit_dom_dom_selection_get_focus_node (dom_selection)) {
-               range = webkit_dom_document_caret_range_from_point (document, 0, 0);
-               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
-               webkit_dom_dom_selection_add_range (dom_selection, range);
+               g_free (new_uri);
        }
 
-       g_clear_object (&range);
-       g_clear_object (&dom_selection);
-       g_clear_object (&dom_window);
+       return FALSE;
 }
 
 static void
-web_page_notify_uri_cb (GObject *object,
-                       GParamSpec *param,
-                       gpointer user_data)
+web_page_document_loaded_cb (WebKitWebPage *web_page,
+                            gpointer user_data)
 {
-       EEditorWebExtension *extension = user_data;
-       WebKitWebPage *web_page;
-       GSList *link;
-       const gchar *uri;
-
-       g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
-
-       web_page = WEBKIT_WEB_PAGE (object);
-       uri = webkit_web_page_get_uri (web_page);
-
-       for (link = extension->priv->pages; link; link = g_slist_next (link)) {
-               EEditorPage *page = link->data;
-
-               if (page && e_editor_page_get_web_page (page) == web_page) {
-                       gint new_stamp = 0;
-
-                       if (uri && *uri) {
-                               SoupURI *suri;
-
-                               suri = soup_uri_new (uri);
-                               if (suri) {
-                                       if (soup_uri_get_query (suri)) {
-                                               GHashTable *form;
-
-                                               form = soup_form_decode (soup_uri_get_query (suri));
-                                               if (form) {
-                                                       const gchar *evo_stamp;
-
-                                                       evo_stamp = g_hash_table_lookup (form, "evo-stamp");
-                                                       if (evo_stamp)
-                                                               new_stamp = (gint) g_ascii_strtoll 
(evo_stamp, NULL, 10);
-
-                                                       g_hash_table_destroy (form);
-                                               }
-                                       }
-
-                                       soup_uri_free (suri);
-                               }
-                       }
-
-                       e_editor_page_set_stamp (page, new_stamp);
-
-                       if (extension->priv->dbus_connection) {
-                               GError *error = NULL;
-
-                               g_dbus_connection_emit_signal (
-                                       extension->priv->dbus_connection,
-                                       NULL,
-                                       E_WEBKIT_EDITOR_WEB_EXTENSION_OBJECT_PATH,
-                                       E_WEBKIT_EDITOR_WEB_EXTENSION_INTERFACE,
-                                       "ExtensionHandlesPage",
-                                       g_variant_new ("(ti)", webkit_web_page_get_id (web_page), new_stamp),
-                                       &error);
-
-                               if (error) {
-                                       g_warning ("Error emitting signal ExtensionHandlesPage: %s", 
error->message);
-                                       g_error_free (error);
-                               }
-                       }
-
-                       return;
-               }
-       }
+       g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
 
-       g_warning ("%s: Cannot find web_page %p\n", G_STRFUNC, web_page);
+       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), user_data);
 }
 
 static void
 web_page_created_cb (WebKitWebExtension *wk_extension,
-                     WebKitWebPage *web_page,
-                     EEditorWebExtension *extension)
+                    WebKitWebPage *web_page,
+                    EEditorWebExtension *extension)
 {
-       EEditorPage *editor_page;
-
        g_return_if_fail (WEBKIT_IS_WEB_PAGE (web_page));
        g_return_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension));
 
-       editor_page = e_editor_page_new (web_page, extension);
-       extension->priv->pages = g_slist_prepend (extension->priv->pages, editor_page);
-
-       g_object_weak_ref (G_OBJECT (web_page), web_page_gone_cb, extension);
+       window_object_cleared_cb (NULL, web_page, webkit_web_page_get_main_frame (web_page), extension);
 
        g_signal_connect (
                web_page, "send-request",
@@ -2592,77 +567,25 @@ web_page_created_cb (WebKitWebExtension *wk_extension,
 
        g_signal_connect (
                web_page, "document-loaded",
-               G_CALLBACK (web_page_document_loaded_cb), NULL);
-
-       g_signal_connect_object (
-               web_page, "notify::uri",
-               G_CALLBACK (web_page_notify_uri_cb),
-               extension, 0);
+               G_CALLBACK (web_page_document_loaded_cb), extension);
 }
 
 void
 e_editor_web_extension_initialize (EEditorWebExtension *extension,
-                                   WebKitWebExtension *wk_extension)
+                                  WebKitWebExtension *wk_extension)
 {
+       WebKitScriptWorld *script_world;
+
        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 = g_object_ref (connection);
-               }
-       }
-}
-
-GDBusConnection *
-e_editor_web_extension_get_connection (EEditorWebExtension *extension)
-{
-       g_return_val_if_fail (E_IS_EDITOR_WEB_EXTENSION (extension), NULL);
+       script_world = webkit_script_world_get_default ();
 
-       return extension->priv->dbus_connection;
+       g_signal_connect (script_world, "window-object-cleared",
+               G_CALLBACK (window_object_cleared_cb), extension);
 }
diff --git a/src/modules/webkit-editor/web-extension/e-editor-web-extension.h 
b/src/modules/webkit-editor/web-extension/e-editor-web-extension.h
index 987b3de1c0..9470b63b15 100644
--- a/src/modules/webkit-editor/web-extension/e-editor-web-extension.h
+++ b/src/modules/webkit-editor/web-extension/e-editor-web-extension.h
@@ -22,12 +22,6 @@
 #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 ())
@@ -72,11 +66,5 @@ EEditorWebExtension *
 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/src/plugins/external-editor/external-editor.c b/src/plugins/external-editor/external-editor.c
index fd140b81bd..a4c7ce17f3 100644
--- a/src/plugins/external-editor/external-editor.c
+++ b/src/plugins/external-editor/external-editor.c
@@ -56,9 +56,6 @@ static gboolean       key_press_cb                    (GtkWidget *widget,
                                                 GdkEventKey *event,
                                                 EMsgComposer *composer);
 
-/* used to track when the external editor is active */
-static GThread *editor_thread;
-
 gint e_plugin_lib_enable (EPlugin *ep, gint enable);
 
 gint
@@ -201,9 +198,23 @@ enable_composer_idle (gpointer user_data)
 struct ExternalEditorData {
        EMsgComposer *composer;
        gchar *content;
-       gint cursor_position, cursor_offset;
+       GDestroyNotify content_destroy_notify;
+       guint cursor_position, cursor_offset;
 };
 
+static void
+external_editor_data_free (gpointer ptr)
+{
+       struct ExternalEditorData *eed = ptr;
+
+       if (eed) {
+               g_clear_object (&eed->composer);
+               if (eed->content_destroy_notify)
+                       eed->content_destroy_notify (eed->content);
+               g_slice_free (struct ExternalEditorData, eed);
+       }
+}
+
 /* needed because the new thread needs to call g_idle_add () */
 static gboolean
 update_composer_text (gpointer user_data)
@@ -224,9 +235,7 @@ update_composer_text (gpointer user_data)
 
        e_content_editor_set_changed (cnt_editor, TRUE);
 
-       g_clear_object (&eed->composer);
-       g_free (eed->content);
-       g_slice_free (struct ExternalEditorData, eed);
+       external_editor_data_free (eed);
 
        return FALSE;
 }
@@ -387,7 +396,8 @@ external_editor_thread (gpointer user_data)
 
                        eed2 = g_slice_new0 (struct ExternalEditorData);
                        eed2->composer = g_object_ref (eed->composer);
-                       eed2->content =  camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
+                       eed2->content = camel_text_to_html (buf, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
+                       eed2->content_destroy_notify = g_free;
 
                        g_idle_add ((GSourceFunc) update_composer_text, eed2);
 
@@ -406,14 +416,44 @@ finished:
        external_editor_running = FALSE;
        g_mutex_unlock (&external_editor_running_lock);
 
-       g_clear_object (&eed->composer);
-       g_free (eed->content);
-       g_slice_free (struct ExternalEditorData, eed);
+       external_editor_data_free (eed);
 
        return NULL;
 }
 
-static void launch_editor (GtkAction *action, EMsgComposer *composer)
+static void
+launch_editor_content_ready_cb (GObject *source_object,
+                               GAsyncResult *result,
+                               gpointer user_data)
+{
+       struct ExternalEditorData *eed = user_data;
+       EContentEditor *cnt_editor;
+       EContentEditorContentHash *content_hash;
+       GThread *editor_thread;
+       GError *error = NULL;
+
+       g_return_if_fail (E_IS_CONTENT_EDITOR (source_object));
+       g_return_if_fail (eed != NULL);
+
+       cnt_editor = E_CONTENT_EDITOR (source_object);
+
+       content_hash = e_content_editor_get_content_finish (cnt_editor, result, &error);
+
+       if (!content_hash)
+               g_warning ("%s: Faild to get content: %s", G_STRFUNC, error ? error->message : "Unknown 
error");
+
+       eed->content = content_hash ? e_content_editor_util_steal_content_data (content_hash, 
E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, &(eed->content_destroy_notify)) : NULL;
+
+       editor_thread = g_thread_new (NULL, external_editor_thread, eed);
+       g_thread_unref (editor_thread);
+
+       e_content_editor_util_free_content_hash (content_hash);
+       g_clear_error (&error);
+}
+
+static void
+launch_editor (GtkAction *action,
+              EMsgComposer *composer)
 {
        struct ExternalEditorData *eed;
        EHTMLEditor *editor;
@@ -438,16 +478,9 @@ static void launch_editor (GtkAction *action, EMsgComposer *composer)
 
        eed = g_slice_new0 (struct ExternalEditorData);
        eed->composer = g_object_ref (composer);
-       eed->content = e_content_editor_get_content (cnt_editor,
-               E_CONTENT_EDITOR_GET_TEXT_PLAIN |
-               E_CONTENT_EDITOR_GET_PROCESSED,
-               NULL, NULL);
-       eed->cursor_position = e_content_editor_get_caret_position (cnt_editor);
-       if (eed->cursor_position > 0)
-               eed->cursor_offset = e_content_editor_get_caret_offset (cnt_editor);
 
-       editor_thread = g_thread_new (NULL, external_editor_thread, eed);
-       g_thread_unref (editor_thread);
+       e_content_editor_get_content (cnt_editor, E_CONTENT_EDITOR_GET_TO_SEND_PLAIN, NULL, NULL,
+               launch_editor_content_ready_cb, eed);
 }
 
 static GtkActionEntry entries[] = {
diff --git a/src/web-extensions/CMakeLists.txt b/src/web-extensions/CMakeLists.txt
index 35151503fe..dd699b76c0 100644
--- a/src/web-extensions/CMakeLists.txt
+++ b/src/web-extensions/CMakeLists.txt
@@ -19,6 +19,7 @@ add_dependencies(ewebextension
 target_compile_definitions(ewebextension PRIVATE
        -DG_LOG_DOMAIN=\"ewebextension\"
        -DEVOLUTION_WEBKITDATADIR=\"${webkitdatadir}\"
+       -DEVOLUTION_SOURCE_WEBKITDATADIR=\"${CMAKE_SOURCE_DIR}/data/webkit\"
 )
 
 target_compile_options(ewebextension PUBLIC
diff --git a/src/web-extensions/e-web-extension.c b/src/web-extensions/e-web-extension.c
index 29a7c209d2..979818a67c 100644
--- a/src/web-extensions/e-web-extension.c
+++ b/src/web-extensions/e-web-extension.c
@@ -145,19 +145,41 @@ evo_jsc_get_uri_tooltip (const gchar *uri,
        return e_util_get_uri_tooltip (uri);
 }
 
+static gboolean
+use_sources_js_file (void)
+{
+       static gint res = -1;
+
+       if (res == -1)
+               res = g_strcmp0 (g_getenv ("E_WEB_VIEW_TEST_SOURCES"), "1") == 0 ? 1 : 0;
+
+       return res;
+}
+
 static void
 load_javascript_file (JSCContext *jsc_context,
                      const gchar *js_filename)
 {
        JSCValue *result;
        JSCException *exception;
-       gchar *content, *filename, *resource_uri;
+       gchar *content, *filename = NULL, *resource_uri;
        gsize length = 0;
        GError *error = NULL;
 
        g_return_if_fail (jsc_context != NULL);
 
-       filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
+       if (use_sources_js_file ()) {
+               filename = g_build_filename (EVOLUTION_SOURCE_WEBKITDATADIR, js_filename, NULL);
+
+               if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+                       g_warning ("Cannot find '%s', using installed file '%s/%s' instead", filename, 
EVOLUTION_WEBKITDATADIR, js_filename);
+
+                       g_clear_pointer (&filename, g_free);
+               }
+       }
+
+       if (!filename)
+               filename = g_build_filename (EVOLUTION_WEBKITDATADIR, js_filename, NULL);
 
        if (!g_file_get_contents (filename, &content, &length, &error)) {
                g_warning ("Failed to load '%s': %s", filename, error ? error->message : "Unknown error");


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