--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -329,17 +329,17 @@ nsContextMenu.prototype = {
this.showItem("context-shareselect", shareEnabled && this.isContentSelected);
this.showItem("context-sharelink", shareEnabled && (this.onLink || this.onPlainTextLink) && !this.onMailtoLink);
this.showItem("context-shareimage", shareEnabled && this.onImage);
this.showItem("context-sharevideo", shareEnabled && this.onVideo);
this.setItemAttr("context-sharevideo", "disabled", !this.mediaURL);
},
initSpellingItems: function() {
- var canSpell = InlineSpellCheckerUI.canSpellCheck;
+ var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
var onMisspelling = InlineSpellCheckerUI.overMisspelling;
var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
this.showItem("spell-check-enabled", canSpell);
this.showItem("spell-separator", canSpell || this.onEditableArea);
document.getElementById("spell-check-enabled")
.setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled);
this.showItem("spell-add-to-dictionary", onMisspelling);
@@ -354,17 +354,17 @@ nsContextMenu.prototype = {
InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode,
suggestionsSeparator, 5);
this.showItem("spell-no-suggestions", numsug == 0);
}
else
this.showItem("spell-no-suggestions", false);
// dictionary list
- this.showItem("spell-dictionaries", InlineSpellCheckerUI.enabled);
+ this.showItem("spell-dictionaries", canSpell && InlineSpellCheckerUI.enabled);
if (canSpell) {
var dictMenu = document.getElementById("spell-dictionaries-menu");
var dictSep = document.getElementById("spell-language-separator");
InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep);
this.showItem("spell-add-dictionaries-main", false);
}
else if (this.onEditableArea) {
// when there is no spellchecker but we might be able to spellcheck
@@ -515,16 +515,17 @@ nsContextMenu.prototype = {
this.onMathML = false;
this.inFrame = false;
this.inSyntheticDoc = false;
this.hasBGImage = false;
this.bgImageURL = "";
this.onEditableArea = false;
this.isDesignMode = false;
this.onCTPPlugin = false;
+ this.canSpellCheck = false;
// Remember the node that was clicked.
this.target = aNode;
this.browser = this.target.ownerDocument.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
@@ -605,16 +606,23 @@ nsContextMenu.prototype = {
}
}
else if ((this.target instanceof HTMLEmbedElement ||
this.target instanceof HTMLObjectElement ||
this.target instanceof HTMLAppletElement) &&
this.target.mozMatchesSelector(":-moz-handler-clicktoplay")) {
this.onCTPPlugin = true;
}
+
+ this.canSpellCheck = this._isSpellCheckEnabled(this.target);
+ }
+ else if (this.target.nodeType == Node.TEXT_NODE) {
+ // For text nodes, look at the parent node to determine the spellcheck attribute.
+ this.canSpellCheck = this.target.parentNode &&
+ this._isSpellCheckEnabled(this.target);
}
// Second, bubble out, looking for items of interest that can have childen.
// Always pick the innermost link, background image, etc.
const XMLNS = "http://www.w3.org/XML/1998/namespace";
var elem = this.target;
while (elem) {
if (elem.nodeType == Node.ELEMENT_NODE) {
@@ -700,17 +708,17 @@ nsContextMenu.prototype = {
this.onLoadedImage = false;
this.onCompletedImage = false;
this.onMathML = false;
this.inFrame = false;
this.hasBGImage = false;
this.isDesignMode = true;
this.onEditableArea = true;
InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
- var canSpell = InlineSpellCheckerUI.canSpellCheck;
+ var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
this.showItem("spell-check-enabled", canSpell);
this.showItem("spell-separator", canSpell);
}
}
}
},
@@ -741,16 +749,33 @@ nsContextMenu.prototype = {
// until we do.
return this.linkProtocol && !(
this.linkProtocol == "mailto" ||
this.linkProtocol == "javascript" ||
this.linkProtocol == "news" ||
this.linkProtocol == "snews" );
},
+ _isSpellCheckEnabled: function(aNode) {
+ // We can always force-enable spellchecking on textboxes
+ if (this.isTargetATextBox(aNode)) {
+ return true;
+ }
+ // We can never spell check something which is not content editable
+ var editable = aNode.isContentEditable;
+ if (!editable && aNode.ownerDocument) {
+ editable = aNode.ownerDocument.designMode == "on";
+ }
+ if (!editable) {
+ return false;
+ }
+ // Otherwise make sure that nothing in the parent chain disables spellchecking
+ return aNode.spellcheck;
+ },
+
// Open linked-to URL in a new window.
openLink : function () {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
openLinkIn(this.linkURL, "window",
{ charset: doc.characterSet,
referrerURI: doc.documentURIObject });
},
--- a/browser/base/content/test/test_contextmenu.html
+++ b/browser/base/content/test/test_contextmenu.html
@@ -117,17 +117,18 @@ function getVisibleMenuItems(aMenu, aDat
var label = item.getAttribute("label");
ok(label.length, "menuitem " + item.id + " has a label");
if (isSpellSuggestion) {
is(key, "", "Spell suggestions shouldn't have an access key");
items.push("*" + label);
} else if (isGenerated) {
items.push("+" + label);
} else if (item.id.indexOf("spell-check-dictionary-") != 0 &&
- item.id != "spell-no-suggestions") {
+ item.id != "spell-no-suggestions" &&
+ item.id != "spell-add-dictionaries-main") {
ok(key, "menuitem " + item.id + " has an access key");
if (accessKeys[key])
ok(false, "menuitem " + item.id + " has same accesskey as " + accessKeys[key]);
else
accessKeys[key] = item.id;
}
if (!isSpellSuggestion && !isGenerated) {
items.push(item.id);
@@ -655,26 +656,44 @@ function runTest(testNum) {
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null
].concat(inspectItems));
closeContextMenu();
+ openContextMenuFor(inputspellfalse, false, true); // Invoke context menu for next test.
+ break;
+
+ case 20:
+ // Context menu for text input field with spellcheck=false
+ checkContextMenu(["context-undo", false,
+ "---", null,
+ "context-cut", false,
+ "context-copy", false,
+ "context-paste", null, // ignore clipboard state
+ "context-delete", false,
+ "---", null,
+ "context-selectall", true,
+ "---", null,
+ "spell-add-dictionaries-main", true,
+ ].concat(inspectItems));
+
+ closeContextMenu();
openContextMenuFor(link); // Invoke context menu for next test.
break;
- case 20:
+ case 21:
executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
closeContextMenu();
openContextMenuFor(pagemenu); // Invoke context menu for next test.
break;
- case 21:
+ case 22:
// Context menu for element with assigned content context menu
checkContextMenu(["+Plain item", {type: "", icon: "", checked: false, disabled: false},
"+Disabled item", {type: "", icon: "", checked: false, disabled: true},
"+Item w/ textContent", {type: "", icon: "", checked: false, disabled: false},
"---", null,
"+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false},
"---", null,
"+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false},
@@ -715,17 +734,17 @@ function runTest(testNum) {
openContextMenuFor(dom_full_screen, true); // Invoke context menu for next test.
}
subwindow.addEventListener("mozfullscreenchange", openDomFullScreen, false);
SpecialPowers.setBoolPref("full-screen-api.approval-required", false);
SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
full_screen_element.mozRequestFullScreen();
break;
- case 22:
+ case 23:
// Context menu for DOM Fullscreen mode (NOTE: this is *NOT* on an img)
checkContextMenu(["context-leave-dom-fullscreen", true,
"---", null,
"context-back", false,
"context-forward", false,
"context-reload", true,
"---", null,
"context-bookmarkpage", true,
@@ -744,17 +763,17 @@ function runTest(testNum) {
SpecialPowers.clearUserPref("full-screen-api.approval-required");
SpecialPowers.clearUserPref("full-screen-api.allow-trusted-requests-only");
openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
}
subwindow.addEventListener("mozfullscreenchange", openPagemenu, false);
subwindow.document.mozCancelFullScreen();
break;
- case 23:
+ case 24:
// Context menu for element with assigned content context menu
// The shift key should bypass content context menu processing
checkContextMenu(["context-back", false,
"context-forward", false,
"context-reload", true,
"---", null,
"context-bookmarkpage", true,
"context-savepage", true,
@@ -765,33 +784,33 @@ function runTest(testNum) {
"context-viewsource", true,
"context-viewinfo", true
].concat(inspectItems));
closeContextMenu();
selectText(selecttext); // Select text prior to opening context menu.
openContextMenuFor(selecttext); // Invoke context menu for next test.
return;
- case 24:
+ case 25:
// Context menu for selected text
if (SpecialPowers.Services.appinfo.OS == "Darwin") {
// This test is only enabled on Mac due to bug 736399.
checkContextMenu(["context-copy", true,
"context-selectall", true,
"---", null,
"context-searchselect", true,
"context-viewpartialsource-selection", true
].concat(inspectItems));
}
closeContextMenu();
selectText(selecttextlink); // Select text prior to opening context menu.
openContextMenuFor(selecttextlink); // Invoke context menu for next test.
return;
- case 25:
+ case 26:
// Context menu for selected text which matches valid URL pattern
if (SpecialPowers.Services.appinfo.OS == "Darwin") {
// This test is only enabled on Mac due to bug 736399.
if (perWindowPrivateBrowsing) {
checkContextMenu(["context-openlinkincurrent", true,
"context-openlinkintab", true,
"context-openlink", true,
"context-openlinkprivate", true,
@@ -821,17 +840,17 @@ function runTest(testNum) {
}
closeContextMenu();
// clear the selection because following tests don't expect any selection
subwindow.getSelection().removeAllRanges();
openContextMenuFor(imagelink)
break;
- case 26:
+ case 27:
// Context menu for image link
if (perWindowPrivateBrowsing) {
checkContextMenu(["context-openlinkintab", true,
"context-openlink", true,
"context-openlinkprivate", true,
"---", null,
"context-bookmarklink", true,
"context-savelink", true,
@@ -864,17 +883,17 @@ function runTest(testNum) {
"context-viewimageinfo", true
].concat(inspectItems));
}
closeContextMenu();
selectInputText(select_inputtext); // Select text prior to opening context menu.
openContextMenuFor(select_inputtext); // Invoke context menu for next test.
return;
- case 27:
+ case 28:
// Context menu for selected text in input
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", true,
"---", null,
@@ -883,17 +902,17 @@ function runTest(testNum) {
"---", null,
"spell-check-enabled", true
].concat(inspectItems));
closeContextMenu();
selectInputText(select_inputtext_password); // Select text prior to opening context menu.
openContextMenuFor(select_inputtext_password); // Invoke context menu for next test.
return;
- case 28:
+ case 29:
// Context menu for selected text in input[type="password"]
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", true,
"---", null,
@@ -906,17 +925,17 @@ function runTest(testNum) {
"---", null,
"spell-add-dictionaries", true], null
].concat(inspectItems));
closeContextMenu();
subwindow.getSelection().removeAllRanges();
openContextMenuFor(plugin);
return;
- case 29:
+ case 30:
// Context menu for click-to-play blocked plugin
checkContextMenu(["context-ctp-play", true,
"context-ctp-hide", true,
"---", null,
"context-back", false,
"context-forward", false,
"context-reload", true,
"---", null,
@@ -963,17 +982,17 @@ function runTest(testNum) {
var testNum = 1;
var subwindow, chromeWin, contextMenu, lastElement;
var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
iframe, video_in_iframe, image_in_iframe, textarea, contenteditable,
inputspell, pagemenu, dom_full_screen, plainTextItems, audio_in_video,
selecttext, selecttextlink, imagelink, select_inputtext, select_inputtext_password,
- plugin;
+ plugin, inputspellfalse;
function startTest() {
chromeWin = SpecialPowers.wrap(subwindow)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1005,16 +1024,17 @@ function startTest() {
iframe = subwindow.document.getElementById("test-iframe");
video_in_iframe = subwindow.document.getElementById("test-video-in-iframe").contentDocument.getElementsByTagName("video")[0];
video_in_iframe.pause();
image_in_iframe = subwindow.document.getElementById("test-image-in-iframe").contentDocument.getElementsByTagName("img")[0];
textarea = subwindow.document.getElementById("test-textarea");
contenteditable = subwindow.document.getElementById("test-contenteditable");
contenteditable.focus(); // content editable needs to be focused to enable spellcheck
inputspell = subwindow.document.getElementById("test-input-spellcheck");
+ inputspellfalse = subwindow.document.getElementById("test-contenteditable-spellcheck-false");
pagemenu = subwindow.document.getElementById("test-pagemenu");
dom_full_screen = subwindow.document.getElementById("test-dom-full-screen");
selecttext = subwindow.document.getElementById("test-select-text");
selecttextlink = subwindow.document.getElementById("test-select-text-link");
select_inputtext = subwindow.document.getElementById("test-select-input-text");
select_inputtext_password = subwindow.document.getElementById("test-select-input-text-type-password");
plugin = subwindow.document.getElementById("test-plugin");