[evolution/wip/webkit2] Bug 724651 - [webkit-composer] Undo or Redo does not work



commit 397528dfd75fde454b6af9ac26b9b30eae660384
Author: Tomas Popela <tpopela redhat com>
Date:   Tue Apr 21 15:04:30 2015 +0200

    Bug 724651 - [webkit-composer] Undo or Redo does not work
    
    Implement the undo and redo operations in EHTMLEditorView. We have
    to implement all the stuff ourselves as there was nothing that we
    can reuse on WebKit side (at least not now).

 e-util/e-html-editor-actions.c                     |   13 +-
 e-util/e-html-editor-cell-dialog.c                 |   51 +-
 e-util/e-html-editor-cell-dialog.h                 |    1 -
 e-util/e-html-editor-hrule-dialog.c                |    4 +-
 e-util/e-html-editor-image-dialog.c                |    8 +-
 e-util/e-html-editor-page-dialog.c                 |   21 +
 e-util/e-html-editor-table-dialog.c                |    4 +-
 e-util/e-html-editor-view.c                        |   35 +-
 e-util/e-html-editor-view.h                        |    2 +
 web-extensions/Makefile.am                         |    5 +
 .../e-html-editor-actions-dom-functions.c          |  142 ++-
 .../e-html-editor-actions-dom-functions.h          |   26 +-
 .../e-html-editor-cell-dialog-dom-functions.c      |   52 +
 .../e-html-editor-cell-dialog-dom-functions.h      |    7 +
 web-extensions/e-html-editor-history-event.h       |   98 +
 .../e-html-editor-hrule-dialog-dom-functions.c     |   64 +-
 .../e-html-editor-hrule-dialog-dom-functions.h     |    4 +
 .../e-html-editor-image-dialog-dom-functions.c     |   51 +
 .../e-html-editor-image-dialog-dom-functions.h     |   11 +
 .../e-html-editor-link-dialog-dom-functions.c      |    5 +-
 .../e-html-editor-link-dialog-dom-functions.h      |    3 +
 .../e-html-editor-page-dialog-dom-functions.c      |   63 +
 .../e-html-editor-page-dialog-dom-functions.h      |   38 +
 .../e-html-editor-selection-dom-functions.c        |  819 +++++++--
 .../e-html-editor-selection-dom-functions.h        |   28 +-
 .../e-html-editor-table-dialog-dom-functions.c     |   55 +-
 .../e-html-editor-table-dialog-dom-functions.h     |    5 +
 web-extensions/e-html-editor-undo-redo-manager.c   | 1867 ++++++++++++++++++++
 web-extensions/e-html-editor-undo-redo-manager.h   |  102 ++
 web-extensions/e-html-editor-view-dom-functions.c  |  796 +++++++--
 web-extensions/e-html-editor-view-dom-functions.h  |   25 +
 web-extensions/e-html-editor-web-extension.c       |  194 ++-
 web-extensions/e-html-editor-web-extension.h       |   19 +-
 33 files changed, 4186 insertions(+), 432 deletions(-)
---
diff --git a/e-util/e-html-editor-actions.c b/e-util/e-html-editor-actions.c
index eb7e17a..65a7d86 100644
--- a/e-util/e-html-editor-actions.c
+++ b/e-util/e-html-editor-actions.c
@@ -572,8 +572,7 @@ action_properties_cell_cb (GtkAction *action,
                        e_html_editor_cell_dialog_new (editor);
        }
 
-       e_html_editor_cell_dialog_show (
-               E_HTML_EDITOR_CELL_DIALOG (editor->priv->cell_dialog));
+       gtk_window_present (GTK_WINDOW (editor->priv->cell_dialog));
 }
 
 static void
@@ -668,8 +667,7 @@ action_redo_cb (GtkAction *action,
        EHTMLEditorView *view = e_html_editor_get_view (editor);
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               webkit_web_view_execute_editing_command (
-                       WEBKIT_WEB_VIEW (view), WEBKIT_EDITING_COMMAND_REDO);
+               e_html_editor_view_redo (view);
 }
 
 static void
@@ -737,8 +735,7 @@ action_undo_cb (GtkAction *action,
        EHTMLEditorView *view = e_html_editor_get_view (editor);
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
-               webkit_web_view_execute_editing_command (
-                       WEBKIT_WEB_VIEW (view), WEBKIT_EDITING_COMMAND_UNDO);
+               e_html_editor_view_undo (view);
 }
 
 static void
@@ -749,7 +746,7 @@ action_unindent_cb (GtkAction *action,
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
                html_editor_call_simple_extension_function (
-                       editor, "EHTMLEditorSelectionDOMUnindent");
+                       editor, "DOMSelectionUnindent");
 }
 
 static void
@@ -760,7 +757,7 @@ action_wrap_lines_cb (GtkAction *action,
 
        if (gtk_widget_has_focus (GTK_WIDGET (view)))
                html_editor_call_simple_extension_function (
-                       editor, "EHTMLEditorSelectionDOMWrapLines");
+                       editor, "DOMSelectionWrap");
 }
 
 static void
diff --git a/e-util/e-html-editor-cell-dialog.c b/e-util/e-html-editor-cell-dialog.c
index 0b58149..e70f52e 100644
--- a/e-util/e-html-editor-cell-dialog.c
+++ b/e-util/e-html-editor-cell-dialog.c
@@ -450,6 +450,19 @@ html_editor_cell_dialog_show (GtkWidget *widget)
        if (!web_extension)
                return;
 
+       g_dbus_proxy_call (
+               web_extension,
+               "EHTMLEditorCellDialogMarkCurrentCellElement",
+               g_variant_new (
+                       "(ts)",
+                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view)),
+                       "-x-evo-table-cell"),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1,
+               NULL,
+               NULL,
+               NULL);
+
        gtk_toggle_button_set_active (
                GTK_TOGGLE_BUTTON (dialog->priv->scope_cell_button), TRUE);
 
@@ -670,8 +683,8 @@ html_editor_cell_dialog_hide (GtkWidget *widget)
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        view = e_html_editor_get_view (editor);
 
-       e_html_editor_view_remove_element_attribute (
-               view, "#-x-evo-current-cell", "id");
+       e_html_editor_view_call_simple_extension_function_sync (
+               view, "EHTMLEditorCellDialogSaveHistoryOnExit");
 
        GTK_WIDGET_CLASS (e_html_editor_cell_dialog_parent_class)->hide (widget);
 }
@@ -982,37 +995,3 @@ e_html_editor_cell_dialog_new (EHTMLEditor *editor)
                        "title", _("Cell Properties"),
                        NULL));
 }
-
-void
-e_html_editor_cell_dialog_show (EHTMLEditorCellDialog *dialog)
-{
-       EHTMLEditor *editor;
-       EHTMLEditorView *view;
-       EHTMLEditorCellDialogClass *class;
-       GDBusProxy *web_extension;
-
-       g_return_if_fail (E_IS_HTML_EDITOR_CELL_DIALOG (dialog));
-
-       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
-       view = e_html_editor_get_view (editor);
-       web_extension = e_html_editor_view_get_web_extension_proxy (view);
-       if (!web_extension)
-               return;
-
-       g_dbus_proxy_call (
-               web_extension,
-               "EHTMLEditorCellDialogMarkCurrentCellElement",
-               g_variant_new (
-                       "(ts)",
-                       webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view)),
-                       "-x-evo-table-cell"),
-               G_DBUS_CALL_FLAGS_NONE,
-               -1,
-               NULL,
-               NULL,
-               NULL);
-
-       class = E_HTML_EDITOR_CELL_DIALOG_GET_CLASS (dialog);
-       GTK_WIDGET_CLASS (class)->show (GTK_WIDGET (dialog));
-}
-
diff --git a/e-util/e-html-editor-cell-dialog.h b/e-util/e-html-editor-cell-dialog.h
index 963e12b..68ae1bb 100644
--- a/e-util/e-html-editor-cell-dialog.h
+++ b/e-util/e-html-editor-cell-dialog.h
@@ -64,7 +64,6 @@ struct _EHTMLEditorCellDialogClass {
 GType          e_html_editor_cell_dialog_get_type
                                                (void) G_GNUC_CONST;
 GtkWidget *    e_html_editor_cell_dialog_new   (EHTMLEditor *editor);
-void           e_html_editor_cell_dialog_show  (EHTMLEditorCellDialog *dialog);
 
 G_END_DECLS
 
diff --git a/e-util/e-html-editor-hrule-dialog.c b/e-util/e-html-editor-hrule-dialog.c
index 4b8ff38..e60bde8 100644
--- a/e-util/e-html-editor-hrule-dialog.c
+++ b/e-util/e-html-editor-hrule-dialog.c
@@ -281,8 +281,8 @@ html_editor_hrule_dialog_hide (GtkWidget *widget)
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        view = e_html_editor_get_view (editor);
 
-       e_html_editor_view_remove_element_attribute (
-               view, "#-x-evo-current-hr", "id");
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorHRuleDialogSaveHistoryOnExit");
 
        GTK_WIDGET_CLASS (e_html_editor_hrule_dialog_parent_class)->hide (widget);
 }
diff --git a/e-util/e-html-editor-image-dialog.c b/e-util/e-html-editor-image-dialog.c
index 84d2296..4030986 100644
--- a/e-util/e-html-editor-image-dialog.c
+++ b/e-util/e-html-editor-image-dialog.c
@@ -522,6 +522,9 @@ html_editor_image_dialog_show (GtkWidget *widget)
        if (!web_extension)
                return;
 
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorImageDialogMarkImage");
+
        result = e_html_editor_view_get_element_attribute (
                view, "#-x-evo-current-img", "data-uri");
 
@@ -690,8 +693,9 @@ html_editor_image_dialog_hide (GtkWidget *widget)
        dialog = E_HTML_EDITOR_IMAGE_DIALOG (widget);
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        view = e_html_editor_get_view (editor);
-       e_html_editor_view_remove_element_attribute (
-               view, "#-x-evo-current-img", "id");
+
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorImageDialogSaveHistoryOnExit");
 
        GTK_WIDGET_CLASS (e_html_editor_image_dialog_parent_class)->hide (widget);
 }
diff --git a/e-util/e-html-editor-page-dialog.c b/e-util/e-html-editor-page-dialog.c
index 795c748..269be51 100644
--- a/e-util/e-html-editor-page-dialog.c
+++ b/e-util/e-html-editor-page-dialog.c
@@ -342,6 +342,9 @@ html_editor_page_dialog_show (GtkWidget *widget)
        if (!web_extension)
                return;
 
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorPageDialogSaveHistory");
+
        result = e_html_editor_view_get_element_attribute (
                view, "body", "data-uri");
 
@@ -441,6 +444,23 @@ html_editor_page_dialog_show (GtkWidget *widget)
 }
 
 static void
+html_editor_page_dialog_hide (GtkWidget *widget)
+{
+       EHTMLEditor *editor;
+       EHTMLEditorView *view;
+       EHTMLEditorPageDialog *dialog;
+
+       dialog = E_HTML_EDITOR_PAGE_DIALOG (widget);
+       editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
+       view = e_html_editor_get_view (editor);
+
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorPageDialogSaveHistoryOnExit");
+
+       GTK_WIDGET_CLASS (e_html_editor_page_dialog_parent_class)->hide (widget);
+}
+
+static void
 e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
 {
        GtkWidgetClass *widget_class;
@@ -449,6 +469,7 @@ e_html_editor_page_dialog_class_init (EHTMLEditorPageDialogClass *class)
 
        widget_class = GTK_WIDGET_CLASS (class);
        widget_class->show = html_editor_page_dialog_show;
+       widget_class->hide = html_editor_page_dialog_hide;
 }
 
 static void
diff --git a/e-util/e-html-editor-table-dialog.c b/e-util/e-html-editor-table-dialog.c
index ab7c420..92e606f 100644
--- a/e-util/e-html-editor-table-dialog.c
+++ b/e-util/e-html-editor-table-dialog.c
@@ -754,8 +754,8 @@ html_editor_table_dialog_hide (GtkWidget *widget)
        editor = e_html_editor_dialog_get_editor (E_HTML_EDITOR_DIALOG (dialog));
        view = e_html_editor_get_view (editor);
 
-       e_html_editor_view_remove_element_attribute (
-               view, "#-x-evo-current-table", "id");
+       e_html_editor_view_call_simple_extension_function (
+               view, "EHTMLEditorTableDialogSaveHistoryOnExit");
 
        GTK_WIDGET_CLASS (e_html_editor_table_dialog_parent_class)->hide (widget);
 }
diff --git a/e-util/e-html-editor-view.c b/e-util/e-html-editor-view.c
index 805d411..5da9b4e 100644
--- a/e-util/e-html-editor-view.c
+++ b/e-util/e-html-editor-view.c
@@ -149,6 +149,7 @@ html_editor_view_can_redo_cb (WebKitWebView *webkit_web_view,
 {
        gboolean value;
 
+/* FIXME WK2 Connect to extension */
        value = webkit_web_view_can_execute_editing_command_finish (
                webkit_web_view, result, NULL);
 
@@ -171,6 +172,7 @@ html_editor_view_can_undo_cb (WebKitWebView *webkit_web_view,
 {
        gboolean value;
 
+/* FIXME WK2 Connect to extension */
        value = webkit_web_view_can_execute_editing_command_finish (
                webkit_web_view, result, NULL);
 
@@ -186,6 +188,22 @@ html_editor_view_can_undo (EHTMLEditorView *view)
        return view->priv->can_undo;
 }
 
+void
+e_html_editor_view_undo (EHTMLEditorView *view)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       e_html_editor_view_call_simple_extension_function (view, "DOMUndo");
+}
+
+void
+e_html_editor_view_redo (EHTMLEditorView *view)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_VIEW (view));
+
+       e_html_editor_view_call_simple_extension_function (view, "DOMUndo");
+}
+
 static void
 html_editor_view_user_changed_contents_cb (EHTMLEditorView *view)
 {
@@ -194,20 +212,6 @@ html_editor_view_user_changed_contents_cb (EHTMLEditorView *view)
        web_view = WEBKIT_WEB_VIEW (view);
 
        e_html_editor_view_set_changed (view, TRUE);
-
-       webkit_web_view_can_execute_editing_command (
-               WEBKIT_WEB_VIEW (web_view),
-               WEBKIT_EDITING_COMMAND_REDO,
-               NULL, /* cancellable */
-               (GAsyncReadyCallback) html_editor_view_can_redo_cb,
-               view);
-
-       webkit_web_view_can_execute_editing_command (
-               WEBKIT_WEB_VIEW (web_view),
-               WEBKIT_EDITING_COMMAND_UNDO,
-               NULL, /* cancellable */
-               (GAsyncReadyCallback) html_editor_view_can_undo_cb,
-               view);
 }
 
 static void
@@ -521,8 +525,6 @@ html_editor_view_dispose (GObject *object)
 
        priv = E_HTML_EDITOR_VIEW_GET_PRIVATE (object);
 
-       g_clear_object (&priv->selection);
-
        if (priv->aliasing_settings != NULL) {
                g_signal_handlers_disconnect_by_data (priv->aliasing_settings, object);
                g_object_unref (priv->aliasing_settings);
@@ -1550,7 +1552,6 @@ e_html_editor_view_set_html_mode (EHTMLEditorView *view,
 
        view->priv->html_mode = html_mode;
 
-
        e_html_editor_view_call_simple_extension_function_sync (
                view, "DOMProcessContentAfterModeChange");
 
diff --git a/e-util/e-html-editor-view.h b/e-util/e-html-editor-view.h
index e4ad31c..e54657f 100644
--- a/e-util/e-html-editor-view.h
+++ b/e-util/e-html-editor-view.h
@@ -106,6 +106,8 @@ EHTMLEditorSelection *
 gboolean       e_html_editor_view_get_changed  (EHTMLEditorView *view);
 void           e_html_editor_view_set_changed  (EHTMLEditorView *view,
                                                 gboolean changed);
+void           e_html_editor_view_undo         (EHTMLEditorView *view);
+void           e_html_editor_view_redo         (EHTMLEditorView *view);
 gboolean       e_html_editor_view_get_html_mode
                                                (EHTMLEditorView *view);
 void           e_html_editor_view_set_html_mode
diff --git a/web-extensions/Makefile.am b/web-extensions/Makefile.am
index ab28cae..ad2b7ae 100644
--- a/web-extensions/Makefile.am
+++ b/web-extensions/Makefile.am
@@ -49,12 +49,15 @@ libehtmleditorwebextension_la_SOURCES =                     \
        e-dom-utils.h                                   \
        e-html-editor-actions-dom-functions.h           \
        e-html-editor-cell-dialog-dom-functions.h       \
+       e-html-editor-history-event.h                   \
        e-html-editor-hrule-dialog-dom-functions.h      \
        e-html-editor-image-dialog-dom-functions.h      \
        e-html-editor-link-dialog-dom-functions.h       \
+       e-html-editor-page-dialog-dom-functions.h       \
        e-html-editor-selection-dom-functions.h         \
        e-html-editor-spell-check-dialog-dom-functions.h\
        e-html-editor-table-dialog-dom-functions.h      \
+       e-html-editor-undo-redo-manager.h               \
        e-html-editor-view-dom-functions.h              \
        e-msg-composer-dom-functions.h                  \
        e-composer-private-dom-functions.c              \
@@ -64,9 +67,11 @@ libehtmleditorwebextension_la_SOURCES =                      \
        e-html-editor-hrule-dialog-dom-functions.c      \
        e-html-editor-image-dialog-dom-functions.c      \
        e-html-editor-link-dialog-dom-functions.c       \
+       e-html-editor-page-dialog-dom-functions.c       \
        e-html-editor-selection-dom-functions.c         \
        e-html-editor-spell-check-dialog-dom-functions.c\
        e-html-editor-table-dialog-dom-functions.c      \
+       e-html-editor-undo-redo-manager.c               \
        e-html-editor-view-dom-functions.c              \
        e-msg-composer-dom-functions.c                  \
        e-html-editor-web-extension.c                   \
diff --git a/web-extensions/e-html-editor-actions-dom-functions.c 
b/web-extensions/e-html-editor-actions-dom-functions.c
index e2980fd..fa64aec 100644
--- a/web-extensions/e-html-editor-actions-dom-functions.c
+++ b/web-extensions/e-html-editor-actions-dom-functions.c
@@ -19,6 +19,8 @@
 #include "e-html-editor-actions-dom-functions.h"
 
 #include "e-dom-utils.h"
+#include "e-html-editor-history-event.h"
+#include "e-html-editor-selection-dom-functions.h"
 
 static WebKitDOMElement *
 get_table_cell_element (WebKitDOMDocument *document)
@@ -26,11 +28,49 @@ get_table_cell_element (WebKitDOMDocument *document)
        return webkit_dom_document_get_element_by_id (document, "-x-evo-table-cell");
 }
 
+static void
+prepare_history_for_table (WebKitDOMDocument *document,
+                           WebKitDOMElement *table,
+                           EHTMLEditorHistoryEvent *ev)
+{
+       ev->type = HISTORY_TABLE_DIALOG;
+
+       dom_selection_get_coordinates (
+               document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+
+       ev->data.dom.from = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (table), TRUE);
+}
+
+
+static void
+save_history_for_table (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension,
+                        WebKitDOMElement *table,
+                        EHTMLEditorHistoryEvent *ev)
+{
+       EHTMLEditorUndoRedoManager *manager;
+
+       if (table)
+               ev->data.dom.to = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (table), TRUE);
+       else
+               ev->data.dom.to = NULL;
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
 void
-e_html_editor_dialog_delete_cell_contents (WebKitDOMDocument *document)
+e_html_editor_dialog_delete_cell_contents (WebKitDOMDocument *document,
+                                           EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
        WebKitDOMNode *node;
-       WebKitDOMElement *cell, *table_cell;
+       WebKitDOMElement *cell, *table_cell, *table;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -40,13 +80,23 @@ e_html_editor_dialog_delete_cell_contents (WebKitDOMDocument *document)
                cell = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TH");
        g_return_if_fail (cell != NULL);
 
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        while ((node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (cell))))
                remove_node (node);
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_delete_column (WebKitDOMDocument *document)
+e_html_editor_dialog_delete_column (WebKitDOMDocument *document,
+                                    EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
        WebKitDOMElement *cell, *table, *table_cell;
        WebKitDOMHTMLCollection *rows;
        gulong index, length, ii;
@@ -63,6 +113,9 @@ e_html_editor_dialog_delete_column (WebKitDOMDocument *document)
        table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        rows = webkit_dom_html_table_element_get_rows (
                        WEBKIT_DOM_HTML_TABLE_ELEMENT (table));
        length = webkit_dom_html_collection_get_length (rows);
@@ -81,12 +134,16 @@ e_html_editor_dialog_delete_column (WebKitDOMDocument *document)
        }
 
        g_object_unref (rows);
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_delete_row (WebKitDOMDocument *document)
+e_html_editor_dialog_delete_row (WebKitDOMDocument *document,
+                                 EHTMLEditorWebExtension *extension)
 {
-       WebKitDOMElement *row, *table_cell;
+       EHTMLEditorHistoryEvent *ev = NULL;
+       WebKitDOMElement *row, *table, *table_cell;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -94,14 +151,22 @@ e_html_editor_dialog_delete_row (WebKitDOMDocument *document)
        row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
        g_return_if_fail (row != NULL);
 
-       webkit_dom_node_remove_child (
-               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
-               WEBKIT_DOM_NODE (row), NULL);
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (row));
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_delete_table (WebKitDOMDocument *document)
+e_html_editor_dialog_delete_table (WebKitDOMDocument *document,
+                                   EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
        WebKitDOMElement *table, *table_cell;
 
        table_cell = get_table_cell_element (document);
@@ -110,16 +175,21 @@ e_html_editor_dialog_delete_table (WebKitDOMDocument *document)
        table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
        g_return_if_fail (table != NULL);
 
-       webkit_dom_node_remove_child (
-               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
-               WEBKIT_DOM_NODE (table), NULL);
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
+       remove_node (WEBKIT_DOM_NODE (table));
+
+       save_history_for_table (document, extension, NULL, ev);
 }
 
 void
-e_html_editor_dialog_insert_column_after (WebKitDOMDocument *document)
+e_html_editor_dialog_insert_column_after (WebKitDOMDocument *document,
+                                          EHTMLEditorWebExtension *extension)
 {
-       WebKitDOMElement *cell, *row, *table_cell;
+       EHTMLEditorHistoryEvent *ev = NULL;
        gulong index;
+       WebKitDOMElement *cell, *row, *table_cell, *table;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -132,6 +202,12 @@ e_html_editor_dialog_insert_column_after (WebKitDOMDocument *document)
        row = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TR");
        g_return_if_fail (row != NULL);
 
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        /* Get the first row in the table */
        row = WEBKIT_DOM_ELEMENT (
                webkit_dom_node_get_first_child (
@@ -147,13 +223,17 @@ e_html_editor_dialog_insert_column_after (WebKitDOMDocument *document)
                row = WEBKIT_DOM_ELEMENT (
                        webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
        }
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_insert_column_before (WebKitDOMDocument *document)
+e_html_editor_dialog_insert_column_before (WebKitDOMDocument *document,
+                                           EHTMLEditorWebExtension *extension)
 {
-       WebKitDOMElement *cell, *row, *table_cell;
+       EHTMLEditorHistoryEvent *ev = NULL;
        gulong index;
+       WebKitDOMElement *cell, *row, *table_cell, *table;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -167,6 +247,12 @@ e_html_editor_dialog_insert_column_before (WebKitDOMDocument *document)
        row = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TR");
        g_return_if_fail (row != NULL);
 
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (table_cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        /* Get the first row in the table */
        row = WEBKIT_DOM_ELEMENT (
                webkit_dom_node_get_first_child (
@@ -182,15 +268,19 @@ e_html_editor_dialog_insert_column_before (WebKitDOMDocument *document)
                row = WEBKIT_DOM_ELEMENT (
                        webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (row)));
        }
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_insert_row_above (WebKitDOMDocument *document)
+e_html_editor_dialog_insert_row_above (WebKitDOMDocument *document,
+                                       EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       gulong index, cell_count, ii;
        WebKitDOMElement *row, *table, *table_cell;
        WebKitDOMHTMLCollection *cells;
        WebKitDOMHTMLElement *new_row;
-       gulong index, cell_count, ii;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -201,6 +291,9 @@ e_html_editor_dialog_insert_row_above (WebKitDOMDocument *document)
        table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        index = webkit_dom_html_table_row_element_get_row_index (
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
 
@@ -216,15 +309,19 @@ e_html_editor_dialog_insert_row_above (WebKitDOMDocument *document)
        }
 
        g_object_unref (cells);
+
+       save_history_for_table (document, extension, table, ev);
 }
 
 void
-e_html_editor_dialog_insert_row_below (WebKitDOMDocument *document)
+e_html_editor_dialog_insert_row_below (WebKitDOMDocument *document,
+                                       EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       gulong index, cell_count, ii;
        WebKitDOMElement *row, *table, *table_cell;
        WebKitDOMHTMLCollection *cells;
        WebKitDOMHTMLElement *new_row;
-       gulong index, cell_count, ii;
 
        table_cell = get_table_cell_element (document);
        g_return_if_fail (table_cell != NULL);
@@ -235,6 +332,9 @@ e_html_editor_dialog_insert_row_below (WebKitDOMDocument *document)
        table = dom_node_find_parent_element (WEBKIT_DOM_NODE (row), "TABLE");
        g_return_if_fail (table != NULL);
 
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       prepare_history_for_table (document, table, ev);
+
        index = webkit_dom_html_table_row_element_get_row_index (
                        WEBKIT_DOM_HTML_TABLE_ROW_ELEMENT (row));
 
@@ -250,4 +350,6 @@ e_html_editor_dialog_insert_row_below (WebKitDOMDocument *document)
        }
 
        g_object_unref (cells);
+
+       save_history_for_table (document, extension, table, ev);
 }
diff --git a/web-extensions/e-html-editor-actions-dom-functions.h 
b/web-extensions/e-html-editor-actions-dom-functions.h
index ce7632a..1a78099 100644
--- a/web-extensions/e-html-editor-actions-dom-functions.h
+++ b/web-extensions/e-html-editor-actions-dom-functions.h
@@ -21,31 +21,41 @@
 
 #include <webkitdom/webkitdom.h>
 
+#include "e-html-editor-web-extension.h"
+
 G_BEGIN_DECLS
 
 void           e_html_editor_dialog_delete_cell_contents
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_delete_column
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_delete_row
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_delete_table
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_insert_column_after
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_insert_column_before
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_insert_row_above
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 void           e_html_editor_dialog_insert_row_below
-                                               (WebKitDOMDocument *document);
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
 
 G_END_DECLS
 
diff --git a/web-extensions/e-html-editor-cell-dialog-dom-functions.c 
b/web-extensions/e-html-editor-cell-dialog-dom-functions.c
index 91aa50b..1fe2f2c 100644
--- a/web-extensions/e-html-editor-cell-dialog-dom-functions.c
+++ b/web-extensions/e-html-editor-cell-dialog-dom-functions.c
@@ -19,6 +19,8 @@
 #include "e-html-editor-cell-dialog-dom-functions.h"
 
 #include "e-dom-utils.h"
+#include "e-html-editor-undo-redo-manager.h"
+#include "e-html-editor-selection-dom-functions.h"
 
 enum {
        SCOPE_CELL,
@@ -231,8 +233,10 @@ cell_set_header_style (WebKitDOMHTMLTableCellElement *cell,
 
 void
 e_html_editor_cell_dialog_mark_current_cell_element (WebKitDOMDocument *document,
+                                                     EHTMLEditorWebExtension *extension,
                                                      const gchar *id)
 {
+       EHTMLEditorUndoRedoManager *manager;
        WebKitDOMElement *element, *parent = NULL;
 
        element = webkit_dom_document_get_element_by_id (document, id);
@@ -245,7 +249,55 @@ e_html_editor_cell_dialog_mark_current_cell_element (WebKitDOMDocument *document
        if (element)
                webkit_dom_element_remove_attribute (element, "id");
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+               WebKitDOMElement *table;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_DIALOG;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               table = dom_node_find_parent_element (
+                       WEBKIT_DOM_NODE (parent), "TABLE");
+               ev->data.dom.from = webkit_dom_node_clone_node (
+                       WEBKIT_DOM_NODE (table), TRUE);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        webkit_dom_element_set_id (parent, "-x-evo-current-cell");
+
+}
+
+void
+e_html_editor_cell_dialog_save_history_on_exit (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
+       WebKitDOMElement *cell, *table;
+
+       cell = get_current_cell_element (document);
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (cell), "TABLE");
+       g_return_if_fail (table != NULL);
+
+       webkit_dom_element_remove_attribute (cell, "id");
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (table), TRUE);
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
 }
 
 void
diff --git a/web-extensions/e-html-editor-cell-dialog-dom-functions.h 
b/web-extensions/e-html-editor-cell-dialog-dom-functions.h
index 83959b2..75ec4a2 100644
--- a/web-extensions/e-html-editor-cell-dialog-dom-functions.h
+++ b/web-extensions/e-html-editor-cell-dialog-dom-functions.h
@@ -21,12 +21,19 @@
 
 #include <webkitdom/webkitdom.h>
 
+#include "e-html-editor-web-extension.h"
+
 G_BEGIN_DECLS
 
 void           e_html_editor_cell_dialog_mark_current_cell_element
                                                (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
                                                 const gchar *id);
 
+void           e_html_editor_cell_dialog_save_history_on_exit
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
 void           e_html_editor_cell_dialog_set_element_v_align
                                                (WebKitDOMDocument *document,
                                                 const gchar *v_align,
diff --git a/web-extensions/e-html-editor-history-event.h b/web-extensions/e-html-editor-history-event.h
new file mode 100644
index 0000000..62e65d2
--- /dev/null
+++ b/web-extensions/e-html-editor-history-event.h
@@ -0,0 +1,98 @@
+/*
+ * e-html-editor-history-event.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_HTML_EDITOR_HISTORY_EVENT_H
+#define E_HTML_EDITOR_HISTORY_EVENT_H
+
+#include "config.h"
+
+G_BEGIN_DECLS
+
+enum EHTMLEditorHistoryEventType {
+       HISTORY_ALIGNMENT,
+       HISTORY_BLOCK_FORMAT,
+       HISTORY_BOLD,
+       HISTORY_CELL_DIALOG,
+       HISTORY_DELETE, /* BackSpace, Delete, with and without selection */
+       HISTORY_FONT_COLOR,
+       HISTORY_FONT_SIZE,
+       HISTORY_HRULE_DIALOG,
+       HISTORY_INDENT,
+       HISTORY_INPUT,
+       HISTORY_IMAGE,
+       HISTORY_IMAGE_DIALOG,
+       HISTORY_INSERT_HTML,
+       HISTORY_ITALIC,
+       HISTORY_MONOSPACE,
+       HISTORY_PAGE_DIALOG,
+       HISTORY_PASTE,
+       HISTORY_PASTE_AS_TEXT,
+       HISTORY_PASTE_QUOTED,
+       HISTORY_REMOVE_LINK,
+       HISTORY_REPLACE,
+       HISTORY_REPLACE_ALL,
+       HISTORY_CITATION_SPLIT,
+       HISTORY_SMILEY,
+       HISTORY_START, /* Start of history */
+       HISTORY_STRIKETHROUGH,
+       HISTORY_TABLE_DIALOG,
+       HISTORY_UNDERLINE,
+       HISTORY_WRAP
+};
+
+typedef struct {
+       gint from; /* From what format we are changing. */
+       gint to; /* To what format we are changing. */
+} EHTMLEditorStyleChange;
+
+/* This is used for e-html-editor-*-dialogs */
+typedef struct {
+       WebKitDOMNode *from; /* From what node we are changing. */
+       WebKitDOMNode *to; /* To what node we are changing. */
+} EHTMLEditorDOMChange;
+
+typedef struct {
+       gchar *from; /* From what format we are changing. */
+       gchar *to; /* To what format we are changing. */
+} EHTMLEditorStringChange;
+
+typedef struct {
+       guint x;
+       guint y;
+} EHTMLEditorSelectionPoint;
+
+typedef struct {
+       EHTMLEditorSelectionPoint start;
+       EHTMLEditorSelectionPoint end;
+} EHTMLEditorSelection;
+
+typedef struct {
+       enum EHTMLEditorHistoryEventType type;
+       EHTMLEditorSelection before;
+       EHTMLEditorSelection after;
+       union {
+               WebKitDOMDocumentFragment *fragment;
+               EHTMLEditorStyleChange style;
+               EHTMLEditorStringChange string;
+               EHTMLEditorDOMChange dom;
+       } data;
+} EHTMLEditorHistoryEvent;
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_HISTORY_EVENT_H */
diff --git a/web-extensions/e-html-editor-hrule-dialog-dom-functions.c 
b/web-extensions/e-html-editor-hrule-dialog-dom-functions.c
index ef158b2..66f269c 100644
--- a/web-extensions/e-html-editor-hrule-dialog-dom-functions.c
+++ b/web-extensions/e-html-editor-hrule-dialog-dom-functions.c
@@ -20,22 +20,33 @@
 
 #include "e-dom-utils.h"
 #include "e-html-editor-selection-dom-functions.h"
+#include "e-html-editor-web-extension.h"
+#include "e-html-editor-undo-redo-manager.h"
 
 #define WEBKIT_DOM_USE_UNSTABLE_API
 #include <webkitdom/WebKitDOMDOMSelection.h>
 #include <webkitdom/WebKitDOMDOMWindowUnstable.h>
 
+static WebKitDOMElement *
+get_current_hrule_element (WebKitDOMDocument *document)
+{
+       return webkit_dom_document_get_element_by_id (document, "-x-evo-current-hr");
+}
+
 gboolean
 e_html_editor_hrule_dialog_find_hrule (WebKitDOMDocument *document,
                                        EHTMLEditorWebExtension *extension,
                                        WebKitDOMNode *node_under_mouse_click)
 {
-       if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node_under_mouse_click)) {
-               webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (node_under_mouse_click), "-x-evo-current-hr");
+       EHTMLEditorUndoRedoManager *manager;
+       gboolean created = FALSE;
+       WebKitDOMElement *rule;
 
-               return FALSE;
+       if (node_under_mouse_click && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node_under_mouse_click)) {
+               rule = WEBKIT_DOM_ELEMENT (node_under_mouse_click);
+               webkit_dom_element_set_id (rule, "-x-evo-current-hr");
        } else {
-               WebKitDOMElement *selection_start, *parent, *rule;
+               WebKitDOMElement *selection_start, *parent;
 
                dom_selection_save (document);
 
@@ -56,6 +67,49 @@ e_html_editor_hrule_dialog_find_hrule (WebKitDOMDocument *document,
                dom_selection_restore (document);
 
                e_html_editor_web_extension_set_content_changed (extension);
+
+               created = TRUE;
+       }
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_HRULE_DIALOG;
+
+               dom_selection_get_coordinates (
+                       document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               if (!created)
+                       ev->data.dom.from = webkit_dom_node_clone_node (
+                               WEBKIT_DOM_NODE (rule), FALSE);
+               else
+                       ev->data.dom.from = NULL;
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
        }
-       return TRUE;
+
+       return created;
+}
+
+void
+e_html_editor_hrule_dialog_save_history_on_exit (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
+       WebKitDOMElement *element;
+
+       element = get_current_hrule_element (document);
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (element), TRUE);
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
 }
diff --git a/web-extensions/e-html-editor-hrule-dialog-dom-functions.h 
b/web-extensions/e-html-editor-hrule-dialog-dom-functions.h
index 1d7c55a..de9eb93 100644
--- a/web-extensions/e-html-editor-hrule-dialog-dom-functions.h
+++ b/web-extensions/e-html-editor-hrule-dialog-dom-functions.h
@@ -30,6 +30,10 @@ gboolean     e_html_editor_hrule_dialog_find_hrule
                                                 EHTMLEditorWebExtension *extension,
                                                 WebKitDOMNode *node_under_mouse_click);
 
+void           e_html_editor_hrule_dialog_save_history_on_exit
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_HRULE_DIALOG_DOM_FUNCTIONS_H */
diff --git a/web-extensions/e-html-editor-image-dialog-dom-functions.c 
b/web-extensions/e-html-editor-image-dialog-dom-functions.c
index 4d9fb32..a892476 100644
--- a/web-extensions/e-html-editor-image-dialog-dom-functions.c
+++ b/web-extensions/e-html-editor-image-dialog-dom-functions.c
@@ -19,6 +19,9 @@
 #include "e-html-editor-image-dialog-dom-functions.h"
 
 #include "e-dom-utils.h"
+#include "e-html-editor-selection-dom-functions.h"
+#include "e-html-editor-web-extension.h"
+#include "e-html-editor-undo-redo-manager.h"
 
 static WebKitDOMElement *
 get_current_image_element (WebKitDOMDocument *document)
@@ -27,6 +30,54 @@ get_current_image_element (WebKitDOMDocument *document)
 }
 
 void
+e_html_editor_image_dialog_mark_image (WebKitDOMDocument *document,
+                                       EHTMLEditorWebExtension *extension,
+                                       WebKitDOMNode *node_under_mouse_click)
+{
+       EHTMLEditorUndoRedoManager *manager;
+
+       g_return_if_fail (node_under_mouse_click && WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT 
(node_under_mouse_click));
+
+       webkit_dom_element_set_id (WEBKIT_DOM_ELEMENT (node_under_mouse_click), "-x-evo-current-img");
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE_DIALOG;
+
+               dom_selection_get_coordinates (
+                       document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               ev->data.dom.from = webkit_dom_node_clone_node (node_under_mouse_click, FALSE);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+void
+e_html_editor_image_dialog_save_history_on_exit (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
+       WebKitDOMElement *element;
+
+       element = get_current_image_element (document);
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (element), TRUE);
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+}
+
+void
 e_html_editor_image_dialog_set_element_url (WebKitDOMDocument *document,
                                             const gchar *url)
 {
diff --git a/web-extensions/e-html-editor-image-dialog-dom-functions.h 
b/web-extensions/e-html-editor-image-dialog-dom-functions.h
index 63d4b31..46dd716 100644
--- a/web-extensions/e-html-editor-image-dialog-dom-functions.h
+++ b/web-extensions/e-html-editor-image-dialog-dom-functions.h
@@ -21,8 +21,19 @@
 
 #include <webkitdom/webkitdom.h>
 
+#include "e-html-editor-web-extension.h"
+
 G_BEGIN_DECLS
 
+void           e_html_editor_image_dialog_mark_image
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
+                                                WebKitDOMNode *node_under_mouse_click);
+
+void           e_html_editor_image_dialog_save_history_on_exit
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
 void           e_html_editor_image_dialog_set_element_url
                                                (WebKitDOMDocument *document,
                                                 const gchar *url);
diff --git a/web-extensions/e-html-editor-link-dialog-dom-functions.c 
b/web-extensions/e-html-editor-link-dialog-dom-functions.c
index 7e392ea..1e18c92 100644
--- a/web-extensions/e-html-editor-link-dialog-dom-functions.c
+++ b/web-extensions/e-html-editor-link-dialog-dom-functions.c
@@ -30,6 +30,7 @@
 
 void
 e_html_editor_link_dialog_ok (WebKitDOMDocument *document,
+                              EHTMLEditorWebExtension *extension,
                               const gchar *url,
                               const gchar *inner_text)
 {
@@ -92,13 +93,13 @@ e_html_editor_link_dialog_ok (WebKitDOMDocument *document,
                /* Check whether a text is selected or not */
                text = webkit_dom_range_get_text (range);
                if (text && *text) {
-                       dom_create_link (document, url);
+                       dom_create_link (document, extension, url);
                } else {
                        gchar *html = g_strdup_printf (
                                "<a href=\"%s\">%s</a>", url, inner_text);
 
                        dom_exec_command (
-                               document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html);
+                               document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html);
                        g_free (html);
 
                }
diff --git a/web-extensions/e-html-editor-link-dialog-dom-functions.h 
b/web-extensions/e-html-editor-link-dialog-dom-functions.h
index 89cbd6e..d47a732 100644
--- a/web-extensions/e-html-editor-link-dialog-dom-functions.h
+++ b/web-extensions/e-html-editor-link-dialog-dom-functions.h
@@ -21,9 +21,12 @@
 
 #include <webkitdom/webkitdom.h>
 
+#include "e-html-editor-web-extension.h"
+
 G_BEGIN_DECLS
 
 void           e_html_editor_link_dialog_ok    (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
                                                 const gchar *url,
                                                 const gchar *inner_text);
 
diff --git a/web-extensions/e-html-editor-page-dialog-dom-functions.c 
b/web-extensions/e-html-editor-page-dialog-dom-functions.c
new file mode 100644
index 0000000..b34e85a
--- /dev/null
+++ b/web-extensions/e-html-editor-page-dialog-dom-functions.c
@@ -0,0 +1,63 @@
+/*
+ * e-html-editor-page-dialog-dom-functions.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "e-html-editor-page-dialog-dom-functions.h"
+
+#include "e-dom-utils.h"
+#include "e-html-editor-selection-dom-functions.h"
+#include "e-html-editor-web-extension.h"
+
+void
+e_html_editor_page_dialog_save_history (WebKitDOMDocument *document,
+                                        EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorUndoRedoManager *manager;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+               WebKitDOMHTMLElement *body;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_PAGE_DIALOG;
+
+               dom_selection_get_coordinates (
+                       document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               body = webkit_dom_document_get_body (document);
+               ev->data.dom.from = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+void
+e_html_editor_page_dialog_save_history_on_exit (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
+       WebKitDOMHTMLElement *body;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+       body = webkit_dom_document_get_body (document);
+       ev->data.dom.to = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (body), FALSE);
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+}
diff --git a/web-extensions/e-html-editor-page-dialog-dom-functions.h 
b/web-extensions/e-html-editor-page-dialog-dom-functions.h
new file mode 100644
index 0000000..6dc10b4
--- /dev/null
+++ b/web-extensions/e-html-editor-page-dialog-dom-functions.h
@@ -0,0 +1,38 @@
+/*
+ * e-html-editor-page-dialog-dom-functions.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_HTML_EDITOR_PAGE_DIALOG_DOM_FUNCTIONS_H
+#define E_HTML_EDITOR_PAGE_DIALOG_DOM_FUNCTIONS_H
+
+#include <webkitdom/webkitdom.h>
+
+#include "e-html-editor-web-extension.h"
+
+G_BEGIN_DECLS
+
+void           e_html_editor_page_dialog_save_history
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
+void           e_html_editor_page_dialog_save_history_on_exit
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_PAGE_DIALOG_DOM_FUNCTIONS_H */
diff --git a/web-extensions/e-html-editor-selection-dom-functions.c 
b/web-extensions/e-html-editor-selection-dom-functions.c
index a170e8b..8ae732e 100644
--- a/web-extensions/e-html-editor-selection-dom-functions.c
+++ b/web-extensions/e-html-editor-selection-dom-functions.c
@@ -344,16 +344,31 @@ dom_insert_base64_image (WebKitDOMDocument *document,
                          const gchar *uri,
                          const gchar *base64_content)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        WebKitDOMElement *element, *selection_start_marker, *resizable_wrapper;
        WebKitDOMText *text;
 
        if (!dom_selection_is_collapsed (document))
-               dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+               dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
 
        dom_selection_save (document);
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_IMAGE;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+       }
+
        resizable_wrapper =
                webkit_dom_document_create_element (document, "span", NULL);
        webkit_dom_element_set_attribute (
@@ -394,6 +409,30 @@ dom_insert_base64_image (WebKitDOMDocument *document,
                WEBKIT_DOM_NODE (selection_start_marker),
                NULL);
 
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (resizable_wrapper), TRUE),
+                       NULL);
+
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
        dom_force_spell_check_for_current_paragraph (document, extension);
 }
@@ -406,8 +445,10 @@ dom_insert_base64_image (WebKitDOMDocument *document,
  * cursor position.
  */
 void
-dom_unlink (WebKitDOMDocument *document)
+dom_selection_unlink (WebKitDOMDocument *document,
+                      EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorUndoRedoManager *manager;
        gchar *text;
        WebKitDOMDOMWindow *window;
        WebKitDOMDOMSelection *selection_dom;
@@ -433,12 +474,37 @@ dom_unlink (WebKitDOMDocument *document)
                        } else
                                link = WEBKIT_DOM_ELEMENT (node);
        } else {
-               dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_UNLINK, NULL);
+               dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_UNLINK, NULL);
        }
 
        if (!link)
                return;
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+               WebKitDOMDocumentFragment *fragment;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_REMOVE_LINK;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (link), TRUE),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        text = webkit_dom_html_element_get_inner_text (
                WEBKIT_DOM_HTML_ELEMENT (link));
        webkit_dom_html_element_set_outer_html (
@@ -455,11 +521,12 @@ dom_unlink (WebKitDOMDocument *document)
  */
 void
 dom_create_link (WebKitDOMDocument *document,
+                 EHTMLEditorWebExtension *extension,
                  const gchar *uri)
 {
        g_return_if_fail (uri != NULL && *uri != '\0');
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, uri);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_CREATE_LINK, uri);
 }
 
 /**
@@ -677,27 +744,6 @@ create_list_element (WebKitDOMDocument *document,
        return list;
 }
 
-static WebKitDOMNode *
-get_parent_block_node_from_child (WebKitDOMNode *node)
-{
-       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
-
-       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-temp-text-wrapper") ||
-           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted") ||
-           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quote-character") ||
-           element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-signature") ||
-           WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT (parent) ||
-           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "b") ||
-           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "i") ||
-           element_has_tag (WEBKIT_DOM_ELEMENT (parent), "u"))
-               parent = webkit_dom_node_get_parent_node (parent);
-
-       if (element_has_class (WEBKIT_DOM_ELEMENT (parent), "-x-evo-quoted"))
-               parent = webkit_dom_node_get_parent_node (parent);
-
-       return parent;
-}
-
 static void
 merge_list_into_list (WebKitDOMNode *from,
                       WebKitDOMNode *to,
@@ -936,9 +982,9 @@ dom_get_alignment_from_node (WebKitDOMNode *node)
        return alignment;
 }
 
-static WebKitDOMElement *
-create_selection_marker (WebKitDOMDocument *document,
-                         gboolean start)
+WebKitDOMElement *
+dom_create_selection_marker (WebKitDOMDocument *document,
+                             gboolean selection_start_marker)
 {
        WebKitDOMElement *element;
 
@@ -946,7 +992,8 @@ create_selection_marker (WebKitDOMDocument *document,
                document, "SPAN", NULL);
        webkit_dom_element_set_id (
                element,
-               start ? "-x-evo-selection-start-marker" :
+               selection_start_marker ?
+                       "-x-evo-selection-start-marker" :
                        "-x-evo-selection-end-marker");
 
        return element;
@@ -967,16 +1014,16 @@ remove_selection_markers (WebKitDOMDocument *document)
                remove_node (WEBKIT_DOM_NODE (marker));
 }
 
-static void
-add_selection_markers_into_element_start (WebKitDOMDocument *document,
-                                          WebKitDOMElement *element,
-                                          WebKitDOMElement **selection_start_marker,
-                                          WebKitDOMElement **selection_end_marker)
+void
+dom_add_selection_markers_into_element_start (WebKitDOMDocument *document,
+                                              WebKitDOMElement *element,
+                                              WebKitDOMElement **selection_start_marker,
+                                              WebKitDOMElement **selection_end_marker)
 {
        WebKitDOMElement *marker;
 
        remove_selection_markers (document);
-       marker = create_selection_marker (document, FALSE);
+       marker = dom_create_selection_marker (document, FALSE);
        webkit_dom_node_insert_before (
                WEBKIT_DOM_NODE (element),
                WEBKIT_DOM_NODE (marker),
@@ -985,7 +1032,7 @@ add_selection_markers_into_element_start (WebKitDOMDocument *document,
        if (selection_end_marker)
                *selection_end_marker = marker;
 
-       marker = create_selection_marker (document, TRUE);
+       marker = dom_create_selection_marker (document, TRUE);
        webkit_dom_node_insert_before (
                WEBKIT_DOM_NODE (element),
                WEBKIT_DOM_NODE (marker),
@@ -995,6 +1042,28 @@ add_selection_markers_into_element_start (WebKitDOMDocument *document,
                *selection_start_marker = marker;
 }
 
+void
+dom_add_selection_markers_into_element_end (WebKitDOMDocument *document,
+                                            WebKitDOMElement *element,
+                                            WebKitDOMElement **selection_start_marker,
+                                            WebKitDOMElement **selection_end_marker)
+{
+       WebKitDOMElement *marker;
+
+       remove_selection_markers (document);
+       marker = dom_create_selection_marker (document, TRUE);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
+       if (selection_start_marker)
+               *selection_start_marker = marker;
+
+       marker = dom_create_selection_marker (document, FALSE);
+       webkit_dom_node_append_child (
+               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
+       if (selection_end_marker)
+               *selection_end_marker = marker;
+}
+
 /**
  * e_html_editor_selection_indent:
  * @selection: an #EHTMLEditorSelection
@@ -1005,12 +1074,30 @@ void
 dom_selection_indent (WebKitDOMDocument *document,
                       EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        gboolean after_selection_start = FALSE, after_selection_end = FALSE;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
        WebKitDOMNode *block;
 
        dom_selection_save (document);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
+       }
+
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
 
@@ -1025,7 +1112,7 @@ dom_selection_indent (WebKitDOMDocument *document,
                body = webkit_dom_document_get_body (document);
                child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
 
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
@@ -1124,6 +1211,16 @@ dom_selection_indent (WebKitDOMDocument *document,
                        block = next_block;
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
        dom_force_spell_check_for_current_paragraph (document, extension);
 
@@ -1308,6 +1405,8 @@ void
 dom_selection_unindent (WebKitDOMDocument *document,
                         EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        gboolean after_selection_start = FALSE, after_selection_end = FALSE;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
        WebKitDOMNode *block;
@@ -1322,30 +1421,29 @@ dom_selection_unindent (WebKitDOMDocument *document,
        /* If the selection was not saved, move it into the first child of body */
        if (!selection_start_marker || !selection_end_marker) {
                WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
 
                body = webkit_dom_document_get_body (document);
-               selection_start_marker = webkit_dom_document_create_element (
-                       document, "SPAN", NULL);
-               webkit_dom_element_set_id (
-                       selection_start_marker, "-x-evo-selection-start-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
-                       WEBKIT_DOM_NODE (selection_start_marker),
-                       webkit_dom_node_get_first_child (
-                               webkit_dom_node_get_first_child (
-                                       WEBKIT_DOM_NODE (body))),
-                       NULL);
-               selection_end_marker = webkit_dom_document_create_element (
-                       document, "SPAN", NULL);
-               webkit_dom_element_set_id (
-                       selection_end_marker, "-x-evo-selection-end-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
-                       WEBKIT_DOM_NODE (selection_end_marker),
-                       webkit_dom_node_get_first_child (
-                               webkit_dom_node_get_first_child (
-                                       WEBKIT_DOM_NODE (body))),
-                       NULL);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_INDENT;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
        }
 
        block = get_parent_indented_block (
@@ -1419,6 +1517,16 @@ dom_selection_unindent (WebKitDOMDocument *document,
                block = next_block;
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
 
        dom_force_spell_check_for_current_paragraph (document, extension);
@@ -1497,7 +1605,7 @@ dom_selection_save (WebKitDOMDocument *document)
                return;
 
        collapsed = webkit_dom_range_get_collapsed (range, NULL);
-       start_marker = create_selection_marker (document, TRUE);
+       start_marker = dom_create_selection_marker (document, TRUE);
 
        container = webkit_dom_range_get_start_container (range, NULL);
        offset = webkit_dom_range_get_start_offset (range, NULL);
@@ -1517,6 +1625,16 @@ dom_selection_save (WebKitDOMDocument *document)
                                NULL);
                        goto insert_end_marker;
                }
+       } else if (element_has_class (WEBKIT_DOM_ELEMENT (parent_node), "-x-evo-smiley-text")) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_parent_node (parent_node);
+               marker_node = webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (node),
+                       WEBKIT_DOM_NODE (start_marker),
+                       webkit_dom_node_get_next_sibling (node),
+                       NULL);
+               goto insert_end_marker;
        }
 
        if (WEBKIT_DOM_IS_TEXT (container)) {
@@ -1617,7 +1735,7 @@ dom_selection_save (WebKitDOMDocument *document)
        webkit_dom_node_normalize (parent_node);
 
  insert_end_marker:
-       end_marker = create_selection_marker (document, FALSE);
+       end_marker = dom_create_selection_marker (document, FALSE);
 
        if (collapsed) {
                webkit_dom_node_insert_before (
@@ -2406,7 +2524,7 @@ wrap_lines (WebKitDOMDocument *document,
                /* Get HTML code of the processed content */
                html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (element));
 
-               /* Overwrite the current selection be the processed content */
+               /* Overwrite the current selection by the processed content */
                dom_insert_html (document, extension, html);
 
                g_free (html);
@@ -2527,6 +2645,35 @@ dom_put_node_into_paragraph (WebKitDOMDocument *document,
        return container;
 }
 
+static gint
+get_citation_level (WebKitDOMNode *node)
+{
+       WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
+       gint level = 0;
+
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               if (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
+                   webkit_dom_element_has_attribute (WEBKIT_DOM_ELEMENT (parent), "type"))
+                       level++;
+
+               parent = webkit_dom_node_get_parent_node (parent);
+       }
+
+       return level;
+}
+
+WebKitDOMElement *
+dom_wrap_paragraph_length (WebKitDOMDocument *document,
+                           EHTMLEditorWebExtension *extension,
+                           WebKitDOMElement *paragraph,
+                           gint length)
+{
+       g_return_val_if_fail (WEBKIT_DOM_IS_ELEMENT (paragraph), NULL);
+       g_return_val_if_fail (length >= MINIMAL_PARAGRAPH_WIDTH, NULL);
+
+       return wrap_lines (document, extension, WEBKIT_DOM_NODE (paragraph), FALSE, length);
+}
+
 /**
  * e_html_editor_selection_wrap_lines:
  * @selection: an #EHTMLEditorSelection
@@ -2534,10 +2681,12 @@ dom_put_node_into_paragraph (WebKitDOMDocument *document,
  * Wraps all lines in current selection to be 71 characters long.
  */
 
-static void
-dom_wrap_lines (WebKitDOMDocument *document,
-                EHTMLEditorWebExtension *extension)
+void
+dom_selection_wrap (WebKitDOMDocument *document,
+                    EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        gboolean after_selection_end = FALSE, html_mode;
        gint word_wrap_length;
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
@@ -2554,30 +2703,32 @@ dom_wrap_lines (WebKitDOMDocument *document,
        /* If the selection was not saved, move it into the first child of body */
        if (!selection_start_marker || !selection_end_marker) {
                WebKitDOMHTMLElement *body;
+               WebKitDOMNode *child;
 
                body = webkit_dom_document_get_body (document);
-               selection_start_marker = webkit_dom_document_create_element (
-                       document, "SPAN", NULL);
-               webkit_dom_element_set_id (
-                       selection_start_marker, "-x-evo-selection-start-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
-                       WEBKIT_DOM_NODE (selection_start_marker),
-                       webkit_dom_node_get_first_child (
-                               webkit_dom_node_get_first_child (
-                                       WEBKIT_DOM_NODE (body))),
-                       NULL);
-               selection_end_marker = webkit_dom_document_create_element (
-                       document, "SPAN", NULL);
-               webkit_dom_element_set_id (
-                       selection_end_marker, "-x-evo-selection-end-marker");
-               webkit_dom_node_insert_before (
-                       webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body)),
-                       WEBKIT_DOM_NODE (selection_end_marker),
-                       webkit_dom_node_get_first_child (
-                               webkit_dom_node_get_first_child (
-                                       WEBKIT_DOM_NODE (body))),
-                       NULL);
+               child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
+
+               dom_add_selection_markers_into_element_start (
+                       document,
+                       WEBKIT_DOM_ELEMENT (child),
+                       &selection_start_marker,
+                       &selection_end_marker);
+       }
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_WRAP;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = 1;
+               ev->data.style.to = 1;
        }
 
        block = get_parent_block_node_from_child (
@@ -2617,6 +2768,16 @@ dom_wrap_lines (WebKitDOMDocument *document,
                block = next_block;
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
 
        dom_force_spell_check_for_current_paragraph (document, extension);
@@ -2845,6 +3006,140 @@ dom_selection_is_underline (WebKitDOMDocument *document,
        return ret_val;
 }
 
+static WebKitDOMElement *
+set_font_style (WebKitDOMDocument *document,
+                const gchar *element_name,
+                gboolean value)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNode *parent;
+
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-end-marker");
+       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element));
+       if (value) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *el;
+               gchar *name;
+
+               el = webkit_dom_document_create_element (document, element_name, NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (el), UNICODE_ZERO_WIDTH_SPACE, NULL);
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), node, NULL);
+               name = webkit_dom_node_get_local_name (parent);
+               if (g_strcmp0 (name, element_name) == 0)
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (parent),
+                               WEBKIT_DOM_NODE (el),
+                               webkit_dom_node_get_next_sibling (parent),
+                               NULL);
+               else
+                       webkit_dom_node_insert_before (
+                               parent,
+                               WEBKIT_DOM_NODE (el),
+                               WEBKIT_DOM_NODE (element),
+                               NULL);
+               g_free (name);
+
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (el), WEBKIT_DOM_NODE (element), NULL);
+
+               return el;
+       } else {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       WEBKIT_DOM_NODE (element),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (parent),
+                       WEBKIT_DOM_NODE (node),
+                       webkit_dom_node_get_next_sibling (parent),
+                       NULL);
+
+               webkit_dom_html_element_insert_adjacent_text (
+                       WEBKIT_DOM_HTML_ELEMENT (parent),
+                       "afterend",
+                       UNICODE_ZERO_WIDTH_SPACE,
+                       NULL);
+       }
+
+       return NULL;
+}
+
+static void
+selection_set_font_style (WebKitDOMDocument *document,
+                          EHTMLEditorWebExtension *extension,
+                          EHTMLEditorViewCommand command,
+                          gboolean value)
+{
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
+
+       dom_selection_save (document);
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
+                       ev->type = HISTORY_BOLD;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
+                       ev->type = HISTORY_ITALIC;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
+                       ev->type = HISTORY_UNDERLINE;
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
+                       ev->type = HISTORY_STRIKETHROUGH;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = !value;
+               ev->data.style.to = value;
+       }
+
+       if (dom_selection_is_collapsed (document)) {
+               const gchar *element_name;
+
+               if (command == E_HTML_EDITOR_VIEW_COMMAND_BOLD)
+                       element_name = "b";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_ITALIC)
+                       element_name = "i";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE)
+                       element_name = "u";
+               else if (command == E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH)
+                       element_name = "strike";
+
+               set_font_style (document, element_name, value);
+               dom_selection_restore (document);
+
+               goto exit;
+       }
+       dom_selection_restore (document);
+
+       dom_exec_command (document, extension, command, NULL);
+exit:
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       dom_force_spell_check_for_current_paragraph (document, extension);
+}
+
 /**
  * e_html_editor_selection_set_underline:
  * @selection: an #EHTMLEditorSelection
@@ -2861,9 +3156,8 @@ dom_selection_set_underline (WebKitDOMDocument *document,
        if (dom_selection_is_underline (document, extension) == underline)
                return;
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, NULL);
-
-       dom_force_spell_check_for_current_paragraph (document, extension);
+       selection_set_font_style (
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_UNDERLINE, underline);
 
        set_dbus_property_boolean (extension, "Underline", underline);
 }
@@ -2920,7 +3214,7 @@ dom_selection_set_subscript (WebKitDOMDocument *document,
        if (dom_selection_is_subscript (document, extension) == subscript)
                return;
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, NULL);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_SUBSCRIPT, NULL);
 
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "subscript");
@@ -2979,7 +3273,7 @@ dom_selection_set_superscript (WebKitDOMDocument *document,
        if (dom_selection_is_superscript (document, extension) == superscript)
                return;
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, NULL);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_SUPERSCRIPT, NULL);
 
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "superscript");
@@ -3057,8 +3351,8 @@ dom_selection_set_strikethrough (WebKitDOMDocument *document,
 /* FIXME WK2
        selection->priv->is_strikethrough = strikethrough;
 */
-
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, NULL);
+       selection_set_font_style (
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_STRIKETHROUGH, strikethrough);
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "strikethrough");
 */
@@ -3154,6 +3448,8 @@ dom_selection_set_monospaced (WebKitDOMDocument *document,
                               EHTMLEditorWebExtension *extension,
                               gboolean monospaced)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        guint font_size = 0;
        WebKitDOMRange *range;
        WebKitDOMDOMWindow *window;
@@ -3168,6 +3464,22 @@ dom_selection_set_monospaced (WebKitDOMDocument *document,
        if (!range)
                return;
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_MONOSPACE;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = !monospaced;
+               ev->data.style.to = monospaced;
+       }
+
        font_size = e_html_editor_web_extension_get_font_size (extension);
        if (font_size == 0)
                font_size = E_HTML_EDITOR_SELECTION_FONT_SIZE_NORMAL;
@@ -3197,13 +3509,13 @@ dom_selection_set_monospaced (WebKitDOMDocument *document,
 
                        webkit_dom_node_insert_before (
                                WEBKIT_DOM_NODE (monospace),
-                               WEBKIT_DOM_NODE (create_selection_marker (document, TRUE)),
+                               WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)),
                                webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (monospace)),
                                NULL);
 
                        webkit_dom_node_append_child (
                                WEBKIT_DOM_NODE (monospace),
-                               WEBKIT_DOM_NODE (create_selection_marker (document, FALSE)),
+                               WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)),
                                NULL);
 
                        dom_selection_restore (document);
@@ -3361,6 +3673,16 @@ dom_selection_set_monospaced (WebKitDOMDocument *document,
                        dom_selection_set_font_size (document, extension, font_size);
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_force_spell_check_for_current_paragraph (document, extension);
 
 /* FIXME WK2
@@ -3439,7 +3761,8 @@ dom_selection_set_bold (WebKitDOMDocument *document,
 /* FIXME WK2
        selection->priv->is_bold = bold; */
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_BOLD, NULL);
+       selection_set_font_style (
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_BOLD, bold);
 
        dom_force_spell_check_for_current_paragraph (document, extension);
 /* FIXME WK2
@@ -3518,9 +3841,8 @@ dom_selection_set_italic (WebKitDOMDocument *document,
 /* FIXME WK2
        selection->priv->is_italic = italic;*/
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_ITALIC, NULL);
-
-       dom_force_spell_check_for_current_paragraph (document, extension);
+       selection_set_font_style (
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_ITALIC, italic);
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "italic");*/
 }
@@ -3682,13 +4004,50 @@ dom_selection_set_font_size (WebKitDOMDocument *document,
                              EHTMLEditorWebExtension *extension,
                              guint font_size)
 {
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
        gchar *size_str;
+       guint current_font_size;
+
+       current_font_size = dom_selection_get_font_size (document, extension);
+       if (current_font_size == font_size)
+               return;
+
+       dom_selection_save (document);
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_FONT_SIZE;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.style.from = current_font_size;
+               ev->data.style.to = font_size;
+       }
+
 /* FIXME WK2
        selection->priv->font_size = font_size; */
-
        size_str = g_strdup_printf ("%d", font_size);
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, size_str);
-       g_free (size_str);
+
+       if (dom_selection_is_collapsed (document)) {
+               WebKitDOMElement *font;
+
+               font = set_font_style (document, "font", font_size != 3);
+               if (font)
+                       webkit_dom_element_set_attribute (font, "size", size_str, NULL);
+               dom_selection_restore (document);
+               goto exit;
+       }
+
+       dom_selection_restore (document);
+
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_FONT_SIZE, size_str);
 
        /* Text in <font size="3"></font> (size 3 is our default size) is a little
         * bit smaller than font outsize it. So move it outside of it. */
@@ -3710,6 +4069,19 @@ dom_selection_set_font_size (WebKitDOMDocument *document,
                }
        }
 
+ exit:
+       g_free (size_str);
+
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "font-size"); */
 }
@@ -3727,7 +4099,7 @@ dom_selection_set_font_name (WebKitDOMDocument *document,
                              EHTMLEditorWebExtension *extension,
                              const gchar *font_name)
 {
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, font_name);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_FONT_NAME, font_name);
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "font-name");*/
 }
@@ -3775,10 +4147,39 @@ dom_selection_set_font_color (WebKitDOMDocument *document,
                               EHTMLEditorWebExtension *extension,
                               const gchar *color)
 {
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_FONT_COLOR;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+/* FIXME WK2
+               ev->data.string.from = g_strdup (selection->priv->font_color);*/
+               ev->data.string.to = g_strdup (color);
+       }
+
 /* FIXME WK2
        selection->priv->font_color = g_strdup (color); */
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, color);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR, color);
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "font-color"); */
 }
@@ -3893,26 +4294,6 @@ dom_selection_get_block_format (WebKitDOMDocument *document,
 }
 
 static gboolean
-is_citation_node (WebKitDOMNode *node)
-{
-       char *value;
-
-       if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
-               return FALSE;
-
-       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
-
-       /* citation == <blockquote type='cite'> */
-       if (g_strcmp0 (value, "cite") == 0) {
-               g_free (value);
-               return TRUE;
-       } else {
-               g_free (value);
-               return FALSE;
-       }
-}
-
-static gboolean
 process_block_to_block (WebKitDOMDocument *document,
                         EHTMLEditorWebExtension *extension,
                         EHTMLEditorSelectionBlockFormat format,
@@ -3931,7 +4312,7 @@ process_block_to_block (WebKitDOMDocument *document,
                WebKitDOMNode *child;
                WebKitDOMElement *element;
 
-               if (is_citation_node (block)) {
+               if (dom_node_is_citation_node (block)) {
                        gboolean finished;
 
                        next_block = webkit_dom_node_get_next_sibling (block);
@@ -4063,7 +4444,7 @@ format_change_block_to_block (WebKitDOMDocument *document,
                body = webkit_dom_document_get_body (document);
                child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
 
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
@@ -4112,7 +4493,7 @@ format_change_block_to_list (WebKitDOMDocument *document,
                body = webkit_dom_document_get_body (document);
                child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
 
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
@@ -4139,7 +4520,7 @@ format_change_block_to_list (WebKitDOMDocument *document,
                dom_restore_caret_position (document);
 
                dom_exec_command (
-                       document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+                       document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, 
NULL);
 
                element = webkit_dom_document_query_selector (
                        document, "body>br", NULL);
@@ -4490,6 +4871,8 @@ dom_selection_set_block_format (WebKitDOMDocument *document,
                                 EHTMLEditorSelectionBlockFormat format)
 {
        EHTMLEditorSelectionBlockFormat current_format;
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
        const gchar *value;
        gboolean from_list = FALSE, to_list = FALSE, html_mode = FALSE;
        WebKitDOMRange *range;
@@ -4560,6 +4943,21 @@ dom_selection_set_block_format (WebKitDOMDocument *document,
        if (!range)
                return;
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_BLOCK_FORMAT;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_format;
+               ev->data.style.to = format;
+        }
+
        if (from_list && to_list)
                format_change_list_to_list (document, extension, format, html_mode);
 
@@ -4579,6 +4977,16 @@ dom_selection_set_block_format (WebKitDOMDocument *document,
                document, extension, e_html_editor_web_extension_get_alignment (extension));
 
        e_html_editor_web_extension_set_content_changed (extension);
+
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
 /*
        g_object_notify (G_OBJECT (selection), "block-format");*/
 }
@@ -4627,7 +5035,7 @@ dom_selection_set_background_color (WebKitDOMDocument *document,
                                     EHTMLEditorWebExtension *extension,
                                     const gchar *color)
 {
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, color);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_BACKGROUND_COLOR, color);
 /* FIXME WK2
        g_object_notify (G_OBJECT (selection), "background-color");*/
 }
@@ -4715,12 +5123,17 @@ dom_selection_set_alignment (WebKitDOMDocument *document,
                              EHTMLEditorWebExtension *extension,
                              EHTMLEditorSelectionAlignment alignment)
 {
+       EHTMLEditorSelectionAlignment current_alignment;
+       EHTMLEditorUndoRedoManager *manager;
+       EHTMLEditorHistoryEvent *ev = NULL;
        gboolean after_selection_end = FALSE;
        const gchar *class = "", *list_class = "";
        WebKitDOMElement *selection_start_marker, *selection_end_marker;
        WebKitDOMNode *block;
 
-       if (dom_selection_get_alignment (document, extension) == alignment)
+       current_alignment = dom_selection_get_alignment (document, extension);
+
+       if (current_alignment == alignment)
                return;
 
        switch (alignment) {
@@ -4742,6 +5155,21 @@ dom_selection_set_alignment (WebKitDOMDocument *document,
 
        dom_selection_save (document);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_ALIGNMENT;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+               ev->data.style.from = current_alignment;
+               ev->data.style.to = alignment;
+        }
+
        selection_start_marker = webkit_dom_document_query_selector (
                document, "span#-x-evo-selection-start-marker", NULL);
        selection_end_marker = webkit_dom_document_query_selector (
@@ -4811,6 +5239,16 @@ dom_selection_set_alignment (WebKitDOMDocument *document,
                block = next_block;
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
 
        dom_force_spell_check_for_current_paragraph (document, extension);
@@ -4831,8 +5269,50 @@ dom_selection_replace (WebKitDOMDocument *document,
                        EHTMLEditorWebExtension *extension,
                        const gchar *replacement)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               WebKitDOMDOMWindow *window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMRange *range;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_REPLACE;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (window);
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               ev->data.string.from = webkit_dom_range_get_text (range);
+               ev->data.string.to = g_strdup (replacement);
+       }
+
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, replacement);
+
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       dom_force_spell_check_for_current_paragraph (document, extension);
+
        e_html_editor_web_extension_set_content_changed (extension);
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, replacement);
 }
 
 /**
@@ -4859,7 +5339,7 @@ dom_replace_caret_word (WebKitDOMDocument *document,
        webkit_dom_range_expand (range, "word", NULL);
        webkit_dom_dom_selection_add_range (dom_selection, range);
 
-       dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, replacement);
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, replacement);
        dom_force_spell_check_for_current_paragraph (document, extension);
 }
 
@@ -4964,7 +5444,7 @@ dom_prepare_paragraph (WebKitDOMDocument *document,
        paragraph = dom_get_paragraph_element (document, extension, -1, 0);
 
        if (with_selection)
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document, paragraph, NULL, NULL);
 
        element = webkit_dom_document_create_element (document, "BR", NULL);
@@ -4991,3 +5471,74 @@ dom_selection_set_on_point (WebKitDOMDocument *document,
        webkit_dom_dom_selection_remove_all_ranges (dom_selection);
        webkit_dom_dom_selection_add_range (dom_selection, range);
 }
+
+void
+dom_selection_get_coordinates (WebKitDOMDocument *document,
+                               guint *start_x,
+                               guint *start_y,
+                               guint *end_x,
+                               guint *end_y)
+{
+       gboolean created_selection_markers = FALSE;
+       guint local_x = 0, local_y = 0;
+       WebKitDOMElement *element, *parent;
+
+       g_return_if_fail (start_x != NULL);
+       g_return_if_fail (start_y != NULL);
+       g_return_if_fail (end_x != NULL);
+       g_return_if_fail (end_y != NULL);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       if (!element) {
+               created_selection_markers = TRUE;
+               dom_selection_save (document);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!element)
+                       return;
+       }
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (start_x)
+               *start_x = local_x;
+       if (start_y)
+               *start_y = local_y;
+
+       if (dom_selection_is_collapsed (document)) {
+               *end_x = local_x;
+               *end_y = local_y;
+
+               if (created_selection_markers)
+                       dom_selection_restore (document);
+
+               return;
+       }
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       local_x = 0;
+       local_y = 0;
+
+       parent = element;
+       while (parent && !WEBKIT_DOM_IS_HTML_BODY_ELEMENT (parent)) {
+               local_x += (guint) webkit_dom_element_get_offset_left (parent);
+               local_y += (guint) webkit_dom_element_get_offset_top (parent);
+               parent = webkit_dom_element_get_offset_parent (parent);
+       }
+
+       if (end_x)
+               *end_x = local_x;
+       if (end_y)
+               *end_y = local_y;
+
+       if (created_selection_markers)
+               dom_selection_restore (document);
+}
diff --git a/web-extensions/e-html-editor-selection-dom-functions.h 
b/web-extensions/e-html-editor-selection-dom-functions.h
index 19049b7..7de382d 100644
--- a/web-extensions/e-html-editor-selection-dom-functions.h
+++ b/web-extensions/e-html-editor-selection-dom-functions.h
@@ -84,9 +84,11 @@ void         dom_insert_base64_image         (WebKitDOMDocument *document,
                                                 const gchar *uri,
                                                 const gchar *base64_content);
 
-void           dom_unlink                      (WebKitDOMDocument *document);
+void           dom_selection_unlink            (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension);
 
 void           dom_create_link                 (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
                                                 const gchar *uri);
 
 EHTMLEditorSelectionBlockFormat
@@ -119,6 +121,22 @@ void               dom_set_paragraph_style         (WebKitDOMDocument *document,
                                                 const gchar *style_to_add);
 
 WebKitDOMElement *
+               dom_create_selection_marker     (WebKitDOMDocument *document,
+                                                gboolean selection_start_marker);
+
+void           dom_add_selection_markers_into_element_start
+                                               (WebKitDOMDocument *document,
+                                                WebKitDOMElement *element,
+                                                WebKitDOMElement **selection_start_marker,
+                                                WebKitDOMElement **selection_end_marker);
+
+void           dom_add_selection_markers_into_element_end
+                                               (WebKitDOMDocument *document,
+                                                WebKitDOMElement *element,
+                                                WebKitDOMElement **selection_start_marker,
+                                                WebKitDOMElement **selection_end_marker);
+
+WebKitDOMElement *
                dom_get_paragraph_element       (WebKitDOMDocument *document,
                                                 EHTMLEditorWebExtension *extension,
                                                 gint width,
@@ -130,6 +148,9 @@ WebKitDOMElement *
                                                 WebKitDOMNode *node,
                                                 WebKitDOMNode *caret_position);
 
+void           dom_selection_wrap              (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
 WebKitDOMElement *
                dom_wrap_paragraph_length       (WebKitDOMDocument *document,
                                                 EHTMLEditorWebExtension *extension,
@@ -268,6 +289,11 @@ void               dom_selection_set_on_point      (WebKitDOMDocument *document,
                                                 guint x,
                                                 guint y);
 
+void           dom_selection_get_coordinates   (WebKitDOMDocument *document,
+                                                guint *start_x,
+                                                guint *start_y,
+                                                guint *end_x,
+                                                guint *end_y);
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_SELECTION_DOM_FUNCTIONS_H */
diff --git a/web-extensions/e-html-editor-table-dialog-dom-functions.c 
b/web-extensions/e-html-editor-table-dialog-dom-functions.c
index 64ab7db..e2706db 100644
--- a/web-extensions/e-html-editor-table-dialog-dom-functions.c
+++ b/web-extensions/e-html-editor-table-dialog-dom-functions.c
@@ -149,7 +149,7 @@ e_html_editor_table_dialog_get_column_count (WebKitDOMDocument *document)
        return count;
 }
 
-static void
+static WebKitDOMElement *
 create_table (WebKitDOMDocument *document,
               EHTMLEditorWebExtension *extension)
 {
@@ -226,19 +226,23 @@ create_table (WebKitDOMDocument *document,
        dom_selection_restore (document);
 
        e_html_editor_web_extension_set_content_changed (extension);
+
+       return table;
 }
 
 gboolean
 e_html_editor_table_dialog_show (WebKitDOMDocument *document,
                                  EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorUndoRedoManager *manager;
+       gboolean created = FALSE;
        WebKitDOMDOMWindow *window;
        WebKitDOMDOMSelection *selection;
+       WebKitDOMElement *table = NULL;
 
        window = webkit_dom_document_get_default_view (document);
        selection = webkit_dom_dom_window_get_selection (window);
        if (selection && (webkit_dom_dom_selection_get_range_count (selection) > 0)) {
-               WebKitDOMElement *table;
                WebKitDOMRange *range;
 
                range = webkit_dom_dom_selection_get_range_at (selection, 0, NULL);
@@ -247,12 +251,51 @@ e_html_editor_table_dialog_show (WebKitDOMDocument *document,
 
                if (table) {
                        webkit_dom_element_set_id (table, "-x-evo-current-table");
-                       return FALSE;
                } else {
-                       create_table (document, extension);
-                       return TRUE;
+                       table = create_table (document, extension);
+                       created = TRUE;
                }
        }
 
-       return FALSE;
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               EHTMLEditorHistoryEvent *ev;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_TABLE_DIALOG;
+
+               dom_selection_get_coordinates (
+                       document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, 
&ev->before.end.y);
+               if (!created)
+                       ev->data.dom.from = webkit_dom_node_clone_node (
+                               WEBKIT_DOM_NODE (table), TRUE);
+               else
+                       ev->data.dom.from = NULL;
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       return created;
+}
+
+void
+e_html_editor_table_dialog_save_history_on_exit (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
+       WebKitDOMElement *element;
+
+       element = WEBKIT_DOM_ELEMENT (get_current_table_element (document));
+       g_return_if_fail (element != NULL);
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+       ev->data.dom.to = webkit_dom_node_clone_node (
+               WEBKIT_DOM_NODE (element), TRUE);
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
 }
diff --git a/web-extensions/e-html-editor-table-dialog-dom-functions.h 
b/web-extensions/e-html-editor-table-dialog-dom-functions.h
index fcdf392..40198b6 100644
--- a/web-extensions/e-html-editor-table-dialog-dom-functions.h
+++ b/web-extensions/e-html-editor-table-dialog-dom-functions.h
@@ -41,6 +41,11 @@ gulong               e_html_editor_table_dialog_get_column_count
 
 gboolean       e_html_editor_table_dialog_show (WebKitDOMDocument *document,
                                                 EHTMLEditorWebExtension *extension);
+
+void           e_html_editor_table_dialog_save_history_on_exit
+                                               (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension);
+
 G_END_DECLS
 
 #endif /* E_HTML_EDITOR_TABLE_DIALOG_DOM_FUNCTIONS_H */
diff --git a/web-extensions/e-html-editor-undo-redo-manager.c 
b/web-extensions/e-html-editor-undo-redo-manager.c
new file mode 100644
index 0000000..f4f51fd
--- /dev/null
+++ b/web-extensions/e-html-editor-undo-redo-manager.c
@@ -0,0 +1,1867 @@
+/*
+ * e-html-editor-undo-redo-manager.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "config.h"
+
+#include "e-html-editor-undo-redo-manager.h"
+#include "e-html-editor-web-extension.h"
+#include "e-html-editor-selection-dom-functions.h"
+#include "e-html-editor-view-dom-functions.h"
+#include "e-dom-utils.h"
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMDocumentFragmentUnstable.h>
+#include <webkitdom/WebKitDOMRangeUnstable.h>
+#include <webkitdom/WebKitDOMDOMSelection.h>
+#include <webkitdom/WebKitDOMDOMWindowUnstable.h>
+#include <webkitdom/WebKitDOMHTMLElementUnstable.h>
+#include <webkitdom/WebKitDOMDocumentUnstable.h>
+
+#define E_HTML_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER, EHTMLEditorUndoRedoManagerPrivate))
+
+struct _EHTMLEditorUndoRedoManagerPrivate {
+       WebKitDOMDocument *document;
+       GWeakRef extension;
+
+       gboolean can_undo;
+       gboolean can_redo;
+       gboolean operation_in_progress;
+
+       GList *history;
+       guint history_size;
+};
+
+enum {
+       PROP_0,
+       PROP_CAN_REDO,
+       PROP_CAN_UNDO,
+       PROP_HTML_EDITOR_WEB_EXTENSION
+};
+
+#define HISTORY_SIZE_LIMIT 30
+
+#define d(x)
+
+G_DEFINE_TYPE (
+       EHTMLEditorUndoRedoManager,
+       e_html_editor_undo_redo_manager,
+       G_TYPE_OBJECT
+);
+
+void
+e_html_editor_undo_redo_manager_set_document (EHTMLEditorUndoRedoManager *manager,
+                                              WebKitDOMDocument *document)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       manager->priv->document = document;
+
+       /* The history is not valid if document is changed. */
+       e_html_editor_undo_redo_manager_clean_history (manager);
+}
+
+static EHTMLEditorWebExtension *
+html_editor_undo_redo_manager_ref_extension (EHTMLEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager), NULL);
+
+       return g_weak_ref_get (&manager->priv->extension);
+}
+
+static WebKitDOMRange *
+get_range_for_point (WebKitDOMDocument *document,
+                     EHTMLEditorSelectionPoint point)
+{
+       glong scroll_left, scroll_top;
+       WebKitDOMHTMLElement *body;
+       WebKitDOMRange *range;
+
+       body = webkit_dom_document_get_body (document);
+       scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+       scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+
+       range = webkit_dom_document_caret_range_from_point (
+               document, point.x - scroll_left, point.y - scroll_top);
+
+       /* The point is outside the viewport, scroll to it. */
+       if (!range) {
+               WebKitDOMDOMWindow *window;
+
+               window = webkit_dom_document_get_default_view (document);
+               webkit_dom_dom_window_scroll_to (window, point.x, point.y);
+
+               scroll_left = webkit_dom_element_get_scroll_left (WEBKIT_DOM_ELEMENT (body));
+               scroll_top = webkit_dom_element_get_scroll_top (WEBKIT_DOM_ELEMENT (body));
+               range = webkit_dom_document_caret_range_from_point (
+                       document, point.x - scroll_left, point.y - scroll_top);
+       }
+
+       return range;
+}
+
+static void
+restore_selection_to_history_event_state (WebKitDOMDocument *document,
+                                          EHTMLEditorSelection selection_state)
+{
+       gboolean was_collapsed = FALSE;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMElement *element, *tmp;
+       WebKitDOMRange *range;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       /* Restore the selection how it was before the event occured. */
+       range = get_range_for_point (document, selection_state.start);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       was_collapsed = selection_state.start.x == selection_state.end.x;
+       was_collapsed = was_collapsed && selection_state.start.y == selection_state.end.y;
+       if (was_collapsed)
+               return;
+
+       dom_selection_save (document);
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-end-marker");
+
+       remove_node (WEBKIT_DOM_NODE (element));
+
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       webkit_dom_element_remove_attribute (element, "id");
+
+       range = get_range_for_point (document, selection_state.end);
+       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+       dom_selection_save (document);
+
+       tmp = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       remove_node (WEBKIT_DOM_NODE (tmp));
+
+       webkit_dom_element_set_id (
+               element, "-x-evo-selection-start-marker");
+
+       dom_selection_restore (document);
+}
+
+static void
+undo_delete (WebKitDOMDocument *document,
+             EHTMLEditorWebExtension *extension,
+             EHTMLEditorHistoryEvent *event)
+{
+       gboolean empty, single_block;
+       gchar *content;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+       WebKitDOMElement *element;
+       WebKitDOMNode *first_child, *fragment;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       fragment = webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment),  TRUE);
+       first_child = webkit_dom_node_get_first_child (fragment);
+
+       content = webkit_dom_node_get_text_content (fragment);
+       empty = content && !*content;
+       g_free (content);
+
+       /* Tabulator */
+       single_block = event->type == HISTORY_INPUT;
+       single_block = single_block && event->before.start.x != 0 && event->before.end.y != 0;
+
+       if (!single_block) {
+               /* One block delete */
+               if ((single_block = WEBKIT_DOM_IS_ELEMENT (first_child)))
+                       single_block = element_has_id (WEBKIT_DOM_ELEMENT (first_child), 
"-x-evo-selection-start-marker");
+               else
+                       single_block = WEBKIT_DOM_IS_TEXT (first_child);
+       }
+
+       /* Redoing Return key press */
+       if (empty) {
+               WebKitDOMNode *node;
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               node = webkit_dom_range_get_start_container (range, NULL);
+               if (!node)
+                       return;
+
+               element = get_parent_block_element (node);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       fragment,
+                       webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)),
+                       NULL);
+
+               dom_selection_restore (document);
+               dom_force_spell_check (document, extension);
+
+               return;
+       }
+
+       /* Multi block delete */
+       if (WEBKIT_DOM_IS_ELEMENT (first_child) && !single_block) {
+               WebKitDOMNode *node, *parent, *last_child;
+               WebKitDOMNode *parent_deleted_content;
+               WebKitDOMNode *parent_current_block;
+               WebKitDOMNode *insert_before;
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+               dom_selection_save (document);
+
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Get the last block in deleted content. */
+               last_child = webkit_dom_node_get_last_child (fragment);
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               /* All the nodes that are in current block after the caret position
+                * belongs on the end of the deleted content. */
+               node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               while (node) {
+                       WebKitDOMNode *next_sibling;
+
+                       next_sibling = webkit_dom_node_get_next_sibling (node);
+                       webkit_dom_node_append_child (last_child, node, NULL);
+                       node = next_sibling;
+               }
+
+               /* Get the first block in deleted content. */
+               while (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (first_child))
+                       first_child = webkit_dom_node_get_first_child (first_child);
+
+               /* All the nodes that are in the first block of the deleted content
+                * belongs to the current block right after the caret position. */
+               parent = get_parent_block_node_from_child (WEBKIT_DOM_NODE (element));
+               while ((node = webkit_dom_node_get_first_child (first_child)))
+                       webkit_dom_node_append_child (WEBKIT_DOM_NODE (parent), node, NULL);
+
+               parent_deleted_content = webkit_dom_node_get_parent_node (first_child);
+               parent_current_block = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent));
+               insert_before = webkit_dom_node_get_next_sibling (parent);
+
+               /* Remove the first block from deleted content as its content was already
+                * moved to the right place. */
+               remove_node (first_child);
+
+               /* Move the deleted content back to the body. Start from the next sibling
+                * of the first block (if presented) where the delete occured. */
+               while (parent_deleted_content) {
+                       WebKitDOMNode *tmp, *sibling;
+
+                       /* Move all the siblings from current level back to the body. */
+                       sibling = webkit_dom_node_get_first_child (parent_deleted_content);
+                       while (sibling) {
+                               WebKitDOMNode *next_sibling;
+
+                               next_sibling = webkit_dom_node_get_next_sibling (sibling);
+                               webkit_dom_node_insert_before (
+                                       parent_current_block, sibling, insert_before, NULL);
+                               sibling = next_sibling;
+                       }
+                       tmp = webkit_dom_node_get_parent_node (parent_deleted_content);
+                       remove_node (parent_deleted_content);
+                       parent_deleted_content = tmp;
+                       insert_before = webkit_dom_node_get_next_sibling (parent_current_block);
+                       parent_current_block = webkit_dom_node_get_parent_node (parent_current_block);
+               }
+
+               dom_selection_restore (document);
+               dom_force_spell_check (document, extension);
+       } else {
+               WebKitDOMNode *inserted_node, *nd;
+
+               element = webkit_dom_document_create_element (document, "span", NULL);
+
+               range = get_range_for_point (document, event->after.start);
+               /* Create temporary node on the selection where the delete occured. */
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               nd = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+               if (nd && WEBKIT_DOM_IS_TEXT (nd)) {
+                       gchar *text = webkit_dom_character_data_get_data (WEBKIT_DOM_CHARACTER_DATA (nd));
+                       glong length = webkit_dom_character_data_get_length (WEBKIT_DOM_CHARACTER_DATA (nd));
+
+                       /* We have to preserve empty paragraphs with just UNICODE_ZERO_WIDTH_SPACE
+                        * character as when we will remove it it will collapse */
+                       if (length > 1) {
+                               if (g_str_has_prefix (text, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (nd), 0, 1, "", NULL);
+                               else if (g_str_has_suffix (text, UNICODE_ZERO_WIDTH_SPACE))
+                                       webkit_dom_character_data_replace_data (
+                                               WEBKIT_DOM_CHARACTER_DATA (nd), length - 1, 1, "", NULL);
+                       }
+                       g_free (text);
+               }
+
+               /* Insert the deleted content back to the body. */
+               inserted_node = webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       fragment,
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               remove_node (WEBKIT_DOM_NODE (element));
+
+               /* If the selection markers are presented restore the selection,
+                * otherwise the selection was not callapsed so select the deleted
+                * content as it was before the delete occured. */
+               if (webkit_dom_document_fragment_query_selector (event->data.fragment, 
"span#-x-evo-selection-start-marker", NULL)) {
+                       dom_selection_restore (document);
+               } else {
+                       webkit_dom_range_select_node (range, WEBKIT_DOM_NODE (inserted_node), NULL);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+               }
+
+               if (e_html_editor_web_extension_get_magic_smileys_enabled (extension))
+                       dom_check_magic_smileys (document, extension);
+               if (e_html_editor_web_extension_get_magic_links_enabled (extension))
+                       dom_check_magic_links (document, extension, FALSE);
+               dom_force_spell_check_for_current_paragraph (document, extension);
+       }
+}
+
+static void
+redo_delete (WebKitDOMDocument *document,
+             EHTMLEditorWebExtension *extension,
+             EHTMLEditorHistoryEvent *event)
+{
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMDocumentFragment *fragment = event->data.fragment;
+       WebKitDOMNode *first_child;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       first_child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+
+       restore_selection_to_history_event_state (document, event->before);
+
+       if (webkit_dom_document_fragment_query_selector (fragment, "span#-x-evo-selection-start-marker", 
NULL)) {
+               gboolean delete = FALSE;
+
+               /* Check if the event was delete or backspace press. */
+               delete = WEBKIT_DOM_IS_ELEMENT (first_child);
+               delete = delete && element_has_id (WEBKIT_DOM_ELEMENT (first_child), 
"-x-evo-selection-start-marker");
+               if (delete)
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "character");
+               else
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       }
+
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+       dom_force_spell_check_for_current_paragraph (document, extension);
+}
+
+typedef void (*SelectionStyleChangeFunc) (WebKitDOMDocument *document, EHTMLEditorWebExtension *extension, 
gint style);
+
+static void
+undo_redo_style_change (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension,
+                        EHTMLEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       SelectionStyleChangeFunc func;
+
+       switch (event->type) {
+               case HISTORY_ALIGNMENT:
+                       func = (SelectionStyleChangeFunc) dom_selection_set_alignment;
+                       break;
+               case HISTORY_BOLD:
+                       func = dom_selection_set_bold;
+                       break;
+               case HISTORY_BLOCK_FORMAT:
+                       func = (SelectionStyleChangeFunc) dom_selection_set_block_format;
+                       break;
+               case HISTORY_FONT_SIZE:
+                       func = (SelectionStyleChangeFunc) dom_selection_set_font_size;
+                       break;
+               case HISTORY_ITALIC:
+                       func = dom_selection_set_italic;
+                       break;
+               case HISTORY_MONOSPACE:
+                       func = dom_selection_set_monospaced;
+                       break;
+               case HISTORY_STRIKETHROUGH:
+                       func = dom_selection_set_strikethrough;
+                       break;
+               case HISTORY_UNDERLINE:
+                       func = dom_selection_set_underline;
+                       break;
+               default:
+                       return;
+       }
+
+       restore_selection_to_history_event_state (document, undo ? event->after : event->before);
+
+       func (document, extension, undo ? event->data.style.from : event->data.style.to);
+
+       restore_selection_to_history_event_state (document, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_indent (WebKitDOMDocument *document,
+                  EHTMLEditorWebExtension *extension,
+                  EHTMLEditorHistoryEvent *event,
+                  gboolean undo)
+{
+       gboolean was_indent = FALSE;
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       was_indent = event->data.style.from && event->data.style.to;
+
+       if ((undo && was_indent) || (!undo && !was_indent))
+               dom_selection_unindent (document, extension);
+       else
+               dom_selection_indent (document, extension);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_font_color (WebKitDOMDocument *document,
+                      EHTMLEditorWebExtension *extension,
+                      EHTMLEditorHistoryEvent *event,
+                      gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       dom_exec_command (
+               document,
+               extension,
+               E_HTML_EDITOR_VIEW_COMMAND_FORE_COLOR,
+               undo ? event->data.string.from : event->data.string.to);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_wrap (WebKitDOMDocument *document,
+                EHTMLEditorWebExtension *extension,
+                EHTMLEditorHistoryEvent *event,
+                gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range;
+
+               range = dom_get_current_range (document);
+               node = webkit_dom_range_get_common_ancestor_container (range, NULL);
+               element = get_parent_block_element (WEBKIT_DOM_NODE (node));
+               dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (element));
+
+               dom_force_spell_check_for_current_paragraph (document, extension);
+       } else
+               dom_selection_wrap (document, extension);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_page_dialog (WebKitDOMDocument *document,
+                       EHTMLEditorWebExtension *extension,
+                       EHTMLEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       WebKitDOMHTMLElement *body;
+       WebKitDOMNamedNodeMap *attributes, *attributes_history;
+       gint length, length_history, ii, jj;
+
+       body = webkit_dom_document_get_body (document);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       if (undo) {
+               attributes = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes_history = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.from));
+       } else {
+               attributes_history = webkit_dom_element_get_attributes (WEBKIT_DOM_ELEMENT (body));
+               attributes = webkit_dom_element_get_attributes (
+                       WEBKIT_DOM_ELEMENT (event->data.dom.to));
+       }
+
+       length = webkit_dom_named_node_map_get_length (attributes);
+       length_history = webkit_dom_named_node_map_get_length (attributes_history);
+       for (ii = length - 1; ii >= 0; ii--) {
+               gchar *name;
+               WebKitDOMNode *attr;
+               gboolean replaced = FALSE;
+
+               attr = webkit_dom_named_node_map_item (attributes, ii);
+               name = webkit_dom_node_get_local_name (attr);
+
+               for (jj = length_history - 1; jj >= 0; jj--) {
+                       gchar *name_history;
+                       WebKitDOMNode *attr_history;
+
+                       attr_history = webkit_dom_named_node_map_item (attributes_history, jj);
+                       name_history = webkit_dom_node_get_local_name (attr_history);
+                       if (g_strcmp0 (name, name_history) == 0) {
+                               WebKitDOMNode *attr_clone;
+
+                               attr_clone = webkit_dom_node_clone_node (
+                                               undo ? attr_history : attr, TRUE);
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr_clone),
+                                       NULL);
+
+                               /* Link color has to replaced in HEAD as well. */
+                               if (g_strcmp0 (name, "link") == 0) {
+                                       gchar *value;
+
+                                       value = webkit_dom_node_get_node_value (attr_clone);
+                                       dom_set_link_color (document, value);
+                                       g_free (value);
+                               }
+                               replaced = TRUE;
+                       }
+                       g_free (name_history);
+                       g_object_unref (attr_history);
+                       if (replaced)
+                               break;
+               }
+
+               if (!replaced) {
+                       if (undo) {
+                               webkit_dom_element_remove_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (attr),
+                                       NULL);
+                       } else {
+                               webkit_dom_element_set_attribute_node (
+                                       WEBKIT_DOM_ELEMENT (body),
+                                       WEBKIT_DOM_ATTR (
+                                               webkit_dom_node_clone_node (attr, TRUE)),
+                                       NULL);
+                       }
+               }
+               g_free (name);
+               g_object_unref (attr);
+       }
+       g_object_unref (attributes);
+       g_object_unref (attributes_history);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_hrule_dialog (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension,
+                        EHTMLEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMElement *element;
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       dom_selection_save (document);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+
+       if (undo) {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+               if (event->data.dom.from)
+                       node = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               else
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+               if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node)) {
+                       if (!event->data.dom.from)
+                               remove_node (node);
+                       else
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       event->data.dom.from,
+                                       node,
+                                       NULL);
+               }
+       } else {
+               WebKitDOMNode *node;
+               WebKitDOMElement *parent;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+
+               if (event->data.dom.from) {
+                       node = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+
+                       if (node && WEBKIT_DOM_IS_HTML_HR_ELEMENT (node))
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (node),
+                                       event->data.dom.to,
+                                       node,
+                                       NULL);
+               } else {
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               event->data.dom.to,
+                               webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent)),
+                               NULL);
+               }
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_image_dialog (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension,
+                        EHTMLEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMElement *element;
+       WebKitDOMNode *sibling, *image = NULL;
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       dom_selection_save (document);
+       element = webkit_dom_document_get_element_by_id (
+               document, "-x-evo-selection-start-marker");
+       sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (element));
+       if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+               if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                       image = sibling;
+               else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                       image = webkit_dom_node_get_first_child (sibling);
+       }
+
+       if (!image) {
+               element = WEBKIT_DOM_ELEMENT (webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element)));
+               sibling = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (element));
+               if (sibling && WEBKIT_DOM_IS_ELEMENT (sibling)) {
+                       if (WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT (sibling))
+                               image = sibling;
+                       else if (element_has_class (WEBKIT_DOM_ELEMENT (sibling), "-x-evo-resizable-wrapper"))
+                               image = webkit_dom_node_get_first_child (sibling);
+               }
+       }
+
+       if (!image)
+               return;
+
+       webkit_dom_node_replace_child (
+               webkit_dom_node_get_parent_node (image),
+               undo ? event->data.dom.from : event->data.dom.to,
+               image,
+               NULL);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_table_dialog (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension,
+                        EHTMLEditorHistoryEvent *event,
+                        gboolean undo)
+{
+       WebKitDOMElement *table, *element;
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       dom_selection_save (document);
+       element = webkit_dom_document_get_element_by_id (document, "-x-evo-selection-start-marker");
+       if (!element)
+               return;
+
+       table = dom_node_find_parent_element (WEBKIT_DOM_NODE (element), "TABLE");
+
+       if (!table) {
+               if ((!event->data.dom.to && undo) || (!event->data.dom.from && !undo)) {
+                       WebKitDOMElement *parent;
+
+                       parent = get_parent_block_element (WEBKIT_DOM_NODE (element));
+                       webkit_dom_node_insert_before (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (parent)),
+                               undo ? event->data.dom.from : event->data.dom.to,
+                               WEBKIT_DOM_NODE (parent),
+                               NULL);
+                       restore_selection_to_history_event_state (document, event->before);
+                       return;
+               } else
+                       return;
+       }
+
+       if (undo) {
+               if (!event->data.dom.from)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               event->data.dom.from,
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       } else {
+               if (!event->data.dom.to)
+                       remove_node (WEBKIT_DOM_NODE (table));
+               else
+                       webkit_dom_node_replace_child (
+                               webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (table)),
+                               event->data.dom.to,
+                               WEBKIT_DOM_NODE (table),
+                               NULL);
+       }
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_redo_paste (WebKitDOMDocument *document,
+                 EHTMLEditorWebExtension *extension,
+                 EHTMLEditorHistoryEvent *event,
+                 gboolean undo)
+{
+       if (undo) {
+               if (event->type == HISTORY_PASTE_QUOTED) {
+                       WebKitDOMElement *tmp;
+                       WebKitDOMNode *parent;
+                       WebKitDOMNode *sibling;
+
+                       restore_selection_to_history_event_state (document, event->after);
+
+                       dom_selection_save (document);
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+                       if (!tmp)
+                               return;
+
+                       parent = webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (tmp));
+                       while (!WEBKIT_DOM_IS_HTML_BODY_ELEMENT (webkit_dom_node_get_parent_node (parent)))
+                               parent = webkit_dom_node_get_parent_node (parent);
+
+                       sibling = webkit_dom_node_get_previous_sibling (parent);
+                       if (sibling) {
+                               dom_add_selection_markers_into_element_end (document, WEBKIT_DOM_ELEMENT 
(sibling), NULL, NULL);
+
+                               remove_node (parent);
+                       } else {
+                               webkit_dom_node_replace_child (
+                                       webkit_dom_node_get_parent_node (parent),
+                                       WEBKIT_DOM_NODE (dom_prepare_paragraph (document, extension, TRUE)),
+                                       parent,
+                                       NULL);
+                       }
+                       dom_selection_restore (document);
+               } else {
+                       WebKitDOMDOMWindow *dom_window;
+                       WebKitDOMDOMSelection *dom_selection;
+                       WebKitDOMElement *element, *tmp;
+                       WebKitDOMRange *range;
+
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+                       /* Restore the selection how it was before the event occured. */
+                       range = get_range_for_point (document, event->before.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+                       dom_selection_save (document);
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-end-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (element));
+
+                       element = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       webkit_dom_element_remove_attribute (element, "id");
+
+                       range = get_range_for_point (document, event->after.start);
+                       webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+                       webkit_dom_dom_selection_add_range (dom_selection, range);
+
+                       dom_selection_save (document);
+
+                       tmp = webkit_dom_document_get_element_by_id (
+                               document, "-x-evo-selection-start-marker");
+
+                       remove_node (WEBKIT_DOM_NODE (tmp));
+
+                       webkit_dom_element_set_id (
+                               element, "-x-evo-selection-start-marker");
+
+                       dom_selection_restore (document);
+
+                       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+
+                       dom_force_spell_check_for_current_paragraph (document, extension);
+               }
+       } else {
+               restore_selection_to_history_event_state (document, event->before);
+
+               if (event->type == HISTORY_PASTE)
+                       dom_convert_and_insert_html_into_selection (document, extension, 
event->data.string.to, FALSE);
+               else if (event->type == HISTORY_PASTE_QUOTED)
+                       dom_quote_and_insert_text_into_selection (document, extension, event->data.string.to);
+               else if (event->type == HISTORY_INSERT_HTML)
+                       dom_insert_html (document, extension, event->data.string.to);
+               else
+                       dom_convert_and_insert_html_into_selection (document, extension, 
event->data.string.to, FALSE);
+                       //e_html_editor_selection_insert_as_text (selection, event->data.string.to);
+       }
+}
+
+static void
+undo_redo_image (WebKitDOMDocument *document,
+                 EHTMLEditorWebExtension *extension,
+                 EHTMLEditorHistoryEvent *event,
+                 gboolean undo)
+{
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (undo) {
+               WebKitDOMElement *element;
+               WebKitDOMNode *node;
+
+               range = get_range_for_point (document, event->before.start);
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               dom_selection_save (document);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               node = webkit_dom_node_get_next_sibling  (WEBKIT_DOM_NODE (element));
+
+               if (WEBKIT_DOM_IS_ELEMENT (node))
+                       if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-resizable-wrapper") ||
+                           element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-smiley-wrapper"))
+                               remove_node (node);
+               dom_selection_restore (document);
+       } else {
+               WebKitDOMElement *element;
+
+               range = get_range_for_point (document, event->before.start);
+               /* Create temporary node on the selection where the delete occured. */
+               webkit_dom_dom_selection_remove_all_ranges (dom_selection);
+               webkit_dom_dom_selection_add_range (dom_selection, range);
+
+               dom_selection_save (document);
+               element = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+
+               /* Insert the deleted content back to the body. */
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+
+               dom_selection_restore (document);
+               dom_force_spell_check_for_current_paragraph (document, extension);
+       }
+}
+
+static void
+undo_redo_replace (WebKitDOMDocument *document,
+                   EHTMLEditorWebExtension *extension,
+                   EHTMLEditorHistoryEvent *event,
+                   gboolean undo)
+{
+       restore_selection_to_history_event_state (document, undo ? event->after : event->before);
+
+       if (undo) {
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "word");
+       }
+
+       dom_exec_command (
+               document,
+               extension,
+               E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT,
+               undo ? event->data.string.from : event->data.string.to);
+
+       dom_force_spell_check_for_current_paragraph (document, extension);
+
+       restore_selection_to_history_event_state (document, undo ? event->before : event->after);
+}
+
+static void
+undo_redo_replace_all (EHTMLEditorUndoRedoManager *manager,
+                       WebKitDOMDocument *document,
+                       EHTMLEditorWebExtension *extension,
+                       EHTMLEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       if (undo) {
+               if (event->type == HISTORY_REPLACE) {
+                       undo_redo_replace (document, extension, event, undo);
+                       return;
+               } else {
+                       EHTMLEditorHistoryEvent *next_event;
+                       GList *next_item;
+                       WebKitDOMDOMWindow *dom_window;
+                       WebKitDOMDOMSelection *dom_selection;
+
+                       next_item = manager->priv->history->next;
+
+                       while (next_item) {
+                               next_event = next_item->data;
+
+                               if (next_event->type != HISTORY_REPLACE)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.from, event->data.string.from) != 0)
+                                       break;
+
+                               if (g_strcmp0 (next_event->data.string.to, event->data.string.to) != 0)
+                                       break;
+
+                               undo_redo_replace (document, extension, next_event, undo);
+
+                               next_item = next_item->next;
+                       }
+
+                       manager->priv->history = next_item->prev;
+
+                       dom_window = webkit_dom_document_get_default_view (document);
+                       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+                       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+               }
+       } else {
+               /* Find if this history item is part of HISTORY_REPLACE_ALL. */
+               EHTMLEditorHistoryEvent *prev_event;
+               GList *prev_item;
+               gboolean replace_all = FALSE;
+
+               prev_item = manager->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE)
+                               prev_item = prev_item->prev;
+                       else if (prev_event->type == HISTORY_REPLACE_ALL) {
+                               replace_all = TRUE;
+                               break;
+                       } else
+                               break;
+               }
+
+               if (!replace_all) {
+                       undo_redo_replace (document, extension, event, undo);
+                       return;
+               }
+
+               prev_item = manager->priv->history->prev;
+               while (prev_item) {
+                       prev_event = prev_item->data;
+
+                       if (prev_event->type == HISTORY_REPLACE) {
+                               undo_redo_replace (document, extension, prev_event, undo);
+                               prev_item = prev_item->prev;
+                       } else
+                               break;
+               }
+
+               manager->priv->history = prev_item->next;
+       }
+}
+
+static void
+undo_redo_remove_link (WebKitDOMDocument *document,
+                       EHTMLEditorWebExtension *extension,
+                       EHTMLEditorHistoryEvent *event,
+                       gboolean undo)
+{
+       if (undo)
+               restore_selection_to_history_event_state (document, event->after);
+
+       if (undo) {
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMElement *element;
+               WebKitDOMRange *range;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+               /* Select the anchor. */
+               webkit_dom_dom_selection_modify (dom_selection, "move", "left", "word");
+               webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "word");
+
+               range = dom_get_current_range (document);
+               element = webkit_dom_document_create_element (document, "SPAN", NULL);
+               webkit_dom_range_surround_contents (range, WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_node_insert_before (
+                       webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (element)),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (event->data.fragment), TRUE),
+                       WEBKIT_DOM_NODE (element),
+                       NULL);
+               remove_node (WEBKIT_DOM_NODE (element));
+       } else
+               dom_selection_unlink (document, extension);
+
+       if (undo)
+               restore_selection_to_history_event_state (document, event->before);
+}
+
+static void
+undo_input (EHTMLEditorUndoRedoManager *manager,
+            WebKitDOMDocument *document,
+            EHTMLEditorWebExtension *extension,
+            EHTMLEditorHistoryEvent *event)
+{
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       restore_selection_to_history_event_state (document, event->after);
+
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       if (dom_selection_is_citation (document)) {
+               /* Post processing of quoted text in body_input_event_cb needs to be called. */
+               manager->priv->operation_in_progress = FALSE;
+               e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, TRUE);
+       }
+       dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+}
+
+static void
+undo_redo_citation_split (WebKitDOMDocument *document,
+                          EHTMLEditorWebExtension *extension,
+                          EHTMLEditorHistoryEvent *event,
+                          gboolean undo)
+{
+       if (undo) {
+               gint citation_level = 1, length, word_wrap_length;
+               WebKitDOMElement *selection_start, *parent;
+               WebKitDOMNode *citation_before, *citation_after, *child, *last_child, *tmp;
+
+               restore_selection_to_history_event_state (document, event->after);
+
+               dom_selection_save (document);
+               selection_start = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               if (!selection_start)
+                       return;
+
+               parent = get_parent_block_element (WEBKIT_DOM_NODE (selection_start));
+
+               citation_before = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (parent));
+               if (!dom_node_is_citation_node (citation_before))
+                       return;
+
+               citation_after = webkit_dom_node_get_next_sibling (WEBKIT_DOM_NODE (parent));
+               if (!dom_node_is_citation_node (citation_after))
+                       return;
+
+               /* Get first block in next citation. */
+               child = webkit_dom_node_get_first_child (citation_after);
+               while (child && dom_node_is_citation_node (child)) {
+                       citation_level++;
+                       child = webkit_dom_node_get_first_child (child);
+               }
+
+               dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (child));
+               dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (child));
+
+               /* Get last block in previous citation. */
+               last_child = webkit_dom_node_get_last_child (citation_before);
+               while (last_child && dom_node_is_citation_node (last_child))
+                       last_child = webkit_dom_node_get_last_child (last_child);
+
+               dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (last_child));
+               dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (last_child));
+
+               /* Copy the content of the first block to the last block to get
+                * to the state how the block looked like before it was split. */
+               while ((tmp = webkit_dom_node_get_first_child (child)))
+                       webkit_dom_node_append_child (last_child, tmp, NULL);
+
+               word_wrap_length = e_html_editor_web_extension_get_word_wrap_length (extension);
+               length = word_wrap_length - 2 * citation_level;
+
+               /* We need to re-wrap and re-quote the block. */
+               last_child = WEBKIT_DOM_NODE (dom_wrap_paragraph_length (
+                       document, extension, WEBKIT_DOM_ELEMENT (last_child), length));
+               dom_quote_plain_text_element_after_wrapping (
+                       document, WEBKIT_DOM_ELEMENT (last_child), citation_level);
+
+               remove_node (child);
+
+               /* Move all the block from next citation to the previous one. */
+               while ((child = webkit_dom_node_get_first_child (citation_after)))
+                       webkit_dom_node_append_child (citation_before, child, NULL);
+
+               remove_node (WEBKIT_DOM_NODE (parent));
+               remove_node (WEBKIT_DOM_NODE (citation_after));
+
+               /* If enter was pressed when some text was selected, restore it. */
+               if (event->data.fragment != NULL)
+                       undo_delete (document, extension, event);
+
+               restore_selection_to_history_event_state (document, event->before);
+
+               dom_force_spell_check (document, extension);
+       } else {
+               dom_insert_new_line_into_citation (document, extension, "");
+       }
+}
+
+gboolean
+e_html_editor_undo_redo_manager_is_operation_in_progress (EHTMLEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       return manager->priv->operation_in_progress;
+}
+
+void
+e_html_editor_undo_redo_manager_set_operation_in_progress (EHTMLEditorUndoRedoManager *manager,
+                                                           gboolean value)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       manager->priv->operation_in_progress = value;
+}
+
+#if d(1)+0
+static void
+print_fragment_inner_html (WebKitDOMDocumentFragment *fragment)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *div;
+       gchar *inner_html;
+
+       if (!fragment) {
+               printf ("\tNone'\n");
+               return;
+       }
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (fragment));
+       div = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (div),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (fragment), TRUE),
+                       NULL);
+
+       inner_html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (div));
+       printf ("\t'%s'\n", inner_html);
+       remove_node (WEBKIT_DOM_NODE (div));
+       g_free (inner_html);
+}
+
+static void
+print_node_inner_html (WebKitDOMNode *fragment)
+{
+       WebKitDOMDocument *document;
+       WebKitDOMElement *div;
+       gchar *inner_html;
+
+       if (!fragment) {
+               printf ("\tnone\n");
+               return;
+       }
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (fragment));
+       div = webkit_dom_document_create_element (document, "div", NULL);
+       webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (div),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (fragment), TRUE),
+                       NULL);
+
+       inner_html = webkit_dom_html_element_get_inner_html (WEBKIT_DOM_HTML_ELEMENT (div));
+       remove_node (WEBKIT_DOM_NODE (div));
+
+       printf ("\t'%s'\n", inner_html);
+
+       g_free (inner_html);
+}
+
+static void
+print_history_event (EHTMLEditorHistoryEvent *event)
+{
+       printf ("HISTORY EVENT: %d ; \n", event->type);
+       printf ("\t before: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", event->before.start.x, 
event->before.start.y, event->before.end.x, event->before.end.y);
+       printf ("\t after: start_x: %u ; start_y: %u ; end_x: %u ; end_y: %u ;\n", event->after.start.x, 
event->after.start.y, event->after.end.x, event->after.end.y);
+       switch (event->type) {
+               case HISTORY_DELETE:
+               case HISTORY_INPUT:
+               case HISTORY_REMOVE_LINK:
+               case HISTORY_SMILEY:
+               case HISTORY_IMAGE:
+               case HISTORY_CITATION_SPLIT:
+                       print_fragment_inner_html (event->data.fragment);
+                       break;
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_BOLD:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_INDENT:
+               case HISTORY_ITALIC:
+               case HISTORY_MONOSPACE:
+               case HISTORY_UNDERLINE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_WRAP:
+                       printf (" from %d to %d ;\n", event->data.style.from, event->data.style.to);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       printf (" pasting: '%s' ; \n", event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_PAGE_DIALOG:
+                       print_node_inner_html (event->data.dom.from);
+                       print_node_inner_html (event->data.dom.to);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       printf (" from '%s' to '%s';\n", event->data.string.from, event->data.string.to);
+                       break;
+               case HISTORY_START:
+                       printf ("HISTORY START\n");
+                       break;
+               default:
+                       printf ("Unknown history type\n");
+       }
+}
+
+static void
+print_history (EHTMLEditorUndoRedoManager *manager)
+{
+       if (manager->priv->history) {
+               printf ("\n");
+               g_list_foreach (
+                       manager->priv->history, (GFunc) print_history_event, NULL);
+               printf ("\n");
+       } else {
+               printf ("History empty!\n");
+       }
+}
+
+static void
+print_undo_events (EHTMLEditorUndoRedoManager *manager)
+{
+       GList *item = manager->priv->history;
+
+       printf ("UNDO EVENTS:\n");
+       if (!item || !item->next) {
+               printf ("EMPTY\n");
+               return;
+       }
+
+       print_history_event (item->data);
+       item = item->next;
+       while (item) {
+               print_history_event (item->data);
+               item = item->next;
+       }
+
+       printf ("\n");
+
+}
+
+static void
+print_redo_events (EHTMLEditorUndoRedoManager *manager)
+{
+       GList *item = manager->priv->history;
+
+       printf ("REDO EVENTS:\n");
+       if (!item || !item->prev) {
+               printf ("EMPTY\n");
+               return;
+       }
+
+       item = item->prev;
+       while (item) {
+               print_history_event (item->data);
+               item = item->prev;
+       }
+
+       printf ("\n");
+}
+#endif
+
+static void
+free_history_event_content (EHTMLEditorHistoryEvent *event)
+{
+       switch (event->type) {
+               case HISTORY_INPUT:
+               case HISTORY_DELETE:
+               case HISTORY_CITATION_SPLIT:
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+               case HISTORY_REMOVE_LINK:
+                       if (event->data.fragment != NULL)
+                               g_object_unref (event->data.fragment);
+                       break;
+               case HISTORY_FONT_COLOR:
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       if (event->data.string.from != NULL)
+                               g_free (event->data.string.from);
+                       if (event->data.string.to != NULL)
+                               g_free (event->data.string.to);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+               case HISTORY_IMAGE_DIALOG:
+               case HISTORY_CELL_DIALOG:
+               case HISTORY_TABLE_DIALOG:
+               case HISTORY_PAGE_DIALOG:
+                       if (event->data.dom.from != NULL)
+                               g_object_unref (event->data.dom.from);
+                       if (event->data.dom.to != NULL)
+                               g_object_unref (event->data.dom.to);
+                       break;
+               default:
+                       break;
+       }
+}
+
+static void
+free_history_event (EHTMLEditorHistoryEvent *event)
+{
+       if (event == NULL)
+               return;
+
+       free_history_event_content (event);
+
+       g_free (event);
+}
+
+static void
+remove_history_event (EHTMLEditorUndoRedoManager *manager,
+                      GList *item)
+{
+       free_history_event_content (item->data);
+
+       manager->priv->history = g_list_delete_link (manager->priv->history, item);
+       manager->priv->history_size--;
+}
+
+static void
+remove_forward_redo_history_events_if_needed (EHTMLEditorUndoRedoManager *manager)
+{
+       GList *history = manager->priv->history;
+       GList *item;
+
+       if (!history || !history->prev)
+               return;
+
+       item = history->prev;
+       while (item) {
+               GList *prev_item = item->prev;
+
+               remove_history_event (manager, item);
+               item = prev_item;
+       }
+}
+
+void
+e_html_editor_undo_redo_manager_insert_history_event (EHTMLEditorUndoRedoManager *manager,
+                                                      EHTMLEditorHistoryEvent *event)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (manager->priv->operation_in_progress)
+               return;
+
+       d (print_history_event (event));
+
+       remove_forward_redo_history_events_if_needed (manager);
+
+       if (manager->priv->history_size >= HISTORY_SIZE_LIMIT)
+               remove_history_event (manager, g_list_last (manager->priv->history)->prev);
+
+       manager->priv->history = g_list_prepend (manager->priv->history, event);
+       manager->priv->history_size++;
+       manager->priv->can_undo = TRUE;
+
+       g_object_notify (G_OBJECT (manager), "can-undo");
+}
+
+EHTMLEditorHistoryEvent *
+e_html_editor_undo_redo_manager_get_current_history_event (EHTMLEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager), NULL);
+
+       if (manager->priv->history)
+               return manager->priv->history->data;
+
+       return NULL;
+}
+
+gboolean
+e_html_editor_undo_redo_manager_can_undo (EHTMLEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       if (manager->priv->history) {
+               EHTMLEditorHistoryEvent *event;
+
+               event = manager->priv->history->data;
+
+               return (event->type != HISTORY_START);
+       } else
+               return FALSE;
+}
+
+static gboolean
+event_selection_was_collapsed (EHTMLEditorHistoryEvent *ev)
+{
+       return (ev->before.start.x == ev->before.end.x) && (ev->before.start.y == ev->before.end.y);
+}
+
+void
+e_html_editor_undo_redo_manager_undo (EHTMLEditorUndoRedoManager *manager)
+{
+       EHTMLEditorHistoryEvent *event;
+       EHTMLEditorWebExtension *extension;
+       GList *history;
+       WebKitDOMDocument *document;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (!e_html_editor_undo_redo_manager_can_undo (manager))
+               return;
+
+       history = manager->priv->history;
+       event = history->data;
+
+       d (print_history_event (event));
+
+       manager->priv->operation_in_progress = TRUE;
+
+       document = manager->priv->document;
+       extension = html_editor_undo_redo_manager_ref_extension (manager);
+       g_return_if_fail (extension != NULL);
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_ITALIC:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_FONT_SIZE:
+                       if (event_selection_was_collapsed (event)) {
+                               if (history->next) {
+                                       manager->priv->history = history->next;
+                                       e_html_editor_undo_redo_manager_undo (manager);
+                               }
+                               manager->priv->operation_in_progress = FALSE;
+                               g_object_unref (extension);
+                               return;
+                       }
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_MONOSPACE:
+                       undo_redo_style_change (document, extension, event, TRUE);
+                       break;
+               case HISTORY_DELETE:
+                       undo_delete (document, extension, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (document, extension, event, TRUE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_input (manager, document, extension, event);
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (document, extension, event, TRUE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (document, extension, event, TRUE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (document, extension, event, TRUE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (document, extension, event, TRUE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (document, extension, event, TRUE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (document, extension, event, TRUE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (document, extension, event, TRUE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (document, extension, event, TRUE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (document, extension, event, TRUE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (document, extension, event, TRUE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (manager, document, extension, event, TRUE);
+                       break;
+               default:
+                       g_object_unref (extension);
+                       return;
+       }
+
+       if (history->next)
+               manager->priv->history = manager->priv->history->next;
+
+       d (print_history (manager));
+/* FIXME WK2
+       html_editor_view_user_changed_contents_cb (view);*/
+
+       manager->priv->operation_in_progress = FALSE;
+
+       g_object_unref (extension);
+}
+
+gboolean
+e_html_editor_undo_redo_manager_can_redo (EHTMLEditorUndoRedoManager *manager)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager), FALSE);
+
+       if (manager->priv->history && manager->priv->history->prev)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+void
+e_html_editor_undo_redo_manager_redo (EHTMLEditorUndoRedoManager *manager)
+{
+       EHTMLEditorWebExtension *extension;
+       EHTMLEditorHistoryEvent *event;
+       GList *history;
+       WebKitDOMDocument *document;
+
+       if (!e_html_editor_undo_redo_manager_can_redo (manager))
+               return;
+
+       history = manager->priv->history;
+       event = history->prev->data;
+
+       d (print_history_event (event));
+
+       document = manager->priv->document;
+       extension = html_editor_undo_redo_manager_ref_extension (manager);
+       g_return_if_fail (extension != NULL);
+
+       manager->priv->operation_in_progress = TRUE;
+
+       switch (event->type) {
+               case HISTORY_BOLD:
+               case HISTORY_MONOSPACE:
+               case HISTORY_STRIKETHROUGH:
+               case HISTORY_UNDERLINE:
+               case HISTORY_ALIGNMENT:
+               case HISTORY_BLOCK_FORMAT:
+               case HISTORY_FONT_SIZE:
+               case HISTORY_ITALIC:
+                       undo_redo_style_change (document, extension, event, FALSE);
+                       break;
+               case HISTORY_DELETE:
+                       redo_delete (document, extension, event);
+                       break;
+               case HISTORY_INDENT:
+                       undo_redo_indent (document, extension, event, FALSE);
+                       break;
+               case HISTORY_INPUT:
+                       undo_delete (document, extension, event);
+                       break;
+               case HISTORY_REMOVE_LINK:
+                       undo_redo_remove_link (document, extension, event, FALSE);
+                       break;
+               case HISTORY_FONT_COLOR:
+                       undo_redo_font_color (document, extension, event, FALSE);
+                       break;
+               case HISTORY_CITATION_SPLIT:
+                       undo_redo_citation_split (document, extension, event, FALSE);
+                       break;
+               case HISTORY_PASTE:
+               case HISTORY_PASTE_AS_TEXT:
+               case HISTORY_PASTE_QUOTED:
+               case HISTORY_INSERT_HTML:
+                       undo_redo_paste (document, extension, event, FALSE);
+                       break;
+               case HISTORY_IMAGE:
+               case HISTORY_SMILEY:
+                       undo_redo_image (document, extension, event, FALSE);
+                       break;
+               case HISTORY_WRAP:
+                       undo_redo_wrap (document, extension, event, FALSE);
+                       break;
+               case HISTORY_IMAGE_DIALOG:
+                       undo_redo_image_dialog (document, extension, event, FALSE);
+                       break;
+               case HISTORY_TABLE_DIALOG:
+                       undo_redo_table_dialog (document, extension, event, FALSE);
+                       break;
+               case HISTORY_PAGE_DIALOG:
+                       undo_redo_page_dialog (document, extension, event, FALSE);
+                       break;
+               case HISTORY_HRULE_DIALOG:
+                       undo_redo_hrule_dialog (document, extension, event, FALSE);
+                       break;
+               case HISTORY_REPLACE:
+               case HISTORY_REPLACE_ALL:
+                       undo_redo_replace_all (manager, document, extension, event, FALSE);
+                       break;
+               default:
+                       g_object_unref (extension);
+                       return;
+       }
+
+       manager->priv->history = manager->priv->history->prev;
+
+       d (print_history (manager));
+/* FIXME WK2
+       html_editor_view_user_changed_contents_cb (view);*/
+
+       manager->priv->operation_in_progress = FALSE;
+
+       g_object_unref (extension);
+}
+
+void
+e_html_editor_undo_redo_manager_clean_history (EHTMLEditorUndoRedoManager *manager)
+{
+       EHTMLEditorWebExtension *extension;
+       EHTMLEditorHistoryEvent *ev;
+
+       g_return_if_fail (E_IS_HTML_EDITOR_UNDO_REDO_MANAGER (manager));
+
+       if (manager->priv->history != NULL) {
+               g_list_free_full (manager->priv->history, (GDestroyNotify) free_history_event);
+               manager->priv->history = NULL;
+       }
+
+       manager->priv->history_size = 0;
+       extension = html_editor_undo_redo_manager_ref_extension (manager);
+       g_return_if_fail (extension != NULL);
+       e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, FALSE);
+       g_object_unref (extension);
+       manager->priv->operation_in_progress = FALSE;
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       ev->type = HISTORY_START;
+       manager->priv->history = g_list_append (manager->priv->history, ev);
+
+       manager->priv->can_undo = FALSE;
+       g_object_notify (G_OBJECT (manager), "can-undo");
+       manager->priv->can_redo = FALSE;
+       g_object_notify (G_OBJECT (manager), "can-redo");
+}
+
+static void
+html_editor_undo_redo_manager_set_extension (EHTMLEditorUndoRedoManager *manager,
+                                             EHTMLEditorWebExtension *extension)
+{
+       g_return_if_fail (E_IS_HTML_EDITOR_WEB_EXTENSION (extension));
+
+       g_weak_ref_set (&manager->priv->extension, extension);
+}
+
+static void
+html_editor_undo_redo_manager_dispose (GObject *object)
+{
+       EHTMLEditorUndoRedoManagerPrivate *priv;
+       EHTMLEditorWebExtension *extension;
+
+       priv = E_HTML_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (object);
+
+       if (priv->history != NULL) {
+               g_list_free_full (priv->history, (GDestroyNotify) free_history_event);
+               priv->history = NULL;
+       }
+
+       extension = g_weak_ref_get (&priv->extension);
+       g_clear_object (&extension);
+       g_weak_ref_set (&priv->extension, NULL);
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_html_editor_undo_redo_manager_parent_class)->dispose (object);
+}
+
+static void
+html_editor_undo_redo_manager_get_property (GObject *object,
+                                            guint property_id,
+                                            GValue *value,
+                                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CAN_REDO:
+                       g_value_set_boolean (
+                               value, e_html_editor_undo_redo_manager_can_redo (
+                               E_HTML_EDITOR_UNDO_REDO_MANAGER (object)));
+                       return;
+
+               case PROP_CAN_UNDO:
+                       g_value_set_boolean (
+                               value, e_html_editor_undo_redo_manager_can_undo (
+                               E_HTML_EDITOR_UNDO_REDO_MANAGER (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+html_editor_undo_redo_manager_set_property (GObject *object,
+                                            guint property_id,
+                                            const GValue *value,
+                                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_HTML_EDITOR_WEB_EXTENSION:
+                       html_editor_undo_redo_manager_set_extension (
+                               E_HTML_EDITOR_UNDO_REDO_MANAGER (object),
+                               g_value_get_object (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_html_editor_undo_redo_manager_class_init (EHTMLEditorUndoRedoManagerClass *class)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (class, sizeof (EHTMLEditorUndoRedoManagerPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->dispose = html_editor_undo_redo_manager_dispose;
+       object_class->get_property = html_editor_undo_redo_manager_get_property;
+       object_class->set_property = html_editor_undo_redo_manager_set_property;
+
+       /**
+        * EHTMLEditorUndoRedoManager:can-redo
+        *
+        * Determines whether it's possible to redo previous action. The action
+        * is usually disabled when there is no action to redo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_REDO,
+               g_param_spec_boolean (
+                       "can-redo",
+                       "Can Redo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * EHTMLEditorUndoRedoManager:can-undo
+        *
+        * Determines whether it's possible to undo last action. The action
+        * is usually disabled when there is no previous action to undo.
+        */
+       g_object_class_install_property (
+               object_class,
+               PROP_CAN_UNDO,
+               g_param_spec_boolean (
+                       "can-undo",
+                       "Can Undo",
+                       NULL,
+                       FALSE,
+                       G_PARAM_READABLE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_HTML_EDITOR_WEB_EXTENSION,
+               g_param_spec_object (
+                       "html-editor-web-extension",
+                       NULL,
+                       NULL,
+                       E_TYPE_HTML_EDITOR_WEB_EXTENSION,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_html_editor_undo_redo_manager_init (EHTMLEditorUndoRedoManager *manager)
+{
+       manager->priv = E_HTML_EDITOR_UNDO_REDO_MANAGER_GET_PRIVATE (manager);
+
+       manager->priv->operation_in_progress = FALSE;
+       manager->priv->history = NULL;
+       e_html_editor_undo_redo_manager_clean_history (manager);
+}
diff --git a/web-extensions/e-html-editor-undo-redo-manager.h 
b/web-extensions/e-html-editor-undo-redo-manager.h
new file mode 100644
index 0000000..a1d227e
--- /dev/null
+++ b/web-extensions/e-html-editor-undo-redo-manager.h
@@ -0,0 +1,102 @@
+/*
+ * e-html-editor-undo-redo-manager.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef E_HTML_EDITOR_UNDO_REDO_MANAGER_H
+#define E_HTML_EDITOR_UNDO_REDO_MANAGER_H
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <webkitdom/webkitdom.h>
+
+#include "e-html-editor-history-event.h"
+
+#define E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER \
+       (e_html_editor_undo_redo_manager_get_type ())
+#define E_HTML_EDITOR_UNDO_REDO_MANAGER(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER, EHTMLEditorUndoRedoManager))
+#define E_HTML_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER, EHTMLEditorUndoRedoManagerClass))
+#define E_IS_HTML_EDITOR_UNDO_REDO_MANAGER(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER))
+#define E_IS_HTML_EDITOR_UNDO_REDO_MANAGER_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER))
+#define E_HTML_EDITOR_UNDO_REDO_MANAGER_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER, EHTMLEditorUndoRedoManagerClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EHTMLEditorUndoRedoManager EHTMLEditorUndoRedoManager;
+typedef struct _EHTMLEditorUndoRedoManagerClass EHTMLEditorUndoRedoManagerClass;
+typedef struct _EHTMLEditorUndoRedoManagerPrivate EHTMLEditorUndoRedoManagerPrivate;
+
+struct _EHTMLEditorUndoRedoManager {
+       GObject parent;
+       EHTMLEditorUndoRedoManagerPrivate *priv;
+};
+
+struct _EHTMLEditorUndoRedoManagerClass
+{
+       GObjectClass parent_class;
+};
+
+GType          e_html_editor_undo_redo_manager_get_type
+                                               (void) G_GNUC_CONST;
+
+void           e_html_editor_undo_redo_manager_set_document
+                                               (EHTMLEditorUndoRedoManager *manager,
+                                                WebKitDOMDocument *document);
+
+gboolean       e_html_editor_undo_redo_manager_is_operation_in_progress
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+void           e_html_editor_undo_redo_manager_set_operation_in_progress
+                                               (EHTMLEditorUndoRedoManager *manager,
+                                                gboolean value);
+
+void           e_html_editor_undo_redo_manager_insert_history_event
+                                               (EHTMLEditorUndoRedoManager *manager,
+                                                EHTMLEditorHistoryEvent *event);
+
+EHTMLEditorHistoryEvent *
+               e_html_editor_undo_redo_manager_get_current_history_event
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+gboolean       e_html_editor_undo_redo_manager_can_undo
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+void           e_html_editor_undo_redo_manager_undo
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+gboolean       e_html_editor_undo_redo_manager_can_redo
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+void           e_html_editor_undo_redo_manager_redo
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+void           e_html_editor_undo_redo_manager_clean_history
+                                               (EHTMLEditorUndoRedoManager *manager);
+
+G_END_DECLS
+
+#endif /* E_HTML_EDITOR_UNDO_REDO_MANAGER_H */
diff --git a/web-extensions/e-html-editor-view-dom-functions.c 
b/web-extensions/e-html-editor-view-dom-functions.c
index d6f28db..e2a363f 100644
--- a/web-extensions/e-html-editor-view-dom-functions.c
+++ b/web-extensions/e-html-editor-view-dom-functions.c
@@ -54,6 +54,7 @@
  */
 gboolean
 dom_exec_command (WebKitDOMDocument *document,
+                  EHTMLEditorWebExtension *extension,
                   EHTMLEditorViewCommand command,
                   const gchar *value)
 {
@@ -120,6 +121,8 @@ dom_exec_command (WebKitDOMDocument *document,
                CHECK_COMMAND (E_HTML_EDITOR_VIEW_COMMAND_USE_CSS, "UseCSS", TRUE)
        }
 
+       e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, TRUE);
+
        return webkit_dom_document_exec_command (
                document, cmd_str, FALSE, has_value ? value : "" );
 }
@@ -214,87 +217,6 @@ dom_force_spell_check_for_current_paragraph (WebKitDOMDocument *document,
        dom_selection_restore (document);
 }
 
-static WebKitDOMElement *
-create_selection_marker (WebKitDOMDocument *document,
-                         gboolean start)
-{
-       WebKitDOMElement *element;
-
-       element = webkit_dom_document_create_element (
-               document, "SPAN", NULL);
-       webkit_dom_element_set_id (
-               element,
-               start ? "-x-evo-selection-start-marker" :
-                       "-x-evo-selection-end-marker");
-
-       return element;
-}
-
-static void
-remove_selection_markers (WebKitDOMDocument *document)
-{
-       WebKitDOMElement *marker;
-
-       marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-start-marker");
-       if (marker)
-               remove_node (WEBKIT_DOM_NODE (marker));
-       marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-end-marker");
-       if (marker)
-               remove_node (WEBKIT_DOM_NODE (marker));
-}
-
-static void
-add_selection_markers_into_element_start (WebKitDOMDocument *document,
-                                          WebKitDOMElement *element,
-                                          WebKitDOMElement **selection_start_marker,
-                                          WebKitDOMElement **selection_end_marker)
-{
-       WebKitDOMElement *marker;
-
-       remove_selection_markers (document);
-       marker = create_selection_marker (document, FALSE);
-       webkit_dom_node_insert_before (
-               WEBKIT_DOM_NODE (element),
-               WEBKIT_DOM_NODE (marker),
-               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
-               NULL);
-       if (selection_end_marker)
-               *selection_end_marker = marker;
-
-       marker = create_selection_marker (document, TRUE);
-       webkit_dom_node_insert_before (
-               WEBKIT_DOM_NODE (element),
-               WEBKIT_DOM_NODE (marker),
-               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)),
-               NULL);
-       if (selection_start_marker)
-               *selection_start_marker = marker;
-}
-
-static void
-add_selection_markers_into_element_end (WebKitDOMDocument *document,
-                                        WebKitDOMElement *element,
-                                        WebKitDOMElement **selection_start_marker,
-                                        WebKitDOMElement **selection_end_marker)
-{
-       WebKitDOMElement *marker;
-
-       remove_selection_markers (document);
-       marker = create_selection_marker (document, TRUE);
-       webkit_dom_node_append_child (
-               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
-       if (selection_start_marker)
-               *selection_start_marker = marker;
-
-       marker = create_selection_marker (document, FALSE);
-       webkit_dom_node_append_child (
-               WEBKIT_DOM_NODE (element), WEBKIT_DOM_NODE (marker), NULL);
-       if (selection_end_marker)
-               *selection_end_marker = marker;
-}
-
 static void
 refresh_spell_check (WebKitDOMDocument *document,
                      gboolean enable_spell_check)
@@ -333,7 +255,7 @@ refresh_spell_check (WebKitDOMDocument *document,
                if (!child)
                        return;
 
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
@@ -401,6 +323,26 @@ dom_force_spell_check (WebKitDOMDocument *document,
                refresh_spell_check (document, TRUE);
 }
 
+gboolean
+dom_node_is_citation_node (WebKitDOMNode *node)
+{
+       char *value;
+
+       if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
+               return FALSE;
+
+       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
+
+       /* citation == <blockquote type='cite'> */
+       if (g_strcmp0 (value, "cite") == 0) {
+               g_free (value);
+               return TRUE;
+       } else {
+               g_free (value);
+               return FALSE;
+       }
+}
+
 static gint
 get_citation_level (WebKitDOMNode *node,
                     gboolean set_plaintext_quoted)
@@ -442,10 +384,10 @@ get_quotation_for_level (gint quote_level)
        return g_string_free (output, FALSE);
 }
 
-static void
-quote_plain_text_element_after_wrapping (WebKitDOMDocument *document,
-                                         WebKitDOMElement *element,
-                                         gint quote_level)
+void
+dom_quote_plain_text_element_after_wrapping (WebKitDOMDocument *document,
+                                             WebKitDOMElement *element,
+                                             gint quote_level)
 {
        WebKitDOMNodeList *list;
        WebKitDOMNode *quoted_node;
@@ -485,26 +427,6 @@ quote_plain_text_element_after_wrapping (WebKitDOMDocument *document,
 }
 
 static gboolean
-is_citation_node (WebKitDOMNode *node)
-{
-       char *value;
-
-       if (!WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (node))
-               return FALSE;
-
-       value = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "type");
-
-       /* citation == <blockquote type='cite'> */
-       if (g_strcmp0 (value, "cite") == 0) {
-               g_free (value);
-               return TRUE;
-       } else {
-               g_free (value);
-               return FALSE;
-       }
-}
-
-static gboolean
 return_pressed_in_empty_line (WebKitDOMDocument *document)
 {
        WebKitDOMNode *node;
@@ -532,7 +454,7 @@ return_pressed_in_empty_line (WebKitDOMDocument *document)
        return FALSE;
 }
 
-static WebKitDOMNode *
+WebKitDOMNode *
 get_parent_block_node_from_child (WebKitDOMNode *node)
 {
        WebKitDOMNode *parent = webkit_dom_node_get_parent_node (node);
@@ -553,10 +475,10 @@ get_parent_block_node_from_child (WebKitDOMNode *node)
        return parent;
 }
 
-static WebKitDOMElement *
-insert_new_line_into_citation (WebKitDOMDocument *document,
-                               EHTMLEditorWebExtension *extension,
-                               const gchar *html_to_insert)
+WebKitDOMElement *
+dom_insert_new_line_into_citation (WebKitDOMDocument *document,
+                                   EHTMLEditorWebExtension *extension,
+                                   const gchar *html_to_insert)
 {
        gboolean html_mode = FALSE, ret_val, avoid_editor_call;
        WebKitDOMElement *element, *paragraph = NULL;
@@ -639,7 +561,7 @@ insert_new_line_into_citation (WebKitDOMDocument *document,
                return NULL;
        } else {
                ret_val = dom_exec_command (
-                       document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, NULL);
+                       document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_NEW_LINE_IN_QUOTED_CONTENT, 
NULL);
 
                if (!ret_val)
                        return NULL;
@@ -663,7 +585,7 @@ insert_new_line_into_citation (WebKitDOMDocument *document,
                        WebKitDOMNode *node;
 
                        node = webkit_dom_node_get_first_child (next_sibling);
-                       while (node && is_citation_node (node))
+                       while (node && dom_node_is_citation_node (node))
                                node = webkit_dom_node_get_first_child (node);
 
                        citation_level = get_citation_level (node, FALSE);
@@ -677,7 +599,7 @@ insert_new_line_into_citation (WebKitDOMDocument *document,
                                dom_remove_wrapping_from_element (WEBKIT_DOM_ELEMENT (node));
                                node = WEBKIT_DOM_NODE (dom_wrap_paragraph_length (
                                        document, extension, WEBKIT_DOM_ELEMENT (node), length));
-                               quote_plain_text_element_after_wrapping (
+                               dom_quote_plain_text_element_after_wrapping (
                                        document, WEBKIT_DOM_ELEMENT (node), citation_level);
                        }
 
@@ -691,7 +613,7 @@ insert_new_line_into_citation (WebKitDOMDocument *document,
                        WEBKIT_DOM_HTML_ELEMENT (paragraph),
                        html_to_insert,
                        NULL);
-               add_selection_markers_into_element_end (
+               dom_add_selection_markers_into_element_end (
                        document, paragraph, NULL, NULL);
        } else
                paragraph = dom_prepare_paragraph (document, extension, TRUE);
@@ -1168,8 +1090,10 @@ emoticon_insert_span (EEmoticon *emoticon,
                       LoadContext *load_context,
                       WebKitDOMElement *span)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        EHTMLEditorWebExtension *extension = load_context->extension;
-       gboolean misplaced_selection = FALSE, empty = FALSE;
+       gboolean misplaced_selection = FALSE, empty = FALSE, smiley_written;
        gchar *node_text = NULL, *content;
        const gchar *emoticon_start;
        WebKitDOMDocument *document = load_context->document;
@@ -1178,15 +1102,54 @@ emoticon_insert_span (EEmoticon *emoticon,
        WebKitDOMNode *selection_end_marker_parent;
        WebKitDOMRange *range;
 
-       if (!dom_selection_is_collapsed (document))
-               dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+       smiley_written = e_html_editor_web_extension_get_is_smiley_written (extension);
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
 
-       dom_selection_save (document);
+       if (dom_selection_is_collapsed (document)) {
+               dom_selection_save (document);
 
-       selection_start_marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-start-marker");
-       selection_end_marker = webkit_dom_document_get_element_by_id (
-               document, "-x-evo-selection-end-marker");
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+
+               if (!smiley_written) {
+                       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+                               ev->type = HISTORY_SMILEY;
+
+                               dom_selection_get_coordinates (
+                                       document,
+                                       &ev->before.start.x,
+                                       &ev->before.start.y,
+                                       &ev->before.end.x,
+                                       &ev->before.end.y);
+                       }
+               }
+       } else {
+               if (!smiley_written) {
+                       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+                               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+                               ev->type = HISTORY_SMILEY;
+
+                               dom_selection_get_coordinates (
+                                       document,
+                                       &ev->before.start.x,
+                                       &ev->before.start.y,
+                                       &ev->before.end.x,
+                                       &ev->before.end.y);
+                       }
+               }
+
+               dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+
+               dom_selection_save (document);
+
+               selection_start_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-start-marker");
+               selection_end_marker = webkit_dom_document_get_element_by_id (
+                       document, "-x-evo-selection-end-marker");
+       }
 
        /* If the selection was not saved, move it into the first child of body */
        if (!selection_start_marker || !selection_end_marker) {
@@ -1196,11 +1159,18 @@ emoticon_insert_span (EEmoticon *emoticon,
                body = webkit_dom_document_get_body (document);
                child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
 
-               add_selection_markers_into_element_start (
+               dom_add_selection_markers_into_element_start (
                        document,
                        WEBKIT_DOM_ELEMENT (child),
                        &selection_start_marker,
                        &selection_end_marker);
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
        }
 
        /* Sometimes selection end marker is in body. Move it into next sibling */
@@ -1213,6 +1183,12 @@ emoticon_insert_span (EEmoticon *emoticon,
                        WEBKIT_DOM_NODE (selection_end_marker),
                        WEBKIT_DOM_NODE (selection_start_marker),
                        NULL);
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
        }
        selection_end_marker_parent = webkit_dom_node_get_parent_node (
                WEBKIT_DOM_NODE (selection_end_marker));
@@ -1281,13 +1257,27 @@ emoticon_insert_span (EEmoticon *emoticon,
        }
 
        /* &#8203 == UNICODE_ZERO_WIDTH_SPACE */
-       if (empty || !e_html_editor_web_extension_get_is_smiley_written (extension))
+       if (empty || !smiley_written)
                webkit_dom_html_element_insert_adjacent_html (
                        WEBKIT_DOM_HTML_ELEMENT (span), "afterend", "&#8203;", NULL);
 
+       if (ev) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMNode *node;
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               node = webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       webkit_dom_node_clone_node (WEBKIT_DOM_NODE (span), TRUE),
+                       NULL);
+               webkit_dom_html_element_insert_adjacent_html (
+                       WEBKIT_DOM_HTML_ELEMENT (node), "afterend", "&#8203;", NULL);
+               ev->data.fragment = fragment;
+       }
+
        /* Remove the text that represents the text version of smiley that was
         * written into the composer. */
-       if (node_text && e_html_editor_web_extension_get_is_smiley_written (extension)) {
+       if (node_text && smiley_written) {
                emoticon_start = g_utf8_strrchr (
                        node_text, -1, g_utf8_get_char (emoticon->text_face));
                /* Check if the written smiley is really the one that we inserted. */
@@ -1302,6 +1292,16 @@ emoticon_insert_span (EEmoticon *emoticon,
                e_html_editor_web_extension_set_is_smiley_written (extension, FALSE);
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
 
        e_html_editor_web_extension_set_content_changed (extension);
@@ -1629,6 +1629,9 @@ body_keydown_event_cb (WebKitDOMElement *element,
        if (key_code == HTML_KEY_CODE_CONTROL)
                dom_set_links_active (
                        webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element)), TRUE);
+       else if (key_code == HTML_KEY_CODE_DELETE ||
+                key_code == HTML_KEY_CODE_BACKSPACE)
+               e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, TRUE);
 }
 
 static void
@@ -1637,6 +1640,10 @@ body_keypress_event_cb (WebKitDOMElement *element,
                         EHTMLEditorWebExtension *extension)
 {
        glong key_code;
+       WebKitDOMDocument *document;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
 
        e_html_editor_web_extension_set_return_key_pressed (extension, FALSE);
        e_html_editor_web_extension_set_space_key_pressed (extension, FALSE);
@@ -1646,6 +1653,105 @@ body_keypress_event_cb (WebKitDOMElement *element,
                e_html_editor_web_extension_set_return_key_pressed (extension, TRUE);
        else if (key_code == HTML_KEY_CODE_SPACE)
                e_html_editor_web_extension_set_space_key_pressed (extension, TRUE);
+
+       document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (!webkit_dom_range_get_collapsed (range, NULL)) {
+               EHTMLEditorHistoryEvent *ev;
+               EHTMLEditorUndoRedoManager *manager;
+               WebKitDOMDocumentFragment *fragment;
+
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_DELETE;
+
+               manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+               ev->data.fragment = fragment;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+}
+
+static void
+save_history_for_input (WebKitDOMDocument *document,
+                        EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorHistoryEvent *ev;
+       EHTMLEditorUndoRedoManager *manager;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               return;
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       ev->type = HISTORY_INPUT;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       dom_selection_get_coordinates (
+               document,
+               &ev->after.start.x,
+               &ev->after.start.y,
+               &ev->after.end.x,
+               &ev->after.end.y);
+       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       fragment = webkit_dom_range_clone_contents (range, NULL);
+       /* We have to specialy handle Return key press */
+       if (e_html_editor_web_extension_get_return_key_pressed (extension)) {
+               WebKitDOMNode *node;
+
+               node = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment));
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               webkit_dom_document_create_element (document, "br", NULL)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       node,
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, FALSE)),
+                       NULL);
+
+               remove_node (webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (fragment)));
+       } else {
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (
+                               dom_create_selection_marker (document, FALSE)),
+                       NULL);
+       }
+
+       webkit_dom_dom_selection_modify (dom_selection, "move", "right", "character");
+
+       ev->data.fragment = fragment;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
 }
 
 static void
@@ -1653,6 +1759,7 @@ body_input_event_cb (WebKitDOMElement *element,
                      WebKitDOMEvent *event,
                      EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorUndoRedoManager *manager;
        gboolean html_mode;
        WebKitDOMDocument *document;
        WebKitDOMNode *node;
@@ -1661,12 +1768,29 @@ body_input_event_cb (WebKitDOMElement *element,
        document = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
        range = dom_get_current_range (document);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+
        html_mode = e_html_editor_web_extension_get_html_mode (extension);
        e_html_editor_web_extension_set_content_changed (extension);
 
-       if (e_html_editor_web_extension_get_magic_smileys_enabled (extension))
+       if (e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               e_html_editor_undo_redo_manager_set_operation_in_progress (manager, FALSE);
+               e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, FALSE);
+               dom_force_spell_check_for_current_paragraph (document, extension);
+               return;
+       }
+
+       if (!e_html_editor_web_extension_get_dont_save_history_in_body_input (extension))
+               save_history_for_input (document, extension);
+       else
+               dom_force_spell_check_for_current_paragraph (document, extension);
+
+       if (e_html_editor_web_extension_get_magic_smileys_enabled (extension) &&
+           !e_html_editor_web_extension_get_dont_save_history_in_body_input (extension))
                dom_check_magic_smileys (document, extension);
 
+       e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, FALSE);
+
        if (e_html_editor_web_extension_get_return_key_pressed (extension) ||
            e_html_editor_web_extension_get_space_key_pressed (extension)) {
                dom_check_magic_links (document, extension, FALSE);
@@ -1811,7 +1935,7 @@ body_input_event_cb (WebKitDOMElement *element,
                        body = webkit_dom_document_get_body (document);
                        child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
 
-                       add_selection_markers_into_element_start (
+                       dom_add_selection_markers_into_element_start (
                                document,
                                WEBKIT_DOM_ELEMENT (child),
                                &selection_start_marker,
@@ -1855,21 +1979,34 @@ body_input_event_cb (WebKitDOMElement *element,
 
                        /* Wrap and quote the line */
                        if (!remove_quoting && text_length >= word_wrap_length) {
+                               EHTMLEditorHistoryEvent *ev;
+
                                dom_remove_quoting_from_element (block);
+                               dom_remove_wrapping_from_element (block);
 
                                block = dom_wrap_paragraph_length (document, extension, block, length);
                                webkit_dom_node_normalize (WEBKIT_DOM_NODE (block));
-                               quote_plain_text_element_after_wrapping (
+                               dom_quote_plain_text_element_after_wrapping (
                                        document, WEBKIT_DOM_ELEMENT (block), citation_level);
                                selection_start_marker = webkit_dom_document_query_selector (
                                        document, "span#-x-evo-selection-start-marker", NULL);
                                if (!selection_start_marker)
-                                       add_selection_markers_into_element_end (
+                                       dom_add_selection_markers_into_element_end (
                                                document,
                                                WEBKIT_DOM_ELEMENT (block),
                                                NULL,
                                                NULL);
 
+                               /* The content was wrapped and the coordinates
+                                * of caret could be changed, so renew them. */
+                               ev = e_html_editor_undo_redo_manager_get_current_history_event (manager);
+                               dom_selection_get_coordinates (
+                                       document,
+                                       &ev->after.start.x,
+                                       &ev->after.start.y,
+                                       &ev->after.end.x,
+                                       &ev->after.end.y);
+
                                dom_selection_restore (document);
                                dom_force_spell_check_for_current_paragraph (document, extension);
                                return;
@@ -1881,7 +2018,7 @@ body_input_event_cb (WebKitDOMElement *element,
 
 static void
 remove_input_event_listener_from_body (WebKitDOMDocument *document,
-                                       EHTMLEditorWebExtension *extension)
+                                      EHTMLEditorWebExtension *extension)
 {
        if (!e_html_editor_web_extension_get_body_input_event_removed (extension)) {
                webkit_dom_event_target_remove_event_listener (
@@ -1897,7 +2034,7 @@ remove_input_event_listener_from_body (WebKitDOMDocument *document,
 
 static void
 register_input_event_listener_on_body (WebKitDOMDocument *document,
-                                       EHTMLEditorWebExtension *extension)
+                                      EHTMLEditorWebExtension *extension)
 {
        if (e_html_editor_web_extension_get_body_input_event_removed (extension)) {
                webkit_dom_event_target_add_event_listener (
@@ -2046,7 +2183,7 @@ body_keyup_event_cb (WebKitDOMElement *element,
                                                document, extension, block, length);
                                        webkit_dom_node_normalize (WEBKIT_DOM_NODE (block));
                                }
-                               quote_plain_text_element_after_wrapping (
+                               dom_quote_plain_text_element_after_wrapping (
                                        document, block, level);
                        }
                }
@@ -2115,6 +2252,8 @@ dom_quote_and_insert_text_into_selection (WebKitDOMDocument *document,
                                           EHTMLEditorWebExtension *extension,
                                           const gchar *text)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        gchar *escaped_text;
        WebKitDOMElement *blockquote, *element, *selection_start;
        WebKitDOMNode *sibling;
@@ -2157,6 +2296,22 @@ dom_quote_and_insert_text_into_selection (WebKitDOMDocument *document,
 
        dom_selection_save (document);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_PASTE_QUOTED;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (text);
+       }
+
        selection_start = webkit_dom_document_get_element_by_id (
                document, "-x-evo-selection-start-marker");
        sibling = webkit_dom_node_get_previous_sibling (WEBKIT_DOM_NODE (selection_start));
@@ -2187,8 +2342,20 @@ dom_quote_and_insert_text_into_selection (WebKitDOMDocument *document,
 
        dom_restore_caret_position (document);
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_force_spell_check_for_current_paragraph (document, extension);
 
+       e_html_editor_web_extension_set_content_changed (extension);
+
        g_free (escaped_text);
 }
 
@@ -2254,7 +2421,7 @@ dom_change_quoted_block_to_normal (WebKitDOMDocument *document,
                webkit_dom_element_set_id (
                        WEBKIT_DOM_ELEMENT (block), "-x-evo-to-remove");
 
-               paragraph = insert_new_line_into_citation (document, extension, inner_html);
+               paragraph = dom_insert_new_line_into_citation (document, extension, inner_html);
                g_free (inner_html);
 
                if (paragraph) {
@@ -2367,7 +2534,7 @@ dom_change_quoted_block_to_normal (WebKitDOMDocument *document,
 
                block = dom_wrap_paragraph_length (document, extension, block, length);
                webkit_dom_node_normalize (WEBKIT_DOM_NODE (block));
-               quote_plain_text_element_after_wrapping (document, block, citation_level - 1);
+               dom_quote_plain_text_element_after_wrapping (document, block, citation_level - 1);
 
        }
 
@@ -2468,8 +2635,8 @@ insert_quote_symbols (WebKitDOMHTMLElement *element,
 
 static void
 quote_node (WebKitDOMDocument *document,
-           WebKitDOMNode *node,
-           gint quote_level)
+            WebKitDOMNode *node,
+            gint quote_level)
 {
        gboolean skip_first = FALSE;
        gboolean insert_newline = FALSE;
@@ -2629,9 +2796,9 @@ quote_br_node (WebKitDOMNode *node,
 
 static void
 quote_plain_text_recursive (WebKitDOMDocument *document,
-                           WebKitDOMNode *node,
-                           WebKitDOMNode *start_node,
-                           gint quote_level)
+                            WebKitDOMNode *node,
+                            WebKitDOMNode *start_node,
+                            gint quote_level)
 {
        gboolean skip_node = FALSE;
        gboolean move_next = FALSE;
@@ -2738,7 +2905,7 @@ quote_plain_text_recursive (WebKitDOMDocument *document,
                }
 
                /* If element doesn't have children, we can quote it */
-               if (is_citation_node (node)) {
+               if (dom_node_is_citation_node (node)) {
                        /* Citation with just text inside */
                        quote_node (document, node, quote_level + 1);
                        /* Set citation as quoted */
@@ -2757,7 +2924,7 @@ quote_plain_text_recursive (WebKitDOMDocument *document,
                        }
                        goto not_br;
                } else if (element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-first-br") ||
-                          element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-last-br")) {
+                          element_has_class (WEBKIT_DOM_ELEMENT (node), "-x-evo-last-br")) {
                        quote_br_node (node, quote_level);
                        node = next_sibling;
                        skip_node = TRUE;
@@ -2795,7 +2962,7 @@ quote_plain_text_recursive (WebKitDOMDocument *document,
                        if (WEBKIT_DOM_IS_HTML_DIV_ELEMENT (parent) ||
                            WEBKIT_DOM_IS_HTML_PRE_ELEMENT (parent) ||
                            (WEBKIT_DOM_IS_HTML_QUOTE_ELEMENT (parent) &&
-                            !is_citation_node (parent))) {
+                            !dom_node_is_citation_node (parent))) {
                                insert_quote_symbols_before_node (
                                        document, node, quote_level, FALSE);
 
@@ -2818,7 +2985,7 @@ quote_plain_text_recursive (WebKitDOMDocument *document,
                        g_free (text_content);
                }
 
-               if (is_citation_node (prev_sibling)) {
+               if (dom_node_is_citation_node (prev_sibling)) {
                        insert_quote_symbols_before_node (
                                document, node, quote_level, FALSE);
                        goto next_node;
@@ -2852,7 +3019,7 @@ quote_plain_text_recursive (WebKitDOMDocument *document,
                goto next_node;
 
  with_children:
-               if (is_citation_node (node)) {
+               if (dom_node_is_citation_node (node)) {
                        /* Go deeper and increase level */
                        quote_plain_text_recursive (
                                document, node, start_node, quote_level + 1);
@@ -2915,7 +3082,7 @@ dom_quote_plain_text_element (WebKitDOMDocument *document,
                document, element_clone, element_clone, level);
 
        /* Set citation as quoted */
-       if (is_citation_node (element_clone))
+       if (dom_node_is_citation_node (element_clone))
                element_add_class (
                        WEBKIT_DOM_ELEMENT (element_clone),
                        "-x-evo-plaintext-quoted");
@@ -3037,7 +3204,7 @@ dom_dequote_plain_text (WebKitDOMDocument *document)
 
                element = WEBKIT_DOM_ELEMENT (webkit_dom_node_list_item (paragraphs, ii));
 
-               if (is_citation_node (WEBKIT_DOM_NODE (element))) {
+               if (dom_node_is_citation_node (WEBKIT_DOM_NODE (element))) {
                        element_remove_class (element, "-x-evo-plaintext-quoted");
                        dom_remove_quoting_from_element (element);
                }
@@ -3170,7 +3337,7 @@ create_and_append_new_paragraph (WebKitDOMDocument *document,
 static void
 append_citation_mark (WebKitDOMDocument *document,
                       WebKitDOMElement *parent,
-                     const gchar *citation_mark_text)
+                      const gchar *citation_mark_text)
 {
        WebKitDOMText *text;
 
@@ -3386,12 +3553,12 @@ parse_html_into_paragraphs (WebKitDOMDocument *document,
                                gchar *html;
                                gchar *content_to_append;
 
-                               if (!paragraph) {
-                                      if (!block || WEBKIT_DOM_IS_HTML_DIV_ELEMENT (block))
-                                               paragraph = dom_get_paragraph_element (document, extension, 
-1, 0);
-                                       else
-                                               paragraph = WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node 
(block, FALSE));
-                               }
+                              if (!paragraph) {
+                                     if (!block || WEBKIT_DOM_IS_HTML_DIV_ELEMENT (block))
+                                              paragraph = dom_get_paragraph_element (document, extension, 
-1, 0);
+                                      else
+                                              paragraph = WEBKIT_DOM_ELEMENT (webkit_dom_node_clone_node 
(block, FALSE));
+                              }
 
                                html = webkit_dom_html_element_get_inner_html (
                                        WEBKIT_DOM_HTML_ELEMENT (paragraph));
@@ -3653,7 +3820,7 @@ quote_plain_text_elements_after_wrapping_in_document (WebKitDOMDocument *documen
 
                child = webkit_dom_node_list_item (list, ii);
                citation_level = get_citation_level (child, TRUE);
-               quote_plain_text_element_after_wrapping (
+               dom_quote_plain_text_element_after_wrapping (
                        document, WEBKIT_DOM_ELEMENT (child), citation_level);
                g_object_unref (child);
        }
@@ -3966,7 +4133,7 @@ dom_convert_content (WebKitDOMDocument *document,
                remove_node (WEBKIT_DOM_NODE (paragraph));
                child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (body));
                if (child)
-                       add_selection_markers_into_element_start (
+                       dom_add_selection_markers_into_element_start (
                                document, WEBKIT_DOM_ELEMENT (child), NULL, NULL);
        }
 
@@ -4007,6 +4174,8 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                                             const gchar *html,
                                             gboolean is_html)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
        gboolean has_selection;
        gchar *inner_html;
        gint citation_level;
@@ -4025,6 +4194,24 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
        if (WEBKIT_DOM_IS_HTML_BODY_ELEMENT (current_block))
                current_block = NULL;
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_PASTE;
+/* FIXME WK2
+               ev->type = HISTORY_PASTE_AS_TEXT;*/
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (html);
+       }
+
        element = webkit_dom_document_create_element (document, "div", NULL);
        if (is_html) {
                gchar *inner_text;
@@ -4087,7 +4274,7 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                                dom_wrap_paragraph_length (
                                        document, extension, WEBKIT_DOM_ELEMENT (parent), length));
                        webkit_dom_node_normalize (parent);
-                       quote_plain_text_element_after_wrapping (
+                       dom_quote_plain_text_element_after_wrapping (
                                document, WEBKIT_DOM_ELEMENT (parent), citation_level);
 
                        goto delete;
@@ -4133,12 +4320,12 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                /* Caret will be restored on the end of pasted text */
                webkit_dom_node_append_child (
                        last_paragraph,
-                       WEBKIT_DOM_NODE (create_selection_marker (document, TRUE)),
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)),
                        NULL);
 
                webkit_dom_node_append_child (
                        last_paragraph,
-                       WEBKIT_DOM_NODE (create_selection_marker (document, FALSE)),
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)),
                        NULL);
 
                /* Insert the paragraph with the end of the pasted text after
@@ -4153,7 +4340,7 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                while ((child = webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (element)))) {
                        child = WEBKIT_DOM_NODE (dom_wrap_paragraph_length (
                                document, extension, WEBKIT_DOM_ELEMENT (child), length));
-                       quote_plain_text_element_after_wrapping (
+                       dom_quote_plain_text_element_after_wrapping (
                                document, WEBKIT_DOM_ELEMENT (child), citation_level);
                        webkit_dom_node_insert_before (
                                webkit_dom_node_get_parent_node (last_paragraph),
@@ -4167,7 +4354,7 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                last_paragraph = WEBKIT_DOM_NODE (
                        dom_wrap_paragraph_length (
                                document, extension, WEBKIT_DOM_ELEMENT (last_paragraph), length));
-               quote_plain_text_element_after_wrapping (
+               dom_quote_plain_text_element_after_wrapping (
                        document, WEBKIT_DOM_ELEMENT (last_paragraph), citation_level);
 
                dom_remove_quoting_from_element (WEBKIT_DOM_ELEMENT (parent));
@@ -4177,7 +4364,7 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                        WEBKIT_DOM_NODE (selection_start_marker));
                parent = WEBKIT_DOM_NODE (dom_wrap_paragraph_length (
                        document, extension, WEBKIT_DOM_ELEMENT (parent), length));
-               quote_plain_text_element_after_wrapping (
+               dom_quote_plain_text_element_after_wrapping (
                        document, WEBKIT_DOM_ELEMENT (parent), citation_level);
 
                /* If the pasted text begun or ended with a new line we have to
@@ -4202,11 +4389,21 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                        webkit_dom_element_remove_attribute (br, "class");
                }
  delete:
+               if (ev) {
+                       dom_selection_get_coordinates (
+                               document,
+                               &ev->after.start.x,
+                               &ev->after.start.y,
+                               &ev->after.end.x,
+                               &ev->after.end.y);
+                       e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+               }
+
                dom_selection_restore (document);
                /* Remove the text that was meant to be replaced by the pasted text */
                if (has_selection)
                        dom_exec_command (
-                               document, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
+                               document, extension, E_HTML_EDITOR_VIEW_COMMAND_DELETE, NULL);
 
                g_object_unref (element);
                goto out;
@@ -4218,13 +4415,13 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
        inner_html = webkit_dom_html_element_get_inner_html (
                WEBKIT_DOM_HTML_ELEMENT (element));
        dom_exec_command (
-               document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html);
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, inner_html);
        g_free (inner_html);
 
        inner_html = webkit_dom_html_element_get_inner_text (
                WEBKIT_DOM_HTML_ELEMENT (element));
        if (g_str_has_suffix (inner_html, " "))
-               dom_exec_command (document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, " ");
+               dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, " ");
 
        g_free (inner_html);
 
@@ -4357,6 +4554,16 @@ dom_convert_and_insert_html_into_selection (WebKitDOMDocument *document,
                        NULL);
        }
 
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
        dom_selection_restore (document);
  out:
        dom_force_spell_check (document, extension);
@@ -5238,7 +5445,7 @@ process_elements (EHTMLEditorWebExtension *extension,
                                        if (webkit_dom_node_get_next_sibling (parent)) {
                                                parent = webkit_dom_node_get_parent_node (parent);
 
-                                               if (is_citation_node (parent))
+                                               if (dom_node_is_citation_node (parent))
                                                        g_string_append (buffer, changing_mode ? "<br>" : 
"\n");
                                        }
                                }
@@ -5569,7 +5776,7 @@ convert_element_from_html_to_plain_text (WebKitDOMDocument *document,
                restore = input_start ? TRUE : FALSE;
 
                if (input_start)
-                       add_selection_markers_into_element_start (
+                       dom_add_selection_markers_into_element_start (
                                document, WEBKIT_DOM_ELEMENT (input_start), NULL, NULL);
                from = WEBKIT_DOM_NODE (main_blockquote);
        } else {
@@ -5638,7 +5845,7 @@ convert_element_from_html_to_plain_text (WebKitDOMDocument *document,
                                        "<br>",
                                        NULL);
                        }
-                       add_selection_markers_into_element_start (
+                       dom_add_selection_markers_into_element_start (
                                document, WEBKIT_DOM_ELEMENT (first_child), NULL, NULL);
                }
        }
@@ -6062,7 +6269,7 @@ dom_process_content_after_load (WebKitDOMDocument *document,
        /* Don't use CSS when possible to preserve compatibility with older
         * versions of Evolution or other MUAs */
        dom_exec_command (
-               document, E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "false");
+               document, extension, E_HTML_EDITOR_VIEW_COMMAND_STYLE_WITH_CSS, "false");
 
        body = webkit_dom_document_get_body (document);
 
@@ -6234,11 +6441,30 @@ dom_insert_html (WebKitDOMDocument *document,
                  EHTMLEditorWebExtension *extension,
                  const gchar *html_text)
 {
+       EHTMLEditorHistoryEvent *ev = NULL;
+       EHTMLEditorUndoRedoManager *manager;
+
        g_return_if_fail (html_text != NULL);
 
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       if (!e_html_editor_undo_redo_manager_is_operation_in_progress (manager)) {
+               ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+               ev->type = HISTORY_INSERT_HTML;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->before.start.x,
+                       &ev->before.start.y,
+                       &ev->before.end.x,
+                       &ev->before.end.y);
+
+               ev->data.string.from = NULL;
+               ev->data.string.to = g_strdup (html_text);
+       }
+
        if (e_html_editor_web_extension_get_html_mode (extension)) {
                dom_exec_command (
-                       document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html_text);
+                       document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_HTML, html_text);
                if (strstr (html_text, "id=\"-x-evo-selection-start-marker\""))
                        dom_selection_restore (document);
                dom_check_magic_links (document, extension, FALSE);
@@ -6246,6 +6472,17 @@ dom_insert_html (WebKitDOMDocument *document,
                dom_scroll_to_caret (document);
        } else
                dom_convert_and_insert_html_into_selection (document, extension, html_text, TRUE);
+
+       if (ev) {
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
 }
 
 static gboolean
@@ -6364,6 +6601,153 @@ fix_structure_after_delete_before_quoted_content (WebKitDOMDocument *document)
        return FALSE;
 }
 
+static void
+save_history_for_delete_or_backspace (WebKitDOMDocument *document,
+                                      EHTMLEditorWebExtension *extension,
+                                      gboolean delete_key)
+{
+       EHTMLEditorHistoryEvent *ev;
+       EHTMLEditorUndoRedoManager *manager;
+       WebKitDOMDocumentFragment *fragment;
+       WebKitDOMDOMWindow *dom_window;
+       WebKitDOMDOMSelection *dom_selection;
+       WebKitDOMRange *range;
+
+       dom_window = webkit_dom_document_get_default_view (document);
+       dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+       if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+               return;
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       ev->type = HISTORY_DELETE;
+
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+       dom_selection_get_coordinates (
+               document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+       range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+
+       if (webkit_dom_range_get_collapsed (range, NULL)) {
+               WebKitDOMNode *node = webkit_dom_range_get_end_container (range, NULL);
+
+               if (delete_key && !webkit_dom_node_get_next_sibling (node)) {
+                       g_free (ev);
+                       return;
+               }
+
+               if (delete_key)
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "right", "character");
+               else
+                       webkit_dom_dom_selection_modify (dom_selection, "extend", "left", "character");
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+
+               if (!webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment))) {
+                       g_free (ev);
+                       return;
+               }
+
+               if (delete_key) {
+                       webkit_dom_dom_selection_collapse_to_start (dom_selection, NULL);
+
+                       dom_selection_get_coordinates (
+                               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+               } else {
+                       dom_selection_get_coordinates (
+                               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, 
&ev->after.end.y);
+
+                       webkit_dom_dom_selection_collapse_to_end (dom_selection, NULL);
+               }
+
+               if (!delete_key) {
+                       ev->after.end.x = ev->after.start.x;
+                       ev->after.end.y = ev->after.start.y;
+               }
+
+               if (delete_key) {
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, FALSE)),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+                       webkit_dom_node_insert_before (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, TRUE)),
+                               webkit_dom_node_get_first_child (WEBKIT_DOM_NODE (fragment)),
+                               NULL);
+               } else {
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, TRUE)),
+                               NULL);
+                       webkit_dom_node_append_child (
+                               WEBKIT_DOM_NODE (fragment),
+                               WEBKIT_DOM_NODE (
+                                       dom_create_selection_marker (document, FALSE)),
+                               NULL);
+               }
+       } else {
+               ev->after.start.x = ev->before.start.x;
+               ev->after.start.y = ev->before.start.y;
+               ev->after.end.x = ev->before.end.x;
+               ev->after.end.y = ev->before.end.y;
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+       }
+
+       ev->data.fragment = fragment;
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+}
+
+static gboolean
+split_citation (WebKitDOMDocument *document,
+                EHTMLEditorWebExtension *extension)
+{
+       EHTMLEditorHistoryEvent *ev;
+       EHTMLEditorUndoRedoManager *manager;
+       WebKitDOMElement *element;
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       ev->type = HISTORY_CITATION_SPLIT;
+
+       dom_selection_get_coordinates (
+               document, &ev->before.start.x, &ev->before.start.y, &ev->before.end.x, &ev->before.end.y);
+
+       if (!dom_selection_is_collapsed (document)) {
+               WebKitDOMDocumentFragment *fragment;
+               WebKitDOMDOMWindow *dom_window;
+               WebKitDOMDOMSelection *dom_selection;
+               WebKitDOMRange *range;
+
+               dom_window = webkit_dom_document_get_default_view (document);
+               dom_selection = webkit_dom_dom_window_get_selection (dom_window);
+
+               if (!webkit_dom_dom_selection_get_range_count (dom_selection))
+                       return FALSE;
+
+               range = webkit_dom_dom_selection_get_range_at (dom_selection, 0, NULL);
+               fragment = webkit_dom_range_clone_contents (range, NULL);
+
+               ev->data.fragment = fragment;
+       } else
+               ev->data.fragment = NULL;
+
+       element = dom_insert_new_line_into_citation (document, extension, "");
+
+       dom_selection_get_coordinates (
+               document, &ev->after.start.x, &ev->after.start.y, &ev->after.end.x, &ev->after.end.y);
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+
+       return element != NULL;
+}
+
 static gboolean
 selection_is_in_table (WebKitDOMDocument *document,
                        gboolean *first_cell,
@@ -6562,18 +6946,77 @@ is_return_key (guint key_val)
            (key_val == GDK_KEY_KP_Enter));
 }
 
+static gboolean
+insert_tabulator (WebKitDOMDocument *document,
+                  EHTMLEditorWebExtension *extension)
+{
+       gboolean success;
+       EHTMLEditorHistoryEvent *ev;
+
+       ev = g_new0 (EHTMLEditorHistoryEvent, 1);
+       ev->type = HISTORY_INPUT;
+
+       dom_selection_get_coordinates (
+               document,
+               &ev->before.start.x,
+               &ev->before.start.y,
+               &ev->before.end.x,
+               &ev->before.end.y);
+
+       success = dom_exec_command (document, extension, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+
+       if (success) {
+               EHTMLEditorUndoRedoManager *manager;
+               WebKitDOMElement *element;
+               WebKitDOMDocumentFragment *fragment;
+
+               dom_selection_get_coordinates (
+                       document,
+                       &ev->after.start.x,
+                       &ev->after.start.y,
+                       &ev->after.end.x,
+                       &ev->after.end.y);
+
+               fragment = webkit_dom_document_create_document_fragment (document);
+               element = webkit_dom_document_create_element (document, "span", NULL);
+               webkit_dom_html_element_set_inner_text (
+                       WEBKIT_DOM_HTML_ELEMENT (element), "\t", NULL);
+               webkit_dom_element_set_attribute (
+                       element, "class", "Apple-tab-span", NULL);
+               webkit_dom_element_set_attribute (
+                       element, "style", "white-space:pre", NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment), WEBKIT_DOM_NODE (element), NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, TRUE)),
+                       NULL);
+               webkit_dom_node_append_child (
+                       WEBKIT_DOM_NODE (fragment),
+                       WEBKIT_DOM_NODE (dom_create_selection_marker (document, FALSE)),
+                       NULL);
+               ev->data.fragment = fragment;
+
+               manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+               e_html_editor_undo_redo_manager_insert_history_event (manager, ev);
+       }
+
+       return success;
+}
+
 gboolean
 dom_process_on_key_press (WebKitDOMDocument *document,
                           EHTMLEditorWebExtension *extension,
                           guint key_val)
 {
+       e_html_editor_web_extension_set_dont_save_history_in_body_input (extension, FALSE);
+
        if (key_val == GDK_KEY_Tab || key_val == GDK_KEY_ISO_Left_Tab) {
                if (jump_to_next_table_cell (document, key_val == GDK_KEY_ISO_Left_Tab))
                        return TRUE;
 
                if (key_val == GDK_KEY_Tab)
-                       return dom_exec_command (
-                               document, E_HTML_EDITOR_VIEW_COMMAND_INSERT_TEXT, "\t");
+                       return insert_tabulator (document, extension);
                else
                        return FALSE;
        }
@@ -6598,7 +7041,7 @@ dom_process_on_key_press (WebKitDOMDocument *document,
                                        WEBKIT_DOM_NODE (webkit_dom_document_create_element (
                                                document, "br", NULL)),
                                        NULL);
-                               add_selection_markers_into_element_start (
+                               dom_add_selection_markers_into_element_start (
                                        document, WEBKIT_DOM_ELEMENT (node), NULL, NULL);
                                webkit_dom_node_insert_before (
                                        webkit_dom_node_get_parent_node (table),
@@ -6615,7 +7058,7 @@ dom_process_on_key_press (WebKitDOMDocument *document,
                 * the special command to do it. */
                if (dom_selection_is_citation (document)) {
                        remove_input_event_listener_from_body (document, extension);
-                       return (insert_new_line_into_citation (document, extension, "")) ? TRUE : FALSE;
+                       return split_citation (document, extension);
                }
 
                /* When the return is pressed in a H1-6 element, WebKit doesn't
@@ -6675,6 +7118,7 @@ dom_process_on_key_press (WebKitDOMDocument *document,
        if (key_val == GDK_KEY_Delete || key_val == GDK_KEY_BackSpace) {
                gboolean html_mode;
 
+               save_history_for_delete_or_backspace (document, extension, key_val == GDK_KEY_Delete);
                html_mode = e_html_editor_web_extension_get_html_mode (extension);
                if (key_val == GDK_KEY_BackSpace && !html_mode) {
                        if (delete_character_from_quoted_line_start (document))
@@ -6717,6 +7161,7 @@ void
 dom_process_content_after_mode_change (WebKitDOMDocument *document,
                                        EHTMLEditorWebExtension *extension)
 {
+       EHTMLEditorUndoRedoManager *manager;
        gboolean html_mode;
        WebKitDOMElement *blockquote;
 
@@ -6762,6 +7207,9 @@ dom_process_content_after_mode_change (WebKitDOMDocument *document,
 
                g_free (plain);
        }
+
+       manager = e_html_editor_web_extension_get_undo_redo_manager (extension);
+       e_html_editor_undo_redo_manager_clean_history (manager);
 }
 
 gint
diff --git a/web-extensions/e-html-editor-view-dom-functions.h 
b/web-extensions/e-html-editor-view-dom-functions.h
index 1c44069..d0e4960 100644
--- a/web-extensions/e-html-editor-view-dom-functions.h
+++ b/web-extensions/e-html-editor-view-dom-functions.h
@@ -24,10 +24,12 @@
 #include "e-html-editor-web-extension.h"
 
 #include <e-util/e-util-enums.h>
+#include <e-util/e-emoticon.h>
 
 G_BEGIN_DECLS
 
 gboolean       dom_exec_command                (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
                                                 EHTMLEditorViewCommand command,
                                                 const gchar *value);
 
@@ -54,6 +56,13 @@ void         dom_check_magic_links           (WebKitDOMDocument *document,
                                                 EHTMLEditorWebExtension *extension,
                                                 gboolean include_space_by_user);
 
+void           dom_insert_smiley               (WebKitDOMDocument *document,
+                                                 EHTMLEditorWebExtension *extension,
+                                                 EEmoticon *emoticon);
+
+void           dom_check_magic_smileys         (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension);
+
 void           dom_convert_content             (WebKitDOMDocument *document,
                                                 EHTMLEditorWebExtension *extension,
                                                 const gchar *preferred_text);
@@ -64,6 +73,22 @@ void         dom_convert_and_insert_html_into_selection
                                                 const gchar *html,
                                                 gboolean is_html);
 
+gboolean       dom_node_is_citation_node       (WebKitDOMNode *node);
+
+void           dom_quote_plain_text_element_after_wrapping
+                                               (WebKitDOMDocument *document,
+                                                WebKitDOMElement *element,
+                                                gint quote_level);
+
+WebKitDOMNode * get_parent_block_node_from_child
+                                               (WebKitDOMNode *node);
+
+WebKitDOMElement *
+               dom_insert_new_line_into_citation
+                                               (WebKitDOMDocument *document,
+                                                EHTMLEditorWebExtension *extension,
+                                                const gchar *html_to_insert);
+
 WebKitDOMElement *
                dom_quote_plain_text_element    (WebKitDOMDocument *document,
                                                 WebKitDOMElement *element);
diff --git a/web-extensions/e-html-editor-web-extension.c b/web-extensions/e-html-editor-web-extension.c
index ec50281..e5c065c 100644
--- a/web-extensions/e-html-editor-web-extension.c
+++ b/web-extensions/e-html-editor-web-extension.c
@@ -1,5 +1,5 @@
 /*
- * e-html-editor-web-extension.h
+ * e-html-editor-web-extension.c
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -39,6 +39,7 @@
 #include "e-html-editor-hrule-dialog-dom-functions.h"
 #include "e-html-editor-image-dialog-dom-functions.h"
 #include "e-html-editor-link-dialog-dom-functions.h"
+#include "e-html-editor-page-dialog-dom-functions.h"
 #include "e-html-editor-selection-dom-functions.h"
 #include "e-html-editor-spell-check-dialog-dom-functions.h"
 #include "e-html-editor-table-dialog-dom-functions.h"
@@ -91,11 +92,14 @@ struct _EHTMLEditorWebExtensionPrivate {
        gboolean is_message_from_edit_as_new;
        gboolean is_message_from_selection;
        gboolean remove_initial_input_line;
+       gboolean dont_save_history_in_body_input;
 
        GHashTable *inline_images;
 
        WebKitDOMNode *node_under_mouse_click;
        guint node_under_mouse_click_flags;
+
+       EHTMLEditorUndoRedoManager *undo_redo_manager;
 };
 
 static CamelDataCache *emd_global_http_cache = NULL;
@@ -199,6 +203,9 @@ static const char introspection_xml[] =
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='element_id' direction='in'/>"
 "    </method>"
+"    <method name='EHTMLEditorCellDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
 "    <method name='EHTMLEditorCellDialogSetElementVAlign'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='value' direction='in'/>"
@@ -246,6 +253,9 @@ static const char introspection_xml[] =
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='b' name='created_new_hr' direction='out'/>"
 "    </method>"
+"    <method name='EHTMLEditorHRuleDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
 "    <method name='HRElementSetNoShade'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='element_id' direction='in'/>"
@@ -259,6 +269,12 @@ static const char introspection_xml[] =
 "<!-- ********************************************************* -->"
 "<!--     Functions that are used in EHTMLEditorImageDialog     -->"
 "<!-- ********************************************************* -->"
+"    <method name='EHTMLEditorImageDialogMarkImage'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EHTMLEditorImageDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
 "    <method name='EHTMLEditorImageDialogSetElementUrl'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='value' direction='in'/>"
@@ -326,6 +342,15 @@ static const char introspection_xml[] =
 "      <arg type='s' name='inner_text' direction='in'/>"
 "    </method>"
 "<!-- ********************************************************* -->"
+"<!--     Functions that are used in EHTMLEditorPageDialog     -->"
+"<!-- ********************************************************* -->"
+"    <method name='EHTMLEditorPageDialogSaveHistory'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"    <method name='EHTMLEditorPageDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
 "<!--   Functions that are used in EHTMLEditorSpellCheckDialog  -->"
 "<!-- ********************************************************* -->"
 "    <method name='EHTMLEditorSpellCheckDialogNext'>"
@@ -361,6 +386,12 @@ static const char introspection_xml[] =
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='b' name='created_new_table' direction='out'/>"
 "    </method>"
+"    <method name='EHTMLEditorTableDialogSaveHistoryOnExit'>"
+"      <arg type='t' name='page_id' direction='in'/>"
+"    </method>"
+"<!-- ********************************************************* -->"
+"<!--     Functions that are used in EHTMLEditorActions         -->"
+"<!-- ********************************************************* -->"
 "    <method name='TableCellElementGetNoWrap'>"
 "      <arg type='t' name='page_id' direction='in'/>"
 "      <arg type='s' name='element_id' direction='in'/>"
@@ -764,7 +795,19 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_cell_dialog_mark_current_cell_element (document, element_id);
+               e_html_editor_cell_dialog_mark_current_cell_element (document, extension, element_id);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EHTMLEditorCellDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_cell_dialog_save_history_on_exit (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorCellDialogSetElementVAlign") == 0) {
@@ -903,6 +946,18 @@ handle_method_call (GDBusConnection *connection,
 
                g_dbus_method_invocation_return_value (
                        invocation, g_variant_new_boolean (created_new_hr));
+       } else if (g_strcmp0 (method_name, "EHTMLEditorHRuleDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_hrule_dialog_save_history_on_exit (document, extension);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "HRElementSetNoShade") == 0) {
                gboolean value = FALSE;
                const gchar *element_id;
@@ -944,6 +999,31 @@ handle_method_call (GDBusConnection *connection,
 
                g_dbus_method_invocation_return_value (
                        invocation, g_variant_new_boolean (value));
+       } else if (g_strcmp0 (method_name, "EHTMLEditorImageDialogMarkImage") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_image_dialog_mark_image (
+                       document, extension, extension->priv->node_under_mouse_click);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EHTMLEditorImageDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_image_dialog_save_history_on_exit (document, extension);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorImageDialogSetElementUrl") == 0) {
                const gchar *value;
 
@@ -1191,7 +1271,31 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_link_dialog_ok (document, url, inner_text);
+               e_html_editor_link_dialog_ok (document, extension, url, inner_text);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EHTMLEditorPageDialogSaveHistory") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_page_dialog_save_history (document, extension);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "EHTMLEditorPageDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_page_dialog_save_history_on_exit (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorSpellCheckDialogNext") == 0) {
@@ -1301,6 +1405,18 @@ handle_method_call (GDBusConnection *connection,
 
                g_dbus_method_invocation_return_value (
                        invocation, g_variant_new_boolean (created_new_table));
+       } else if (g_strcmp0 (method_name, "EHTMLEditorTableDialogSaveHistoryOnExit") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               e_html_editor_table_dialog_save_history_on_exit (document, extension);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDeleteCellContents") == 0) {
                g_variant_get (parameters, "(t)", &page_id);
 
@@ -1310,7 +1426,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_delete_cell_contents (document);
+               e_html_editor_dialog_delete_cell_contents (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDeleteColumn") == 0) {
@@ -1322,7 +1438,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_delete_column (document);
+               e_html_editor_dialog_delete_column (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDeleteRow") == 0) {
@@ -1334,7 +1450,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_delete_row (document);
+               e_html_editor_dialog_delete_row (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDeleteTable") == 0) {
@@ -1346,7 +1462,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_delete_table (document);
+               e_html_editor_dialog_delete_table (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDeleteTable") == 0) {
@@ -1358,7 +1474,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_delete_cell_contents (document);
+               e_html_editor_dialog_delete_cell_contents (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogInsertColumnAfter") == 0) {
@@ -1370,7 +1486,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_insert_column_after (document);
+               e_html_editor_dialog_insert_column_after (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogInsertColumnBefore") == 0) {
@@ -1382,7 +1498,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_insert_column_before (document);
+               e_html_editor_dialog_insert_column_before (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogInsertRowAbove") == 0) {
@@ -1394,7 +1510,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_insert_row_above (document);
+               e_html_editor_dialog_insert_row_above (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogInsertRowBelow") == 0) {
@@ -1406,7 +1522,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               e_html_editor_dialog_insert_row_below (document);
+               e_html_editor_dialog_insert_row_below (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "EHTMLEditorDialogDOMUnlink") == 0) {
@@ -1418,7 +1534,7 @@ handle_method_call (GDBusConnection *connection,
                        return;
 
                document = webkit_web_page_get_dom_document (web_page);
-               dom_unlink (document);
+               dom_selection_unlink (document, extension);
 
                g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "TableCellElementGetNoWrap") == 0) {
@@ -1809,6 +1925,17 @@ handle_method_call (GDBusConnection *connection,
                document = webkit_web_page_get_dom_document (web_page);
                dom_selection_unindent (document, extension);
                g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "DOMSelectionWrap") == 0) {
+               g_variant_get (parameters, "(t)", &page_id);
+
+               web_page = get_webkit_web_page_or_return_dbus_error (
+                       invocation, web_extension, page_id);
+               if (!web_page)
+                       return;
+
+               document = webkit_web_page_get_dom_document (web_page);
+               dom_selection_wrap (document, extension);
+               g_dbus_method_invocation_return_value (invocation, NULL);
        } else if (g_strcmp0 (method_name, "DOMRemoveSignatures") == 0) {
                gboolean top_signature;
                gchar *active_signature;
@@ -2343,6 +2470,12 @@ e_html_editor_web_extension_dispose (GObject *object)
                extension->priv->text = NULL;
        }
 
+       if (extension->priv->undo_redo_manager != NULL) {
+               g_object_unref (extension->priv->undo_redo_manager);
+               extension->priv->undo_redo_manager = NULL;
+               extension->priv->text = NULL;
+       }
+
        g_clear_object (&extension->priv->wk_extension);
 
        g_hash_table_remove_all (extension->priv->inline_images);
@@ -2412,9 +2545,15 @@ e_html_editor_web_extension_init (EHTMLEditorWebExtension *extension)
        extension->priv->is_from_new_message = FALSE;
        extension->priv->is_message_from_selection = FALSE;
        extension->priv->remove_initial_input_line = FALSE;
+       extension->priv->dont_save_history_in_body_input = FALSE;
 
        extension->priv->node_under_mouse_click = NULL;
 
+       extension->priv->undo_redo_manager = g_object_new (
+               E_TYPE_HTML_EDITOR_UNDO_REDO_MANAGER,
+               "html-editor-web-extension", extension->priv->undo_redo_manager,
+               NULL);
+
        extension->priv->inline_images = g_hash_table_new_full (
                g_str_hash, g_str_equal,
                (GDestroyNotify) g_free,
@@ -2544,6 +2683,9 @@ web_page_document_loaded_cb (WebKitWebPage *web_page,
 
        document = webkit_web_page_get_dom_document (web_page);
 
+       e_html_editor_undo_redo_manager_set_document (
+               web_extension->priv->undo_redo_manager, document);
+
        dom_process_content_after_load (document, web_extension);
 }
 
@@ -2809,6 +2951,12 @@ e_html_editor_web_extension_set_space_key_pressed (EHTMLEditorWebExtension *exte
 }
 
 gboolean
+e_html_editor_web_extension_get_magic_links_enabled (EHTMLEditorWebExtension *extension)
+{
+       return extension->priv->magic_links;
+}
+
+gboolean
 e_html_editor_web_extension_get_magic_smileys_enabled (EHTMLEditorWebExtension *extension)
 {
        return extension->priv->magic_smileys;
@@ -2908,3 +3056,23 @@ e_html_editor_web_extension_set_is_smiley_written (EHTMLEditorWebExtension *exte
        extension->priv->smiley_written = value;
 }
 
+gboolean
+e_html_editor_web_extension_get_dont_save_history_in_body_input (EHTMLEditorWebExtension *extension)
+{
+       return extension->priv->dont_save_history_in_body_input;
+}
+
+void
+e_html_editor_web_extension_set_dont_save_history_in_body_input (EHTMLEditorWebExtension *extension,
+                                                                 gboolean value)
+{
+       extension->priv->dont_save_history_in_body_input = value;
+}
+
+EHTMLEditorUndoRedoManager *
+e_html_editor_web_extension_get_undo_redo_manager (EHTMLEditorWebExtension *extension)
+{
+       g_return_val_if_fail (E_IS_HTML_EDITOR_WEB_EXTENSION (extension), NULL);
+
+       return extension->priv->undo_redo_manager;
+}
diff --git a/web-extensions/e-html-editor-web-extension.h b/web-extensions/e-html-editor-web-extension.h
index ff64fa0..45a6d6f 100644
--- a/web-extensions/e-html-editor-web-extension.h
+++ b/web-extensions/e-html-editor-web-extension.h
@@ -23,6 +23,8 @@
 
 #include "e-html-editor-web-extension-names.h"
 
+#include "e-html-editor-undo-redo-manager.h"
+
 #include <e-util/e-util-enums.h>
 #include <webkit2/webkit-web-extension.h>
 #include <glib-object.h>
@@ -48,6 +50,8 @@
 
 G_BEGIN_DECLS
 
+struct _EHTMLEditorUndoRedoManager;
+
 typedef struct _EHTMLEditorWebExtension EHTMLEditorWebExtension;
 typedef struct _EHTMLEditorWebExtensionClass EHTMLEditorWebExtensionClass;
 typedef struct _EHTMLEditorWebExtensionPrivate EHTMLEditorWebExtensionPrivate;
@@ -135,6 +139,9 @@ void                e_html_editor_web_extension_set_space_key_pressed
                                                (EHTMLEditorWebExtension *extension,
                                                 gboolean value);
 
+gboolean       e_html_editor_web_extension_get_magic_links_enabled
+                                               (EHTMLEditorWebExtension *extension);
+
 gboolean       e_html_editor_web_extension_get_magic_smileys_enabled
                                                (EHTMLEditorWebExtension *extension);
 
@@ -181,6 +188,16 @@ gboolean   e_html_editor_web_extension_get_is_smiley_written
 
 void           e_html_editor_web_extension_set_is_smiley_written
                                                (EHTMLEditorWebExtension *extension,
-                                                 gboolean value);
+                                                gboolean value);
 
+gboolean       e_html_editor_web_extension_get_dont_save_history_in_body_input
+                                               (EHTMLEditorWebExtension *extension);
+
+void           e_html_editor_web_extension_set_dont_save_history_in_body_input
+                                               (EHTMLEditorWebExtension *extension,
+                                                gboolean value);
+
+struct _EHTMLEditorUndoRedoManager *
+               e_html_editor_web_extension_get_undo_redo_manager
+                                               (EHTMLEditorWebExtension *extension);
 #endif /* E_HTML_EDITOR_WEB_EXTENSION_H */


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