Bug 1569749 - Update spellcheck context menu - Port Bug 1026099 "Rework the spellchecker context menu to not use CPOWs". r=frg a=frg
authorIan Neal <iann_cvs@blueyonder.co.uk>
Wed, 31 Jul 2019 21:56:04 +0200
changeset 32256 d40072488eaea90bd58f1336f0b32d2a0e228730
parent 32255 fefca2976fee13e40bc01466a3f24fba76c28ad2
child 32257 7f9f81bab5610f028e80c9f2b90412c66085f51c
push id208
push userfrgrahl@gmx.net
push dateWed, 31 Jul 2019 19:58:37 +0000
treeherdercomm-esr60@2375c07411d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg, frg
bugs1569749, 1026099
Bug 1569749 - Update spellcheck context menu - Port Bug 1026099 "Rework the spellchecker context menu to not use CPOWs". r=frg a=frg
suite/base/content/nsContextMenu.js
--- a/suite/base/content/nsContextMenu.js
+++ b/suite/base/content/nsContextMenu.js
@@ -8,25 +8,24 @@
 |   context menu.                                                              |
 |                                                                              |
 |   For usage, see references to this class in navigator.xul.                  |
 |                                                                              |
 |   Currently, this code is relatively useless for any other purpose.  In the  |
 |   longer term, this code will be restructured to make it more reusable.      |
 ------------------------------------------------------------------------------*/
 
+ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var gContextMenuContentData = null;
 
 XPCOMUtils.defineLazyGetter(this, "InlineSpellCheckerUI", function() {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/InlineSpellChecker.jsm", tmp);
-  return new tmp.InlineSpellChecker();
+  return new InlineSpellChecker();
 });
 
 XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
   let tmp = {};
   ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuParent();
 });
 
@@ -567,16 +566,17 @@ nsContextMenu.prototype = {
     this.isTextSelected        = false;
     this.isContentSelected     = false;
     this.onEditableArea        = false;
     this.canSpellCheck         = false;
 
     // Remember the node that was clicked.
     this.target = aNode;
 
+    let editFlags = SpellCheckHelper.isEditable(this.target, window);
     this.browser = this.target.ownerDocument.defaultView
                               .QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebNavigation)
                               .QueryInterface(Ci.nsIDocShell)
                               .chromeEventHandler;
 
     this.autoDownload = Services.prefs.getBoolPref("browser.download.useDownloadDir");
 
@@ -613,34 +613,24 @@ nsContextMenu.prototype = {
           this.onVideo = true;
 
         this.mediaURL = this.target.currentSrc || this.target.src;
       }
       else if (this.target instanceof HTMLAudioElement) {
         this.onAudio = true;
         this.mediaURL = this.target.currentSrc || this.target.src;
       }
-      else if (this.target instanceof HTMLInputElement) {
-        this.onTextInput = this.isTargetATextBox(this.target);
-        // allow spellchecking UI on all writable text boxes except passwords
-        if (this.onTextInput && !this.target.readOnly &&
-            this.target.mozIsTextField(true) && this.target.spellcheck) {
-          this.onEditableArea = true;
+      else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
+        this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
+        this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
+        if (this.onEditableArea) {
           InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
           InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
         }
-        this.onKeywordField = this.isTargetAKeywordField(this.target);
-      }
-      else if (this.target instanceof HTMLTextAreaElement) {
-        this.onTextInput = this.isTextBoxEnabled(this.target);
-        if (this.onTextInput && !this.target.readOnly && this.target.spellcheck) {
-          this.onEditableArea = true;
-          InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
-          InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
-        }
+        this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
       }
       else if ( this.target instanceof HTMLHtmlElement ) {
         // pages with multiple <body>s are lame. we'll teach them a lesson.
         var bodyElt = this.target.ownerDocument.body;
         if (bodyElt) {
           var computedURL = this.getComputedURL(bodyElt, "background-image");
           if (computedURL) {
             this.hasBGImage = true;
@@ -766,38 +756,37 @@ nsContextMenu.prototype = {
 
     // See if the user clicked in a frame.
     var docDefaultView = this.target.ownerDocument.defaultView;
     if (docDefaultView != docDefaultView.top)
       this.inFrame = true;
 
     // if the document is editable, show context menu like in text inputs
     if (!this.onEditableArea) {
-      var win = this.target.ownerDocument.defaultView;
-      if (win) {
+      if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
+        // If this.onEditableArea is false but editFlags is CONTENTEDITABLE,
+        // then the document itself must be editable.
+        this.onTextInput       = true;
+        this.onKeywordField    = false;
+        this.onImage           = false;
+        this.onLoadedImage     = false;
+        this.onMathML          = false;
+        this.inFrame           = false;
+        this.hasBGImage        = false;
+        this.onEditableArea    = true;
+        var win = this.target.ownerDocument.defaultView;
         var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIWebNavigation)
                                 .QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIEditingSession);
-        if (editingSession.windowIsEditable(win) &&
-            this.isTargetEditable() && this.target.spellcheck) {
-          this.onTextInput       = true;
-          this.onKeywordField    = false;
-          this.onImage           = false;
-          this.onLoadedImage     = false;
-          this.onMathML          = false;
-          this.inFrame           = false;
-          this.hasBGImage        = false;
-          this.onEditableArea    = true;
-          InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
-          InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
-          var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
-          this.showItem("spell-check-enabled", canSpell);
-          this.showItem("spell-separator", canSpell);
-        }
+        InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
+        InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
+        var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
+        this.showItem("spell-check-enabled", canSpell);
+        this.showItem("spell-separator", canSpell);
       }
     }
   },
 
   initPopupPrincipal: function() {
     // quick check: if no opener, it can't be a popup
     if (!window.content.opener)
       return;
@@ -1504,28 +1493,16 @@ nsContextMenu.prototype = {
 
   isTargetATextBox: function(aNode) {
     if (aNode instanceof HTMLInputElement)
       return aNode.mozIsTextField(false) && this.isTextBoxEnabled(aNode);
 
     return aNode instanceof HTMLTextAreaElement && this.isTextBoxEnabled(aNode);
   },
 
-  isTargetAKeywordField: function(aNode) {
-    if (!(aNode instanceof HTMLInputElement))
-      return false;
-
-    var form = aNode.form;
-    if (!form || !aNode.mozIsTextField(true))
-      return false;
-
-    return form.method == "get" || (form.method == "post" &&
-           form.enctype == "application/x-www-form-urlencoded");
-  },
-
   // Determines whether or not the separator with the specified ID should be
   // shown or not by determining if there are any non-hidden items between it
   // and the previous separator.
   shouldShowSeparator: function(aSeparatorID) {
     var separator = document.getElementById(aSeparatorID);
     if (separator) {
       var sibling = separator.previousSibling;
       while (sibling && sibling.localName != "menuseparator") {