[evolution/wip/mcrha/webkit-jsc-api] Implement link dialog/editing



commit 92a0a9c247ffa8a02a605fafd63908225950a0f4
Author: Milan Crha <mcrha redhat com>
Date:   Fri Dec 6 15:33:08 2019 +0100

    Implement link dialog/editing

 data/webkit/e-editor.js                     | 292 +++++++++++++++++++++++++---
 data/webkit/e-undo-redo.js                  |  14 +-
 src/e-util/e-content-editor.c               |  20 +-
 src/e-util/e-content-editor.h               |   6 +-
 src/e-util/e-html-editor.c                  |  79 ++++----
 src/e-util/e-html-editor.h                  |   3 +-
 src/e-util/test-html-editor-units.c         |  16 +-
 src/modules/webkit-editor/e-webkit-editor.c | 265 ++++++++++++++++++-------
 8 files changed, 528 insertions(+), 167 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 3e9c567864..693adb0927 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -29,34 +29,43 @@ var EvoEditor = {
        EMAIL_PATTERN : "[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}" +
                        "[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*+",
 
-       E_CONTENT_EDITOR_ALIGNMENT_NONE : -1,
-       E_CONTENT_EDITOR_ALIGNMENT_LEFT : 0,
-       E_CONTENT_EDITOR_ALIGNMENT_CENTER : 1,
-       E_CONTENT_EDITOR_ALIGNMENT_RIGHT : 2,
-       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY : 3,
-
-       E_CONTENT_EDITOR_BLOCK_FORMAT_NONE : 0,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH : 1,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_PRE : 2,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS : 3,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H1 : 4,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H2 : 5,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H3 : 6,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H4 : 7,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H5 : 8,
-       E_CONTENT_EDITOR_BLOCK_FORMAT_H6 : 9,
+       E_CONTENT_EDITOR_ALIGNMENT_NONE         : -1,
+       E_CONTENT_EDITOR_ALIGNMENT_LEFT         : 0,
+       E_CONTENT_EDITOR_ALIGNMENT_CENTER       : 1,
+       E_CONTENT_EDITOR_ALIGNMENT_RIGHT        : 2,
+       E_CONTENT_EDITOR_ALIGNMENT_JUSTIFY      : 3,
+
+       E_CONTENT_EDITOR_BLOCK_FORMAT_NONE      : 0,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PARAGRAPH : 1,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_PRE       : 2,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_ADDRESS   : 3,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H1        : 4,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H2        : 5,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H3        : 6,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H4        : 7,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H5        : 8,
+       E_CONTENT_EDITOR_BLOCK_FORMAT_H6        : 9,
        E_CONTENT_EDITOR_BLOCK_FORMAT_UNORDERED_LIST : 10,
        E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST : 11,
        E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ROMAN : 12,
        E_CONTENT_EDITOR_BLOCK_FORMAT_ORDERED_LIST_ALPHA : 13,
 
-       E_CONTENT_EDITOR_GET_INLINE_IMAGES : 1 << 0,
-       E_CONTENT_EDITOR_GET_RAW_BODY_HTML : 1 << 1,
-       E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN : 1 << 2,
-       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED : 1 << 3,
-       E_CONTENT_EDITOR_GET_RAW_DRAFT : 1 << 4,
-       E_CONTENT_EDITOR_GET_TO_SEND_HTML : 1 << 5,
-       E_CONTENT_EDITOR_GET_TO_SEND_PLAIN : 1 << 6,
+       E_CONTENT_EDITOR_GET_INLINE_IMAGES      : 1 << 0,
+       E_CONTENT_EDITOR_GET_RAW_BODY_HTML      : 1 << 1,
+       E_CONTENT_EDITOR_GET_RAW_BODY_PLAIN     : 1 << 2,
+       E_CONTENT_EDITOR_GET_RAW_BODY_STRIPPED  : 1 << 3,
+       E_CONTENT_EDITOR_GET_RAW_DRAFT          : 1 << 4,
+       E_CONTENT_EDITOR_GET_TO_SEND_HTML       : 1 << 5,
+       E_CONTENT_EDITOR_GET_TO_SEND_PLAIN      : 1 << 6,
+
+       E_CONTENT_EDITOR_NODE_UNKNOWN           : 0,
+       E_CONTENT_EDITOR_NODE_IS_ANCHOR         : 1 << 0,
+       E_CONTENT_EDITOR_NODE_IS_H_RULE         : 1 << 1,
+       E_CONTENT_EDITOR_NODE_IS_IMAGE          : 1 << 2,
+       E_CONTENT_EDITOR_NODE_IS_TABLE          : 1 << 3,
+       E_CONTENT_EDITOR_NODE_IS_TABLE_CELL     : 1 << 4,
+       E_CONTENT_EDITOR_NODE_IS_TEXT           : 1 << 5,
+       E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED : 1 << 6,
 
        /* Flags for ClaimAffectedContent() */
        CLAIM_CONTENT_FLAG_NONE : 0,
@@ -78,6 +87,7 @@ var EvoEditor = {
 
        mode : 1, // one of the MODE constants
        storedSelection : null,
+       propertiesSelection : null, // dedicated to Properties dialogs
        inheritThemeColors : false,
        checkInheritFontsOnChange : false,
        forceFormatStateUpdate : false,
@@ -169,7 +179,7 @@ EvoEditor.maybeUpdateFormattingState = function(force)
 
        tmp = computedStyle ? computedStyle.webkitTextDecorationsInEffect : "";
 
-       value = tmp.search("underline") >= 0;
+       value = tmp.search("underline") >= 0 && (!baseElem || baseElem.tagName != "A");
        if (force || value != EvoEditor.formattingState.underline) {
                EvoEditor.formattingState.underline = value;
                changes["underline"] = value;
@@ -264,7 +274,7 @@ EvoEditor.maybeUpdateFormattingState = function(force)
        };
 
        for (parent = baseElem; parent && !(parent == document.body) && (
-            obj.script == 0 || obj.blockFormat == null || obj.fontSize == null || obj.indented == null 
||obj.bgColor == null);
+            obj.script == 0 || obj.blockFormat == null || obj.fontSize == null || obj.indented == null || 
obj.bgColor == null);
             parent = parent.parentElement) {
                if (obj.script == 0) {
                        if (parent.tagName == "SUB")
@@ -1548,9 +1558,56 @@ EvoEditor.SetBodyFontName = function(name)
        }
 }
 
+EvoEditor.beforeInputCb = function(inputEvent)
+{
+       if (EvoUndoRedo.disabled ||
+           !inputEvent ||
+           inputEvent.inputType != "insertText" ||
+           !inputEvent.data ||
+           inputEvent.data.length != 1 ||
+           inputEvent.data == " " ||
+           inputEvent.data == "\t")
+               return;
+
+       var selection = document.getSelection();
+
+       // when writing at the end of the anchor, then write into the anchor, not out (WebKit writes out)
+       if (!selection ||
+           !selection.isCollapsed ||
+           !selection.baseNode ||
+           selection.baseNode.nodeType != selection.baseNode.TEXT_NODE ||
+           selection.baseOffset != selection.baseNode.nodeValue.length ||
+           !selection.baseNode.parentElement ||
+           selection.baseNode.parentElement.tagName != "A")
+               return;
+
+       var node = selection.baseNode;
+
+       EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_EVENT, "insertText", selection.baseNode, 
selection.baseNode,
+               EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML | EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
+
+       try {
+               node.nodeValue += inputEvent.data;
+               selection.setPosition(node, node.nodeValue.length);
+
+               if (EvoEditor.mode == EvoEditor.MODE_PLAIN_TEXT)
+                       node.parentElement.href = node.nodeValue;
+       } finally {
+               EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_EVENT, "insertText");
+       }
+
+       // it will add the text, if anything breaks before it gets here
+       inputEvent.stopImmediatePropagation();
+       inputEvent.stopPropagation();
+       inputEvent.preventDefault();
+}
+
 EvoEditor.initializeContent = function()
 {
        if (document.body) {
+               // attach on body, thus it runs before EvoUndoRedo.beforeInputCb()
+               document.body.onbeforeinput = EvoEditor.beforeInputCb;
+
                if (!document.body.firstChild) {
                        EvoUndoRedo.Disable();
                        try {
@@ -2229,6 +2286,191 @@ EvoEditor.AfterInputEvent = function(inputEvent, isWordDelim)
        }
 }
 
+EvoEditor.getCaretElement = function(tagName)
+{
+       var node;
+
+       node = document.getSelection().extentNode;
+
+       if (!node)
+               node = document.getSelection().baseNode;
+
+       while (node && node.nodeType != node.ELEMENT_NODE) {
+               node = node.parentElement;
+       }
+
+       if (node && node.tagName == tagName)
+               return node;
+
+       return null;
+}
+
+EvoEditor.storePropertiesSelection = function()
+{
+       EvoEditor.propertiesSelection = EvoSelection.Store(document);
+}
+
+EvoEditor.restorePropertiesSelection = function()
+{
+       if (EvoEditor.propertiesSelection) {
+               var selection = EvoEditor.propertiesSelection;
+
+               EvoEditor.propertiesSelection = null;
+
+               try {
+                       // Ignore any errors here
+                       EvoSelection.Restore(document, selection);
+               } catch (exception) {
+               }
+       }
+}
+
+EvoEditor.OnPropertiesOpen = function()
+{
+       EvoEditor.storePropertiesSelection();
+}
+
+EvoEditor.OnPropertiesClose = function()
+{
+       EvoEditor.restorePropertiesSelection();
+}
+
+EvoEditor.GetLinkValues = function()
+{
+       var res = null, anchor = EvoEditor.getCaretElement("A");
+
+       if (anchor) {
+               res = [];
+               res["href"] = anchor.href;
+               res["text"] = anchor.innerText;
+       } else if (!document.getSelection().isCollapsed && document.getSelection().rangeCount > 0) {
+               var range;
+
+               range = document.getSelection().getRangeAt(0);
+
+               if (range) {
+                       res = [];
+                       res["text"] = range.toString();
+               }
+       }
+
+       return res;
+}
+
+EvoEditor.SetLinkValues = function(href, text)
+{
+       // The properties dialog can discard selection, thus restore it before doing changes
+       EvoEditor.restorePropertiesSelection();
+
+       var anchor = EvoEditor.getCaretElement("A");
+
+       if (anchor && (anchor.href != href || anchor.innerText != text)) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetLinkValues", anchor, anchor, 
EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+               try {
+                       if (anchor.href != href)
+                               anchor.href = href;
+                       if (anchor.innerText != text) {
+                               var selection = EvoSelection.Store(document);
+                               anchor.innerText = text;
+                               EvoSelection.Restore(document, selection);
+                       }
+               } finally {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetLinkValues");
+               }
+       } else if (!anchor && href != "" && text != "") {
+               text = text.replace(/\&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+               href = href.replace(/\&/g, "&amp;").replace(/\"/g, "&quot;");
+
+               EvoEditor.InsertHTML("CreateLink", "<A href=\"" + href + "\">" + text + "</A>");
+       }
+}
+
+EvoEditor.Unlink = function()
+{
+       // The properties dialog can discard selection, thus restore it before doing changes
+       EvoEditor.restorePropertiesSelection();
+
+       var anchor = EvoEditor.getCaretElement("A");
+
+       if (anchor) {
+               EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "Unlink", anchor.parentElement, 
anchor.parentElement, EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+               try {
+                       var selectionUpdater = EvoSelection.CreateUpdaterObject(), firstChild;
+
+                       firstChild = anchor.firstChild;
+
+                       while (anchor.firstChild) {
+                               anchor.parentElement.insertBefore(anchor.firstChild, anchor);
+                       }
+
+                       selectionUpdater.beforeRemove(anchor);
+                       anchor.parentElement.removeChild(anchor);
+                       selectionUpdater.afterRemove(firstChild);
+
+                       selectionUpdater.restore();
+               } finally {
+                       EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "Unlink");
+               }
+       }
+}
+
+EvoEditor.GetCaretWord = function()
+{
+       if (document.getSelection().rangeCount < 1)
+               return null;
+
+       var range = document.getSelection().getRangeAt(0);
+
+       if (!range)
+               return null;
+
+       range = range.cloneRange();
+       range.expand("word");
+
+       return range.toString();
+}
+
+EvoEditor.onContextMenu = function(event)
+{
+       var node = event.target;
+
+       if (!node)
+               node = document.getSelection().extentNode;
+       if (!node)
+               node = document.getSelection().baseNode;
+
+       var nodeFlags = EvoEditor.E_CONTENT_EDITOR_NODE_UNKNOWN, hasNode = node, res;
+
+       while (node) {
+               if (node.tagName == "A")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_ANCHOR;
+               else if (node.tagName == "HR")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_H_RULE;
+               else if (node.tagName == "IMG")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_IMAGE;
+               else if (node.tagName == "TABLE")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TABLE;
+               else if (node.tagName == "TD" || node.tagName == "TH")
+                       nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_CELL;
+
+               node = node.parentElement;
+       }
+
+       if (!nodeFlags && hasNode)
+               nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TEXT;
+
+       if (document.getSelection().isCollapsed)
+               nodeFlags |= EvoEditor.E_CONTENT_EDITOR_NODE_IS_TEXT_COLLAPSED;
+
+       res = [];
+
+       res["nodeFlags"] = nodeFlags;
+       res["caretWord"] = EvoEditor.GetCaretWord();
+
+       window.webkit.messageHandlers.contextMenuRequested.postMessage(res);
+}
+
+document.oncontextmenu = EvoEditor.onContextMenu;
 document.onload = EvoEditor.initializeContent;
 
 document.onselectionchange = function() {
diff --git a/data/webkit/e-undo-redo.js b/data/webkit/e-undo-redo.js
index 5155578519..e277e69cb0 100644
--- a/data/webkit/e-undo-redo.js
+++ b/data/webkit/e-undo-redo.js
@@ -327,7 +327,7 @@ var EvoUndoRedo = {
        delete all nodes between index >= firstChildIndex && index < children.length - restChildrenCount.
        */
 
-       dropTarget : null, // passed from drop_cb() into before_input_cb()/input_cb() for "insertFromDrop" 
event
+       dropTarget : null, // passed from dropCb() into beforeInputCb()/inputCb() for "insertFromDrop" event
        disabled : 0,
        ongoingRecordings : [] // the recordings can be nested
 };
@@ -335,9 +335,9 @@ var EvoUndoRedo = {
 EvoUndoRedo.Attach = function()
 {
        if (document.documentElement) {
-               document.documentElement.onbeforeinput = EvoUndoRedo.before_input_cb;
-               document.documentElement.oninput = EvoUndoRedo.input_cb;
-               document.documentElement.ondrop = EvoUndoRedo.drop_cb;
+               document.documentElement.onbeforeinput = EvoUndoRedo.beforeInputCb;
+               document.documentElement.oninput = EvoUndoRedo.inputCb;
+               document.documentElement.ondrop = EvoUndoRedo.dropCb;
        }
 }
 
@@ -376,7 +376,7 @@ EvoUndoRedo.isWordDelimEvent = function(inputEvent)
                (inputEvent.data == " " || inputEvent.data == "\t");
 }
 
-EvoUndoRedo.before_input_cb = function(inputEvent)
+EvoUndoRedo.beforeInputCb = function(inputEvent)
 {
        if (EvoUndoRedo.disabled) {
                return;
@@ -450,7 +450,7 @@ EvoUndoRedo.before_input_cb = function(inputEvent)
        }
 }
 
-EvoUndoRedo.input_cb = function(inputEvent)
+EvoUndoRedo.inputCb = function(inputEvent)
 {
        var isWordDelim = EvoUndoRedo.isWordDelimEvent(inputEvent);
 
@@ -484,7 +484,7 @@ EvoUndoRedo.input_cb = function(inputEvent)
        EvoEditor.AfterInputEvent(inputEvent, isWordDelim);
 }
 
-EvoUndoRedo.drop_cb = function(event)
+EvoUndoRedo.dropCb = function(event)
 {
        EvoUndoRedo.dropTarget = event.toElement;
 }
diff --git a/src/e-util/e-content-editor.c b/src/e-util/e-content-editor.c
index db4f5bc617..08706662a9 100644
--- a/src/e-util/e-content-editor.c
+++ b/src/e-util/e-content-editor.c
@@ -548,10 +548,11 @@ e_content_editor_default_init (EContentEditorInterface *iface)
                E_TYPE_CONTENT_EDITOR,
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (EContentEditorInterface, context_menu_requested),
-               g_signal_accumulator_true_handled, NULL,
+               NULL, NULL,
                NULL,
-               G_TYPE_BOOLEAN, 2,
+               G_TYPE_NONE, 3,
                G_TYPE_INT,
+               G_TYPE_STRING,
                GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
        /**
@@ -4111,18 +4112,15 @@ e_content_editor_emit_paste_primary_clipboard (EContentEditor *editor)
        return handled;
 }
 
-gboolean
+void
 e_content_editor_emit_context_menu_requested (EContentEditor *editor,
-                                              EContentEditorNodeFlags flags,
-                                              GdkEvent *event)
+                                             EContentEditorNodeFlags flags,
+                                             const gchar *caret_word,
+                                             GdkEvent *event)
 {
-       gboolean handled = FALSE;
-
-       g_return_val_if_fail (E_IS_CONTENT_EDITOR (editor), FALSE);
-
-       g_signal_emit (editor, signals[CONTEXT_MENU_REQUESTED], 0, flags, event, &handled);
+       g_return_if_fail (E_IS_CONTENT_EDITOR (editor));
 
-       return handled;
+       g_signal_emit (editor, signals[CONTEXT_MENU_REQUESTED], 0, flags, caret_word, event, NULL);
 }
 
 void
diff --git a/src/e-util/e-content-editor.h b/src/e-util/e-content-editor.h
index 84892846de..548315ed73 100644
--- a/src/e-util/e-content-editor.h
+++ b/src/e-util/e-content-editor.h
@@ -451,8 +451,9 @@ struct _EContentEditorInterface {
        void            (*load_finished)                (EContentEditor *editor);
        gboolean        (*paste_clipboard)              (EContentEditor *editor);
        gboolean        (*paste_primary_clipboard)      (EContentEditor *editor);
-       gboolean        (*context_menu_requested)       (EContentEditor *editor,
+       void            (*context_menu_requested)       (EContentEditor *editor,
                                                         EContentEditorNodeFlags flags,
+                                                        const gchar *caret_word,
                                                         GdkEvent *event);
        void            (*find_done)                    (EContentEditor *editor,
                                                         guint match_count);
@@ -1099,9 +1100,10 @@ gboolean e_content_editor_emit_paste_clipboard
                                                (EContentEditor *editor);
 gboolean       e_content_editor_emit_paste_primary_clipboard
                                                (EContentEditor *editor);
-gboolean       e_content_editor_emit_context_menu_requested
+void           e_content_editor_emit_context_menu_requested
                                                (EContentEditor *editor,
                                                 EContentEditorNodeFlags flags,
+                                                const gchar *caret_word,
                                                 GdkEvent *event);
 void           e_content_editor_emit_find_done (EContentEditor *editor,
                                                 guint match_count);
diff --git a/src/e-util/e-html-editor.c b/src/e-util/e-html-editor.c
index 55e59e7876..e6dff74a41 100644
--- a/src/e-util/e-html-editor.c
+++ b/src/e-util/e-html-editor.c
@@ -240,7 +240,8 @@ action_context_spell_suggest_cb (GtkAction *action,
 }
 
 static void
-html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
+html_editor_inline_spelling_suggestions (EHTMLEditor *editor,
+                                        const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -248,7 +249,6 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
        GtkUIManager *manager;
        gchar **suggestions;
        const gchar *path;
-       gchar *word;
        guint count = 0;
        guint length;
        guint merge_id;
@@ -256,12 +256,11 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
        gint ii;
 
        cnt_editor = e_html_editor_get_content_editor (editor);
-       word = e_content_editor_get_caret_word (cnt_editor);
-       if (word == NULL || *word == '\0')
+       if (!caret_word || !*caret_word)
                return;
 
        spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
-       suggestions = e_spell_checker_get_guesses_for_word (spell_checker, word);
+       suggestions = e_spell_checker_get_guesses_for_word (spell_checker, caret_word);
 
        path = "/context-menu/context-spell-suggest/";
        manager = e_html_editor_get_ui_manager (editor);
@@ -295,12 +294,9 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 
                /* Action name just needs to be unique. */
                action_name = g_strdup_printf ("suggest-%d", count++);
+               action_label = g_markup_printf_escaped ("<b>%s</b>", suggestion);
 
-               action_label = g_markup_printf_escaped (
-                       "<b>%s</b>", suggestion);
-
-               action = gtk_action_new (
-                       action_name, action_label, NULL, NULL);
+               action = gtk_action_new (action_name, action_label, NULL, NULL);
 
                g_object_set_data_full (
                        G_OBJECT (action), "word",
@@ -329,7 +325,6 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
                g_free (action_label);
        }
 
-       g_free (word);
        g_strfreev (suggestions);
        g_clear_object (&spell_checker);
 }
@@ -337,7 +332,8 @@ html_editor_inline_spelling_suggestions (EHTMLEditor *editor)
 /* Helper for html_editor_update_actions() */
 static void
 html_editor_spell_checkers_foreach (EHTMLEditor *editor,
-                                    const gchar *language_code)
+                                   const gchar *language_code,
+                                   const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -346,22 +342,18 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
        GtkUIManager *manager;
        GList *list, *link;
        gchar *path;
-       gchar *word;
        gint ii = 0;
        guint merge_id;
 
        cnt_editor = e_html_editor_get_content_editor (editor);
-       word = e_content_editor_get_caret_word (cnt_editor);
-       if (word == NULL || *word == '\0')
+       if (!caret_word || !*caret_word)
                return;
 
        spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
 
-       dictionary = e_spell_checker_ref_dictionary (
-               spell_checker, language_code);
+       dictionary = e_spell_checker_ref_dictionary (spell_checker, language_code);
        if (dictionary != NULL) {
-               list = e_spell_dictionary_get_suggestions (
-                       dictionary, word, -1);
+               list = e_spell_dictionary_get_suggestions (dictionary, caret_word, -1);
                g_object_unref (dictionary);
        } else {
                list = NULL;
@@ -384,14 +376,10 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
                GSList *proxies;
 
                /* Action name just needs to be unique. */
-               action_name = g_strdup_printf (
-                       "suggest-%s-%d", language_code, ii);
-
-               action_label = g_markup_printf_escaped (
-                       "%s", suggestion);
+               action_name = g_strdup_printf ("suggest-%s-%d", language_code, ii);
+               action_label = g_markup_printf_escaped ("%s", suggestion);
 
-               action = gtk_action_new (
-                       action_name, action_label, NULL, NULL);
+               action = gtk_action_new (action_name, action_label, NULL, NULL);
 
                g_object_set_data_full (
                        G_OBJECT (action), "word",
@@ -425,7 +413,6 @@ html_editor_spell_checkers_foreach (EHTMLEditor *editor,
        g_list_free_full (list, (GDestroyNotify) g_free);
        g_clear_object (&spell_checker);
        g_free (path);
-       g_free (word);
 }
 
 void
@@ -460,7 +447,8 @@ action_set_visible_and_sensitive (GtkAction *action,
 
 static void
 html_editor_update_actions (EHTMLEditor *editor,
-                            EContentEditorNodeFlags flags)
+                           EContentEditorNodeFlags flags,
+                           const gchar *caret_word)
 {
        EContentEditor *cnt_editor;
        ESpellChecker *spell_checker;
@@ -550,13 +538,11 @@ html_editor_update_actions (EHTMLEditor *editor,
        /* Decide if we should show spell checking items. */
        visible = FALSE;
        if (n_languages > 0) {
-               gchar *word = e_content_editor_get_caret_word (cnt_editor);
-               if (word && *word) {
-                       visible = !e_spell_checker_check_word (spell_checker, word, -1);
+               if (caret_word && *caret_word) {
+                       visible = !e_spell_checker_check_word (spell_checker, caret_word, -1);
                } else {
                        visible = FALSE;
                }
-               g_free (word);
        }
 
        action_group = editor->priv->spell_check_actions;
@@ -575,7 +561,7 @@ html_editor_update_actions (EHTMLEditor *editor,
 
        /* Handle a single active language as a special case. */
        if (n_languages == 1) {
-               html_editor_inline_spelling_suggestions (editor);
+               html_editor_inline_spelling_suggestions (editor, caret_word);
                g_strfreev (languages);
 
                e_html_editor_update_spell_actions (editor);
@@ -584,7 +570,7 @@ html_editor_update_actions (EHTMLEditor *editor,
 
        /* Add actions and context menu content for active languages. */
        for (ii = 0; ii < n_languages; ii++)
-               html_editor_spell_checkers_foreach (editor, languages[ii]);
+               html_editor_spell_checkers_foreach (editor, languages[ii], caret_word);
 
        g_strfreev (languages);
 
@@ -621,6 +607,7 @@ html_editor_spell_languages_changed (EHTMLEditor *editor)
 typedef struct _ContextMenuData {
        GWeakRef *editor_weakref; /* EHTMLEditor * */
        EContentEditorNodeFlags flags;
+       gchar *caret_word;
        GdkEvent *event;
 } ContextMenuData;
 
@@ -632,6 +619,7 @@ context_menu_data_free (gpointer ptr)
        if (cmd) {
                g_clear_pointer (&cmd->event, gdk_event_free);
                e_weak_ref_free (cmd->editor_weakref);
+               g_free (cmd->caret_word);
                g_free (cmd);
        }
 }
@@ -660,7 +648,7 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
 
                menu = e_html_editor_get_managed_widget (editor, "/context-menu");
 
-               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags);
+               g_signal_emit (editor, signals[UPDATE_ACTIONS], 0, cmd->flags, cmd->caret_word);
 
                if (!gtk_menu_get_attach_widget (GTK_MENU (menu))) {
                        gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
@@ -678,25 +666,25 @@ html_editor_show_context_menu_idle_cb (gpointer user_data)
        return FALSE;
 }
 
-static gboolean
+static void
 html_editor_context_menu_requested_cb (EContentEditor *cnt_editor,
-                                       EContentEditorNodeFlags flags,
-                                       GdkEvent *event,
-                                       EHTMLEditor *editor)
+                                      EContentEditorNodeFlags flags,
+                                      const gchar *caret_word,
+                                      GdkEvent *event,
+                                      EHTMLEditor *editor)
 {
        ContextMenuData *cmd;
 
-       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
+       g_return_if_fail (E_IS_HTML_EDITOR (editor));
 
        cmd = g_new0 (ContextMenuData, 1);
        cmd->editor_weakref = e_weak_ref_new (editor);
        cmd->flags = flags;
+       cmd->caret_word = g_strdup (caret_word);
        cmd->event = gdk_event_copy (event);
 
        g_idle_add_full (G_PRIORITY_LOW, html_editor_show_context_menu_idle_cb,
                cmd, context_menu_data_free);
-
-       return TRUE;
 }
 
 static gchar *
@@ -1029,9 +1017,10 @@ e_html_editor_class_init (EHTMLEditorClass *class)
                G_SIGNAL_RUN_LAST,
                G_STRUCT_OFFSET (EHTMLEditorClass, update_actions),
                NULL, NULL,
-               g_cclosure_marshal_VOID__UINT,
-               G_TYPE_NONE, 1,
-               G_TYPE_UINT);
+               NULL,
+               G_TYPE_NONE, 2,
+               G_TYPE_UINT,
+               G_TYPE_STRING);
 
        signals[SPELL_LANGUAGES_CHANGED] = g_signal_new (
                "spell-languages-changed",
diff --git a/src/e-util/e-html-editor.h b/src/e-util/e-html-editor.h
index 121eae7758..db0d9270af 100644
--- a/src/e-util/e-html-editor.h
+++ b/src/e-util/e-html-editor.h
@@ -64,7 +64,8 @@ struct _EHTMLEditorClass {
        GtkGridClass parent_class;
 
        void            (*update_actions)       (EHTMLEditor *editor,
-                                                EContentEditorNodeFlags flags);
+                                                EContentEditorNodeFlags flags,
+                                                const gchar *caret_word);
 
        void            (*spell_languages_changed)
                                                (EHTMLEditor *editor);
diff --git a/src/e-util/test-html-editor-units.c b/src/e-util/test-html-editor-units.c
index f4a55c93d5..905e613ab7 100644
--- a/src/e-util/test-html-editor-units.c
+++ b/src/e-util/test-html-editor-units.c
@@ -3316,7 +3316,7 @@ test_link_insert_dialog (TestFixture *fixture)
                "type:http://www.gnome.org\n";
                "seq:n\n",
                HTML_PREFIX "<div>a link example: <a 
href=\"http://www.gnome.org\";>http://www.gnome.org</a></div>" HTML_SUFFIX,
-               "a link example: http://www.gnome.org";))
+               "a link example: http://www.gnome.org\n";))
                g_test_fail ();
 }
 
@@ -3331,7 +3331,7 @@ test_link_insert_dialog_selection (TestFixture *fixture)
                "type:http://www.gnome.org\n";
                "seq:n\n",
                HTML_PREFIX "<div>a link example: <a href=\"http://www.gnome.org\";>GNOME</a></div>" 
HTML_SUFFIX,
-               "a link example: GNOME"))
+               "a link example: GNOME\n"))
                g_test_fail ();
 }
 
@@ -3342,7 +3342,7 @@ test_link_insert_typed (TestFixture *fixture)
                "mode:html\n"
                "type:www.gnome.org \n",
                HTML_PREFIX "<div><a href=\"http://www.gnome.org\";>www.gnome.org</a> </div>" HTML_SUFFIX,
-               "www.gnome.org "))
+               "www.gnome.org \n"))
                g_test_fail ();
 }
 
@@ -3360,7 +3360,7 @@ test_link_insert_typed_change_description (TestFixture *fixture)
                "type:GNOME\n"
                "seq:n\n",
                HTML_PREFIX "<div><a href=\"http://www.gnome.org\";>GNOME</a> </div>" HTML_SUFFIX,
-               "GNOME "))
+               "GNOME \n"))
                g_test_fail ();
 }
 
@@ -3376,7 +3376,7 @@ test_link_insert_dialog_remove_link (TestFixture *fixture)
                "type:R\n"
                "seq:a\n",
                HTML_PREFIX "<div>www.gnome.org </div>" HTML_SUFFIX,
-               "www.gnome.org "))
+               "www.gnome.org \n"))
                g_test_fail ();
 }
 
@@ -3388,8 +3388,8 @@ test_link_insert_typed_append (TestFixture *fixture)
                "type:www.gnome.org \n"
                "seq:l\n"
                "type:/about\n",
-               HTML_PREFIX "<div><a href=\"http://www.gnome.org/\";>www.gnome.org/about</a> </div>" 
HTML_SUFFIX,
-               "www.gnome.org/about "))
+               HTML_PREFIX "<div><a href=\"http://www.gnome.org\";>www.gnome.org/about</a> </div>" 
HTML_SUFFIX,
+               "www.gnome.org/about \n"))
                g_test_fail ();
 }
 
@@ -3401,7 +3401,7 @@ test_link_insert_typed_remove (TestFixture *fixture)
                "type:www.gnome.org \n"
                "seq:bbb\n",
                HTML_PREFIX "<div><a href=\"http://www.gnome.org\";>www.gnome.o</a></div>" HTML_SUFFIX,
-               "www.gnome.o"))
+               "www.gnome.o\n"))
                g_test_fail ();
 }
 
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 145d582e71..b60a003bdc 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -122,6 +122,10 @@ struct _EWebKitEditorPrivate {
        EContentEditorBlockFormat block_format;
        EContentEditorAlignment alignment;
 
+       /* For context menu */
+       gchar *context_menu_caret_word;
+       guint32 context_menu_node_flags; /* bit-or of EContentEditorNodeFlags */
+
        gchar *current_user_stylesheet;
 
        WebKitLoadEvent webkit_load_event;
@@ -194,6 +198,145 @@ G_DEFINE_TYPE_WITH_CODE (
                E_TYPE_CONTENT_EDITOR,
                e_webkit_editor_content_editor_init));
 
+typedef struct _EWebKitEditorFlagClass {
+       GObjectClass parent_class;
+} EWebKitEditorFlagClass;
+
+typedef struct _EWebKitEditorFlag {
+       GObject parent;
+       gboolean is_set;
+} EWebKitEditorFlag;
+
+GType e_webkit_editor_flag_get_type (void);
+
+G_DEFINE_TYPE (EWebKitEditorFlag, e_webkit_editor_flag, G_TYPE_OBJECT)
+
+enum {
+       E_WEBKIT_EDITOR_FLAG_FLAGGED,
+       E_WEBKIT_EDITOR_FLAG_LAST_SIGNAL
+};
+
+static guint e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_LAST_SIGNAL];
+
+static void
+e_webkit_editor_flag_class_init (EWebKitEditorFlagClass *klass)
+{
+       e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_FLAGGED] = g_signal_new (
+               "flagged",
+               G_TYPE_FROM_CLASS (klass),
+               G_SIGNAL_RUN_LAST,
+               0,
+               NULL, NULL, NULL,
+               G_TYPE_NONE, 0,
+               G_TYPE_NONE);
+}
+
+static void
+e_webkit_editor_flag_init (EWebKitEditorFlag *flag)
+{
+       flag->is_set = FALSE;
+}
+
+static void
+e_webkit_editor_flag_set (EWebKitEditorFlag *flag)
+{
+       flag->is_set = TRUE;
+
+       g_signal_emit (flag, e_webkit_editor_flag_signals[E_WEBKIT_EDITOR_FLAG_FLAGGED], 0, NULL);
+}
+
+static JSCValue * /* transfer full */
+webkit_editor_call_jsc_sync (EWebKitEditor *wk_editor,
+                            const gchar *script_format,
+                            ...) G_GNUC_PRINTF (2, 3);
+
+typedef struct _JSCCallData {
+       EWebKitEditorFlag *flag;
+       gchar *script;
+       JSCValue *result;
+} JSCCallData;
+
+static void
+webkit_editor_jsc_call_done_cb (GObject *source,
+                               GAsyncResult *result,
+                               gpointer user_data)
+{
+       WebKitJavascriptResult *js_result;
+       JSCCallData *jcd = user_data;
+       GError *error = NULL;
+
+       js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (source), result, &error);
+
+       if (error) {
+               if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
+                   (!g_error_matches (error, WEBKIT_JAVASCRIPT_ERROR, WEBKIT_JAVASCRIPT_ERROR_SCRIPT_FAILED) 
||
+                    /* WebKit can return empty error message, thus ignore those. */
+                    (error->message && *(error->message))))
+                       g_warning ("Failed to call '%s' function: %s:%d: %s", jcd->script, g_quark_to_string 
(error->domain), error->code, error->message);
+               g_clear_error (&error);
+       }
+
+       if (js_result) {
+               JSCException *exception;
+               JSCValue *value;
+
+               value = webkit_javascript_result_get_js_value (js_result);
+               exception = jsc_context_get_exception (jsc_value_get_context (value));
+
+               if (exception) {
+                       g_warning ("Failed to call '%s': %s", jcd->script, jsc_exception_get_message 
(exception));
+                       jsc_context_clear_exception (jsc_value_get_context (value));
+               } else if (!jsc_value_is_null (value) && !jsc_value_is_undefined (value)) {
+                       jcd->result = g_object_ref (value);
+               }
+
+               webkit_javascript_result_unref (js_result);
+       }
+
+       e_webkit_editor_flag_set (jcd->flag);
+}
+
+static JSCValue * /* transfer full */
+webkit_editor_call_jsc_sync (EWebKitEditor *wk_editor,
+                            const gchar *script_format,
+                            ...)
+{
+       JSCCallData jcd;
+       va_list va;
+
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), NULL);
+       g_return_val_if_fail (script_format != NULL, NULL);
+
+       va_start (va, script_format);
+       jcd.script = e_web_view_jsc_vprintf_script (script_format, va);
+       va_end (va);
+
+       jcd.flag = g_object_new (e_webkit_editor_flag_get_type (), NULL);
+       jcd.result = NULL;
+
+       webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (wk_editor), jcd.script, wk_editor->priv->cancellable,
+               webkit_editor_jsc_call_done_cb, &jcd);
+
+       if (!jcd.flag->is_set) {
+               GMainLoop *loop;
+               gulong handler_id;
+
+               loop = g_main_loop_new (NULL, FALSE);
+
+               handler_id = g_signal_connect_swapped (jcd.flag, "flagged", G_CALLBACK (g_main_loop_quit), 
loop);
+
+               g_main_loop_run (loop);
+               g_main_loop_unref (loop);
+
+               g_signal_handler_disconnect (jcd.flag, handler_id);
+       }
+
+       g_clear_object (&jcd.flag);
+       g_free (jcd.script);
+
+       return jcd.result;
+}
+
 static gint16
 e_webkit_editor_three_state_to_int16 (EThreeState value)
 {
@@ -379,6 +522,26 @@ content_changed_cb (WebKitUserContentManager *manager,
        webkit_editor_set_changed (wk_editor, TRUE);
 }
 
+static void
+context_menu_requested_cb (WebKitUserContentManager *manager,
+                          WebKitJavascriptResult *js_result,
+                          gpointer user_data)
+{
+       EWebKitEditor *wk_editor = user_data;
+       JSCValue *jsc_params;
+
+       g_return_if_fail (E_IS_WEBKIT_EDITOR (wk_editor));
+       g_return_if_fail (js_result != NULL);
+
+       jsc_params = webkit_javascript_result_get_js_value (js_result);
+       g_return_if_fail (jsc_value_is_object (jsc_params));
+
+       g_clear_pointer (&wk_editor->priv->context_menu_caret_word, g_free);
+
+       wk_editor->priv->context_menu_node_flags = e_web_view_jsc_get_object_property_int32 (jsc_params, 
"nodeFlags", 0);
+       wk_editor->priv->context_menu_caret_word = e_web_view_jsc_get_object_property_string (jsc_params, 
"caretWord", NULL);
+}
+
 static gboolean
 webkit_editor_update_color_value (JSCValue *jsc_params,
                                  const gchar *param_name,
@@ -2234,27 +2397,12 @@ static gchar *
 webkit_editor_get_caret_word (EContentEditor *editor)
 {
        EWebKitEditor *wk_editor;
-       gchar *ret_val = NULL;
-       GVariant *result;
 
-       wk_editor = E_WEBKIT_EDITOR (editor);
-       if (!wk_editor->priv->web_extension_proxy) {
-               printf ("EHTMLEditorWebExtension not ready at %s!\n", G_STRFUNC);
-               return NULL;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "DOMGetCaretWord",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (editor), NULL);
 
-       if (result) {
-               g_variant_get (result, "(s)", &ret_val);
-               g_variant_unref (result);
-       }
+       wk_editor = E_WEBKIT_EDITOR (editor);
 
-       return ret_val;
+       return NULL;
 }
 
 static void
@@ -3621,34 +3769,28 @@ webkit_editor_image_get_height (EContentEditor *editor)
 static void
 webkit_editor_selection_unlink (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorLinkDialogUnlink");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.Unlink();");
 }
 
 static void
 webkit_editor_on_link_dialog_open (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorLinkDialogOnOpen");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.OnPropertiesOpen();");
 }
 
 static void
 webkit_editor_on_link_dialog_close (EContentEditor *editor)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       webkit_editor_call_simple_extension_function (
-               wk_editor, "EEditorLinkDialogOnClose");
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.OnPropertiesClose();");
 }
 
 static void
@@ -3656,20 +3798,11 @@ webkit_editor_link_set_values (EContentEditor *editor,
                                const gchar *href,
                                const gchar *text)
 {
-       EWebKitEditor *wk_editor;
-
-       wk_editor = E_WEBKIT_EDITOR (editor);
-
-       if (!wk_editor->priv->web_extension_proxy) {
-               printf ("EHTMLEditorWebExtension not ready at %s!\n", G_STRFUNC);
-               return;
-       }
+       EWebKitEditor *wk_editor = E_WEBKIT_EDITOR (editor);
 
-       e_util_invoke_g_dbus_proxy_call_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorLinkDialogOk",
-               g_variant_new ("(tss)", current_page_id (wk_editor), href, text),
-               wk_editor->priv->cancellable);
+       e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+               "EvoEditor.SetLinkValues(%s, %s);",
+               href, text);
 }
 
 static void
@@ -3678,24 +3811,17 @@ webkit_editor_link_get_values (EContentEditor *editor,
                                gchar **text)
 {
        EWebKitEditor *wk_editor;
-       GVariant *result;
+       JSCValue *result;
 
        wk_editor = E_WEBKIT_EDITOR (editor);
 
-       if (!wk_editor->priv->web_extension_proxy) {
-               printf ("EHTMLEditorWebExtension not ready at %s!\n", G_STRFUNC);
-               return;
-       }
-
-       result = e_util_invoke_g_dbus_proxy_call_sync_wrapper_with_error_check (
-               wk_editor->priv->web_extension_proxy,
-               "EEditorLinkDialogShow",
-               g_variant_new ("(t)", current_page_id (wk_editor)),
-               NULL);
+       result = webkit_editor_call_jsc_sync (wk_editor, "EvoEditor.GetLinkValues();");
 
        if (result) {
-               g_variant_get (result, "(ss)", href, text);
-               g_variant_unref (result);
+               *href = e_web_view_jsc_get_object_property_string (result, "href", NULL);
+               *text = e_web_view_jsc_get_object_property_string (result, "text", NULL);
+
+               g_clear_object (&result);
        } else {
                *href = NULL;
                *text = NULL;
@@ -5265,12 +5391,15 @@ webkit_editor_constructed (GObject *object)
 
        g_signal_connect_object (manager, "script-message-received::contentChanged",
                G_CALLBACK (content_changed_cb), wk_editor, 0);
+       g_signal_connect_object (manager, "script-message-received::contextMenuRequested",
+               G_CALLBACK (context_menu_requested_cb), wk_editor, 0);
        g_signal_connect_object (manager, "script-message-received::formattingChanged",
                G_CALLBACK (formatting_changed_cb), wk_editor, 0);
        g_signal_connect_object (manager, "script-message-received::undoRedoStateChanged",
                G_CALLBACK (undu_redo_state_changed_cb), wk_editor, 0);
 
        webkit_user_content_manager_register_script_message_handler (manager, "contentChanged");
+       webkit_user_content_manager_register_script_message_handler (manager, "contextMenuRequested");
        webkit_user_content_manager_register_script_message_handler (manager, "formattingChanged");
        webkit_user_content_manager_register_script_message_handler (manager, "undoRedoStateChanged");
 
@@ -5468,6 +5597,7 @@ webkit_editor_finalize (GObject *object)
 
        g_free (priv->body_font_name);
        g_free (priv->font_name);
+       g_free (priv->context_menu_caret_word);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_webkit_editor_parent_class)->finalize (object);
@@ -6168,18 +6298,17 @@ webkit_editor_context_menu_cb (EWebKitEditor *wk_editor,
                                GdkEvent *event,
                                WebKitHitTestResult *hit_test_result)
 {
-       GVariant *result;
-       EContentEditorNodeFlags flags = 0;
-       gboolean handled;
-
-       webkit_context_menu_remove_all (context_menu);
+       g_return_val_if_fail (E_IS_WEBKIT_EDITOR (wk_editor), FALSE);
 
-       if ((result = webkit_context_menu_get_user_data (context_menu)))
-               flags = g_variant_get_int32 (result);
+       e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor),
+               wk_editor->priv->context_menu_node_flags,
+               wk_editor->priv->context_menu_caret_word,
+               event);
 
-       handled = e_content_editor_emit_context_menu_requested (E_CONTENT_EDITOR (wk_editor), flags, event);
+       wk_editor->priv->context_menu_node_flags = E_CONTENT_EDITOR_NODE_UNKNOWN;
+       g_clear_pointer (&wk_editor->priv->context_menu_caret_word, g_free);
 
-       return handled;
+       return TRUE;
 }
 
 static void



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