summaryrefslogtreecommitdiffstats
path: root/editor/spellchecker/tests
diff options
context:
space:
mode:
Diffstat (limited to 'editor/spellchecker/tests')
-rw-r--r--editor/spellchecker/tests/bug1200533_subframe.html15
-rw-r--r--editor/spellchecker/tests/bug1204147_subframe.html11
-rw-r--r--editor/spellchecker/tests/bug1204147_subframe2.html9
-rw-r--r--editor/spellchecker/tests/bug678842_subframe.html8
-rw-r--r--editor/spellchecker/tests/bug717433_subframe.html8
-rw-r--r--editor/spellchecker/tests/chrome.toml7
-rw-r--r--editor/spellchecker/tests/de-DE/de_DE.aff2
-rw-r--r--editor/spellchecker/tests/de-DE/de_DE.dic6
-rw-r--r--editor/spellchecker/tests/en-AU/en_AU.aff2
-rw-r--r--editor/spellchecker/tests/en-AU/en_AU.dic4
-rw-r--r--editor/spellchecker/tests/en-GB/en_GB.aff2
-rw-r--r--editor/spellchecker/tests/en-GB/en_GB.dic4
-rw-r--r--editor/spellchecker/tests/mochitest.toml104
-rw-r--r--editor/spellchecker/tests/multiple_content_languages_subframe.html13
-rw-r--r--editor/spellchecker/tests/ru-RU/ru_RU.aff1
-rw-r--r--editor/spellchecker/tests/ru-RU/ru_RU.dic2
-rw-r--r--editor/spellchecker/tests/spellcheck.js36
-rw-r--r--editor/spellchecker/tests/test_async_UpdateCurrentDictionary.html60
-rw-r--r--editor/spellchecker/tests/test_bug1100966.html74
-rw-r--r--editor/spellchecker/tests/test_bug1154791.html74
-rw-r--r--editor/spellchecker/tests/test_bug1200533.html163
-rw-r--r--editor/spellchecker/tests/test_bug1204147.html115
-rw-r--r--editor/spellchecker/tests/test_bug1205983.html134
-rw-r--r--editor/spellchecker/tests/test_bug1209414.html144
-rw-r--r--editor/spellchecker/tests/test_bug1219928.html69
-rw-r--r--editor/spellchecker/tests/test_bug1365383.html46
-rw-r--r--editor/spellchecker/tests/test_bug1368544.html91
-rw-r--r--editor/spellchecker/tests/test_bug1402822.html114
-rw-r--r--editor/spellchecker/tests/test_bug1418629.html210
-rw-r--r--editor/spellchecker/tests/test_bug1497480.html94
-rw-r--r--editor/spellchecker/tests/test_bug1602526.html57
-rw-r--r--editor/spellchecker/tests/test_bug1761273.html96
-rw-r--r--editor/spellchecker/tests/test_bug1773802.html96
-rw-r--r--editor/spellchecker/tests/test_bug1837268.html79
-rw-r--r--editor/spellchecker/tests/test_bug338427.html61
-rw-r--r--editor/spellchecker/tests/test_bug366682.html65
-rw-r--r--editor/spellchecker/tests/test_bug432225.html72
-rw-r--r--editor/spellchecker/tests/test_bug484181.html73
-rw-r--r--editor/spellchecker/tests/test_bug596333.html135
-rw-r--r--editor/spellchecker/tests/test_bug636465.html54
-rw-r--r--editor/spellchecker/tests/test_bug678842.html106
-rw-r--r--editor/spellchecker/tests/test_bug697981.html137
-rw-r--r--editor/spellchecker/tests/test_bug717433.html111
-rw-r--r--editor/spellchecker/tests/test_multiple_content_languages.html176
-rw-r--r--editor/spellchecker/tests/test_nsIEditorSpellCheck_ReplaceWord.html64
-rw-r--r--editor/spellchecker/tests/test_spellcheck_after_edit.html194
-rw-r--r--editor/spellchecker/tests/test_spellcheck_after_pressing_navigation_key.html77
-rw-r--r--editor/spellchecker/tests/test_spellcheck_selection.html36
-rw-r--r--editor/spellchecker/tests/test_suggest.html42
49 files changed, 3353 insertions, 0 deletions
diff --git a/editor/spellchecker/tests/bug1200533_subframe.html b/editor/spellchecker/tests/bug1200533_subframe.html
new file mode 100644
index 0000000000..f095c0601f
--- /dev/null
+++ b/editor/spellchecker/tests/bug1200533_subframe.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Language" content="en-US">
+</head>
+<body>
+<textarea id="none">root en-US</textarea>
+<textarea id="en-GB" lang="en-GB">root en-US, but element en-GB</textarea>
+<textarea id="en-gb" lang="en-gb">root en-US, but element en-gb (lower case)</textarea>
+<textarea id="en-ZA-not-avail" lang="en-ZA">root en-US, but element en-ZA (which is not installed)</textarea>
+<textarea id="en-generic" lang="en">root en-US, but element en</textarea>
+<textarea id="en" lang="en">root en-US, but element en</textarea>
+<textarea id="ko-not-avail" lang="ko">root en-US, but element ko (which is not installed)</textarea>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/bug1204147_subframe.html b/editor/spellchecker/tests/bug1204147_subframe.html
new file mode 100644
index 0000000000..a9b1225cd9
--- /dev/null
+++ b/editor/spellchecker/tests/bug1204147_subframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="en-GB" lang="en-GB">element en-GB</textarea>
+<textarea id="en-US" lang="testing-XX">element should default to en-US</textarea>
+
+<div id="trouble-maker" contenteditable>the presence of this div triggers the faulty code path</div>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/bug1204147_subframe2.html b/editor/spellchecker/tests/bug1204147_subframe2.html
new file mode 100644
index 0000000000..935777bd99
--- /dev/null
+++ b/editor/spellchecker/tests/bug1204147_subframe2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="en-GB" lang="en-GB">element en-GB</textarea>
+<textarea id="en-US" lang="testing-XX">element should default to en-US</textarea>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/bug678842_subframe.html b/editor/spellchecker/tests/bug678842_subframe.html
new file mode 100644
index 0000000000..39d578ee41
--- /dev/null
+++ b/editor/spellchecker/tests/bug678842_subframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="textarea" lang="testing-XXX"></textarea>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/bug717433_subframe.html b/editor/spellchecker/tests/bug717433_subframe.html
new file mode 100644
index 0000000000..3c2927e88f
--- /dev/null
+++ b/editor/spellchecker/tests/bug717433_subframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<textarea id="textarea" lang="en"></textarea>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/chrome.toml b/editor/spellchecker/tests/chrome.toml
new file mode 100644
index 0000000000..f5fbdfac31
--- /dev/null
+++ b/editor/spellchecker/tests/chrome.toml
@@ -0,0 +1,7 @@
+[DEFAULT]
+prefs = ["gfx.font_loader.delay=0"]
+
+skip-if = ["os == 'android'"]
+
+["test_nsIEditorSpellCheck_ReplaceWord.html"]
+support-files = ["spellcheck.js"]
diff --git a/editor/spellchecker/tests/de-DE/de_DE.aff b/editor/spellchecker/tests/de-DE/de_DE.aff
new file mode 100644
index 0000000000..5dc6896b6d
--- /dev/null
+++ b/editor/spellchecker/tests/de-DE/de_DE.aff
@@ -0,0 +1,2 @@
+# Affix file for German English dictionary
+# Fake file, nothing here.
diff --git a/editor/spellchecker/tests/de-DE/de_DE.dic b/editor/spellchecker/tests/de-DE/de_DE.dic
new file mode 100644
index 0000000000..415c216861
--- /dev/null
+++ b/editor/spellchecker/tests/de-DE/de_DE.dic
@@ -0,0 +1,6 @@
+5
+ein
+guter
+heute
+ist
+Tag
diff --git a/editor/spellchecker/tests/en-AU/en_AU.aff b/editor/spellchecker/tests/en-AU/en_AU.aff
new file mode 100644
index 0000000000..e0c467248d
--- /dev/null
+++ b/editor/spellchecker/tests/en-AU/en_AU.aff
@@ -0,0 +1,2 @@
+# Affix file for British English dictionary
+# Fake file, nothing here.
diff --git a/editor/spellchecker/tests/en-AU/en_AU.dic b/editor/spellchecker/tests/en-AU/en_AU.dic
new file mode 100644
index 0000000000..0a1be725d4
--- /dev/null
+++ b/editor/spellchecker/tests/en-AU/en_AU.dic
@@ -0,0 +1,4 @@
+3
+Mary
+Paul
+Peter
diff --git a/editor/spellchecker/tests/en-GB/en_GB.aff b/editor/spellchecker/tests/en-GB/en_GB.aff
new file mode 100644
index 0000000000..e0c467248d
--- /dev/null
+++ b/editor/spellchecker/tests/en-GB/en_GB.aff
@@ -0,0 +1,2 @@
+# Affix file for British English dictionary
+# Fake file, nothing here.
diff --git a/editor/spellchecker/tests/en-GB/en_GB.dic b/editor/spellchecker/tests/en-GB/en_GB.dic
new file mode 100644
index 0000000000..0a1be725d4
--- /dev/null
+++ b/editor/spellchecker/tests/en-GB/en_GB.dic
@@ -0,0 +1,4 @@
+3
+Mary
+Paul
+Peter
diff --git a/editor/spellchecker/tests/mochitest.toml b/editor/spellchecker/tests/mochitest.toml
new file mode 100644
index 0000000000..cd0afc4d9b
--- /dev/null
+++ b/editor/spellchecker/tests/mochitest.toml
@@ -0,0 +1,104 @@
+[DEFAULT]
+prefs = ["gfx.font_loader.delay=0"]
+
+skip-if = ["os == 'android'"]
+support-files = [
+ "en-GB/en_GB.dic",
+ "en-GB/en_GB.aff",
+ "en-AU/en_AU.dic",
+ "en-AU/en_AU.aff",
+ "de-DE/de_DE.dic",
+ "de-DE/de_DE.aff",
+ "ru-RU/ru_RU.dic",
+ "ru-RU/ru_RU.aff",
+ "spellcheck.js",
+]
+
+["test_async_UpdateCurrentDictionary.html"]
+
+["test_bug366682.html"]
+
+["test_bug432225.html"]
+
+["test_bug484181.html"]
+
+["test_bug596333.html"]
+
+["test_bug636465.html"]
+
+["test_bug678842.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+support-files = ["bug678842_subframe.html"]
+
+["test_bug697981.html"]
+
+["test_bug717433.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+support-files = ["bug717433_subframe.html"]
+
+["test_bug1100966.html"]
+
+["test_bug1154791.html"]
+
+["test_bug1200533.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+support-files = ["bug1200533_subframe.html"]
+
+["test_bug1204147.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+support-files = [
+ "bug1204147_subframe.html",
+ "bug1204147_subframe2.html",
+]
+
+["test_bug1205983.html"]
+
+["test_bug1209414.html"]
+
+["test_bug1219928.html"]
+skip-if = ["true"]
+
+["test_bug1365383.html"]
+
+["test_bug1368544.html"]
+
+["test_bug1402822.html"]
+
+["test_bug1418629.html"]
+
+["test_bug1497480.html"]
+
+["test_bug1602526.html"]
+
+["test_bug1761273.html"]
+
+["test_bug1773802.html"]
+
+["test_bug1837268.html"]
+
+["test_multiple_content_languages.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+support-files = ["multiple_content_languages_subframe.html"]
+
+["test_spellcheck_after_edit.html"]
+
+["test_spellcheck_after_pressing_navigation_key.html"]
+
+["test_spellcheck_selection.html"]
+
+["test_suggest.html"]
diff --git a/editor/spellchecker/tests/multiple_content_languages_subframe.html b/editor/spellchecker/tests/multiple_content_languages_subframe.html
new file mode 100644
index 0000000000..da6a4ed664
--- /dev/null
+++ b/editor/spellchecker/tests/multiple_content_languages_subframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Language" content="en-US, en-GB, ko, en-CA">
+</head>
+<body>
+<textarea id="none">root en-US and en-GB</textarea>
+<textarea id="en-GB" lang="en-GB">root multiple, but element only en-GB</textarea>
+<textarea id="en-ZA-not-avail" lang="en-ZA">root multiple en, but element en-ZA (which is not installed)</textarea>
+<textarea id="en" lang="en">root multiple en, but element en</textarea>
+<textarea id="ko-not-avail" lang="ko">root multiple en, but element ko (which is not installed)</textarea>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/ru-RU/ru_RU.aff b/editor/spellchecker/tests/ru-RU/ru_RU.aff
new file mode 100644
index 0000000000..c6cf721484
--- /dev/null
+++ b/editor/spellchecker/tests/ru-RU/ru_RU.aff
@@ -0,0 +1 @@
+SET KOI8-R
diff --git a/editor/spellchecker/tests/ru-RU/ru_RU.dic b/editor/spellchecker/tests/ru-RU/ru_RU.dic
new file mode 100644
index 0000000000..dbfe3a9f23
--- /dev/null
+++ b/editor/spellchecker/tests/ru-RU/ru_RU.dic
@@ -0,0 +1,2 @@
+1
+правильный
diff --git a/editor/spellchecker/tests/spellcheck.js b/editor/spellchecker/tests/spellcheck.js
new file mode 100644
index 0000000000..b6a1586628
--- /dev/null
+++ b/editor/spellchecker/tests/spellcheck.js
@@ -0,0 +1,36 @@
+function isSpellingCheckOk(aEditor, aMisspelledWords, aTodo = false) {
+ var selcon = aEditor.selectionController;
+ var sel = selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+ var numWords = sel.rangeCount;
+
+ if (aTodo) {
+ todo_is(
+ numWords,
+ aMisspelledWords.length,
+ "Correct number of misspellings and words."
+ );
+ } else {
+ is(
+ numWords,
+ aMisspelledWords.length,
+ "Correct number of misspellings and words."
+ );
+ }
+
+ if (numWords !== aMisspelledWords.length) {
+ return false;
+ }
+
+ for (var i = 0; i < numWords; ++i) {
+ var word = String(sel.getRangeAt(i));
+ if (aTodo) {
+ todo_is(word, aMisspelledWords[i], "Misspelling is what we think it is.");
+ } else {
+ is(word, aMisspelledWords[i], "Misspelling is what we think it is.");
+ }
+ if (word !== aMisspelledWords[i]) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/editor/spellchecker/tests/test_async_UpdateCurrentDictionary.html b/editor/spellchecker/tests/test_async_UpdateCurrentDictionary.html
new file mode 100644
index 0000000000..d586eef721
--- /dev/null
+++ b/editor/spellchecker/tests/test_async_UpdateCurrentDictionary.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=856270
+-->
+<head>
+ <title>Test for Bug 856270 - Async UpdateCurrentDictionary</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=856270">Mozilla Bug 856270</a>
+<p id="display"></p>
+<div id="content">
+<textarea id="editor" spellcheck="true"></textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(start);
+
+function start() {
+ var textarea = document.getElementById("editor");
+ textarea.focus();
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ )
+ onSpellCheck(textarea, function() {
+ var isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
+ ok(isc, "Inline spell checker should exist after focus and spell check");
+ var sc = isc.spellChecker;
+
+ sc.setCurrentDictionaries(["testing-XX"]).then(() => {
+ is(true, false, "Setting a non-existent dictionary should fail");
+ }, () => {
+ let currentDictionaries = sc.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 0, "expected no dictionaries");
+ // First, set the lang attribute on the textarea, call Update, and make
+ // sure the spell checker's language was updated appropriately.
+ var lang = "en-US";
+ textarea.setAttribute("lang", lang);
+ sc.UpdateCurrentDictionary(function() {
+ currentDictionaries = sc.getCurrentDictionaries();
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(sc.getCurrentDictionaries()[0], lang,
+ "UpdateCurrentDictionary should set the current dictionary.");
+ sc.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1100966.html b/editor/spellchecker/tests/test_bug1100966.html
new file mode 100644
index 0000000000..15f1e03a3b
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1100966.html
@@ -0,0 +1,74 @@
+<!DOCTYPE>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1100966
+-->
+<head>
+ <title>Test for Bug 1100966</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="display">
+</div>
+<div id="content" contenteditable>
+=====<br>
+correct<br>
+fivee sixx<br>
+====
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+/** Test for Bug 1100966 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var div = document.getElementById("content");
+ div.focus();
+ synthesizeMouseAtCenter(div, {});
+
+ getSpellChecker().UpdateCurrentDictionary(() => {
+ sendString(" ");
+ SimpleTest.executeSoon(function() {
+ sendString("a");
+ SimpleTest.executeSoon(function() {
+ synthesizeKey("KEY_Backspace");
+
+ maybeOnSpellCheck(div, function() {
+ var sel = getSpellCheckSelection();
+ is(sel.rangeCount, 2, "We should have two misspelled words");
+ is(String(sel.getRangeAt(0)), "fivee", "Correct misspelled word");
+ is(String(sel.getRangeAt(1)), "sixx", "Correct misspelled word");
+
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+});
+
+function getEditor() {
+ var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ return editingSession.getEditorForWindow(window);
+}
+
+function getSpellChecker() {
+ return getEditor().getInlineSpellChecker(false).spellChecker;
+}
+
+function getSpellCheckSelection() {
+ var selcon = getEditor().selectionController;
+ return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+}
+
+</script>
+</body>
+
+</html>
diff --git a/editor/spellchecker/tests/test_bug1154791.html b/editor/spellchecker/tests/test_bug1154791.html
new file mode 100644
index 0000000000..1552c7eff8
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1154791.html
@@ -0,0 +1,74 @@
+<!DOCTYPE>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1154791
+-->
+<head>
+ <title>Test for Bug 1154791</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="display">
+</div>
+
+<div id="content" contenteditable>
+<tt>thiss onee is stilll a</tt>
+</div>
+
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+/** Test for Bug 1154791 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var div = document.getElementById("content");
+ div.focus();
+ getSpellChecker().UpdateCurrentDictionary(() => {
+ synthesizeMouseAtCenter(div, {});
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("KEY_ArrowLeft");
+
+ SimpleTest.executeSoon(function() {
+ synthesizeKey("KEY_Backspace");
+ SimpleTest.executeSoon(function() {
+ sendString(" ");
+
+ maybeOnSpellCheck(div, function() {
+ var sel = getSpellCheckSelection();
+ is(sel.rangeCount, 2, "We should have two misspelled words");
+ is(String(sel.getRangeAt(0)), "thiss", "Correct misspelled word");
+ is(String(sel.getRangeAt(1)), "onee", "Correct misspelled word");
+
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+});
+
+function getEditor() {
+ var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ return editingSession.getEditorForWindow(window);
+}
+
+function getSpellChecker() {
+ return getEditor().getInlineSpellChecker(false).spellChecker;
+}
+
+function getSpellCheckSelection() {
+ var selcon = getEditor().selectionController;
+ return selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+}
+
+</script>
+</body>
+
+</html>
diff --git a/editor/spellchecker/tests/test_bug1200533.html b/editor/spellchecker/tests/test_bug1200533.html
new file mode 100644
index 0000000000..0f75825013
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1200533.html
@@ -0,0 +1,163 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1200533
+-->
+<head>
+ <title>Test for Bug 1200533</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1200533">Mozilla Bug 1200533</a>
+<p id="display"></p>
+<iframe id="content"></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" ttype="application/javascript">
+
+/** Test for Bug 1200533 **/
+/** Visit the elements defined above and check the dictionary we got **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+
+var tests = [
+ // text area, value of spellchecker.dictionary, result.
+ // Result: Document language.
+ [ "none", "", "en-US" ],
+ // Result: Element language.
+ [ "en-GB", "", "en-GB" ],
+ [ "en-gb", "", "en-GB" ],
+ // Result: Random en-* or en-US (if application locale is en-US).
+ [ "en-ZA-not-avail", "", "*" ],
+ [ "en-generic", "", "*" ],
+ [ "en", "", "*" ],
+ // Result: Locale.
+ [ "ko-not-avail", "", "en-US" ],
+
+ // Result: Preference value in all cases.
+ [ "en-ZA-not-avail", "en-AU", "en-AU" ],
+ [ "en-generic", "en-AU", "en-AU" ],
+ [ "ko-not-avail", "en-AU", "en-AU" ],
+
+ // Result: Random en-*.
+ [ "en-ZA-not-avail", "de-DE", "*" ],
+ [ "en-generic", "de-DE", "*" ],
+ // Result: Preference value.
+ [ "ko-not-avail", "de-DE", "de-DE" ],
+ ];
+
+var loadCount = 0;
+var retrying = false;
+var script;
+
+var loadListener = async function(evt) {
+ if (loadCount == 0) {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install en-GB, en-AU and de-DE dictionaries.
+ var en_GB = dir.clone();
+ var en_AU = dir.clone();
+ var de_DE = dir.clone();
+ en_GB.append("en-GB");
+ en_AU.append("en-AU");
+ de_DE.append("de-DE");
+ hunspell.addDirectory(en_GB);
+ hunspell.addDirectory(en_AU);
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("check-existence",
+ () => [en_GB.exists(), en_AU.exists(),
+ de_DE.exists()]);
+ addMessageListener("destroy", () => {
+ hunspell.removeDirectory(en_GB);
+ hunspell.removeDirectory(en_AU);
+ hunspell.removeDirectory(de_DE);
+ });
+ });
+ var existenceChecks = await script.sendQuery("check-existence");
+ is(existenceChecks[0], true, "true expected (en-GB directory should exist)");
+ is(existenceChecks[1], true, "true expected (en-AU directory should exist)");
+ is(existenceChecks[2], true, "true expected (de-DE directory should exist)");
+ }
+
+ SpecialPowers.pushPrefEnv({set: [["spellchecker.dictionary", tests[loadCount][1]]]},
+ function() { continueTest(evt); });
+};
+
+function continueTest(evt) {
+ var doc = evt.target.contentDocument;
+ var elem = doc.getElementById(tests[loadCount][0]);
+ var editor = SpecialPowers.wrap(elem).editor;
+ editor.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor.getInlineSpellChecker(true);
+ const is_en_US = SpecialPowers.Services.locale.appLocaleAsBCP47 == "en-US";
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(elem, async function() {
+ var spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries;
+ try {
+ currentDictionaries = spellchecker.getCurrentDictionaries();
+ } catch (e) {}
+
+ if (!currentDictionaries && !retrying) {
+ // It's possible for an asynchronous font-list update to cause a reflow
+ // that disrupts the async spell-check and results in not getting a
+ // current dictionary here; if that happens, we retry the same testcase
+ // by reloading the iframe without bumping loadCount.
+ info(`No current dictionary: retrying testcase ${loadCount}`);
+ retrying = true;
+ } else {
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ let dict = currentDictionaries[0];
+ if (tests[loadCount][2] != "*") {
+ is(dict, tests[loadCount][2], "expected " + tests[loadCount][2]);
+ } else if (is_en_US && tests[loadCount][0].startsWith("en")) {
+ // Current application locale is en-US and content lang is en or
+ // en-unknown, so we should use en-US dictionary as default.
+ is(dict, "en-US", "expected en-US that is application locale");
+ } else {
+ var gotEn = (dict == "en-GB" || dict == "en-AU" || dict == "en-US");
+ is(gotEn, true, "expected en-AU or en-GB or en-US");
+ }
+
+ loadCount++;
+ retrying = false;
+ }
+
+ if (loadCount < tests.length) {
+ // Load the iframe again.
+ content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug1200533_subframe.html?firstload=false";
+ } else {
+ // Remove the fake dictionaries again, since it's otherwise picked up by later tests.
+ await script.sendQuery("destroy");
+
+ SimpleTest.finish();
+ }
+ });
+}
+
+content.addEventListener("load", loadListener);
+
+content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug1200533_subframe.html?firstload=true";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1204147.html b/editor/spellchecker/tests/test_bug1204147.html
new file mode 100644
index 0000000000..39935c0d5c
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1204147.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1204147
+-->
+<head>
+ <title>Test for Bug 1204147</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1204147">Mozilla Bug 1204147</a>
+<p id="display"></p>
+<iframe id="content"></iframe>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1204147 **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+// Load a subframe containing an editor with using "en-GB". At first
+// load, it will set the dictionary to "en-GB". The bug was that a content preference
+// was also created. At second load, we check the dictionary for another element,
+// one that should use "en-US". With the bug corrected, we get "en-US", before
+// we got "en-GB" from the content preference.
+
+var firstLoad = true;
+var script;
+
+var loadListener = async function(evt) {
+ if (firstLoad) {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install en-GB dictionary.
+ let en_GB = dir.clone();
+ en_GB.append("en-GB");
+ hunspell.addDirectory(en_GB);
+
+ addMessageListener("en_GB-exists", () => en_GB.exists());
+ addMessageListener("destroy", () => hunspell.removeDirectory(en_GB));
+ });
+ is(await script.sendQuery("en_GB-exists"), true,
+ "true expected (en-GB directory should exist)");
+ }
+
+ var doc = evt.target.contentDocument;
+ var elem;
+ if (firstLoad) {
+ elem = doc.getElementById("en-GB");
+ } else {
+ elem = doc.getElementById("en-US");
+ }
+
+ var editor = SpecialPowers.wrap(elem).editor;
+ editor.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor.getInlineSpellChecker(true);
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(elem, async function() {
+ let spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ let currentDictionary = currentDictionaries[0];
+ if (firstLoad) {
+ firstLoad = false;
+
+ // First time around, the element's language should be used.
+ is(currentDictionary, "en-GB", "unexpected lang " + currentDictionary + " instead of en-GB");
+
+ // Note that on second load, we load a different page, which does NOT have the trouble-causing
+ // contenteditable in it. Sadly, loading the same page with the trouble-maker in it
+ // doesn't allow the retrieval of the spell check dictionary used for the element,
+ // because the trouble-maker causes the 'global' spell check dictionary to be set to "en-GB"
+ // (since it picks the first one from the list) before we have the chance to retrieve
+ // the dictionary for the element (which happens asynchonously after the spell check has completed).
+ content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug1204147_subframe2.html?firstload=false";
+ } else {
+ // Second time around, the element should default to en-US.
+ // Without the fix, the first run sets the content preference to en-GB for the whole site.
+ is(currentDictionary, "en-US", "unexpected lang " + currentDictionary + " instead of en-US");
+ content.removeEventListener("load", loadListener);
+
+ // Remove the fake en-GB dictionary again, since it's otherwise picked up by later tests.
+ await script.sendQuery("destroy");
+
+ // Reset the preference, so the last value we set doesn't collide with the next test.
+ SimpleTest.finish();
+ }
+ });
+};
+
+content.addEventListener("load", loadListener);
+
+content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug1204147_subframe.html?firstload=true";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1205983.html b/editor/spellchecker/tests/test_bug1205983.html
new file mode 100644
index 0000000000..052f46d312
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1205983.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1205983
+-->
+<head>
+ <title>Test for Bug 1205983</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205983">Mozilla Bug 1205983</a>
+<p id="display"></p>
+</div>
+
+<div contenteditable id="de-DE" lang="de-DE" onfocus="deFocus()">German heute ist ein guter Tag</div>
+<textarea id="en-US" lang="en-US" onfocus="enFocus()">Nogoodword today is a nice day</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function getMisspelledWords(editor) {
+ return editor.selectionController.getSelection(SpecialPowers.Ci.nsISelectionController.SELECTION_SPELLCHECK).toString();
+}
+
+var elem_de;
+var editor_de;
+var selcon_de;
+var script;
+
+var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+/** Test for Bug 1205983 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async function() {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install de-DE dictionary.
+ var de_DE = dir.clone();
+ de_DE.append("de-DE");
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("de_DE-exists", () => de_DE.exists());
+ addMessageListener("destroy", () => hunspell.removeDirectory(de_DE));
+ });
+ is(await script.sendQuery("de_DE-exists"), true,
+ "true expected (de_DE directory should exist)");
+
+ document.getElementById("de-DE").focus();
+});
+
+function deFocus() {
+ elem_de = document.getElementById("de-DE");
+
+ maybeOnSpellCheck(elem_de, function() {
+ var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ editor_de = editingSession.getEditorForWindow(window);
+ selcon_de = editor_de.selectionController;
+ var sel = selcon_de.getSelection(selcon_de.SELECTION_SPELLCHECK);
+
+ // Check that we spelled in German, so there is only one misspelled word.
+ is(sel.toString(), "German", "one misspelled word expected: German");
+
+ // Now focus the textarea, which requires English spelling.
+ document.getElementById("en-US").focus();
+ });
+}
+
+function enFocus() {
+ var elem_en = document.getElementById("en-US");
+ var editor_en =
+ SpecialPowers.wrap(elem_en)
+ .editor;
+ editor_en.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor_en.getInlineSpellChecker(true);
+
+ maybeOnSpellCheck(elem_en, async function() {
+ var spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ let currentDictionary = currentDictionaries[0];
+
+ // Check that the English dictionary is loaded and that the spell check has worked.
+ is(currentDictionary, "en-US", "expected en-US");
+ is(getMisspelledWords(editor_en), "Nogoodword", "one misspelled word expected: Nogoodword");
+
+ // So far all was boring. The important thing is whether the spell check result
+ // in the de-DE editor is still the same. After losing focus, no spell check
+ // updates should take place there.
+ var sel = selcon_de.getSelection(selcon_de.SELECTION_SPELLCHECK);
+ is(sel.toString(), "German", "one misspelled word expected: German");
+
+ // Remove the fake de_DE dictionary again.
+ await script.sendQuery("destroy");
+
+ // Focus again, so the spelling gets updated, but before we need to kill the focus handler.
+ elem_de.onfocus = null;
+ elem_de.blur();
+ elem_de.focus();
+
+ // After removal, the de_DE editor should refresh the spelling with en-US.
+ maybeOnSpellCheck(elem_de, function() {
+ var endSel = selcon_de.getSelection(selcon_de.SELECTION_SPELLCHECK);
+ // eslint-disable-next-line no-useless-concat
+ is(endSel.toString(), "heute" + "ist" + "ein" + "guter",
+ "some misspelled words expected: heute ist ein guter");
+
+ // If we don't reset this, we cause massive leaks.
+ selcon_de = null;
+ editor_de = null;
+
+ SimpleTest.finish();
+ });
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1209414.html b/editor/spellchecker/tests/test_bug1209414.html
new file mode 100644
index 0000000000..1f266758c2
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1209414.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209414
+-->
+<head>
+ <title>Test for Bug 1209414</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1209414">Mozilla Bug 1209414</a>
+<p id="display"></p>
+</div>
+
+<textarea id="de-DE" lang="de-DE">heute ist ein guter Tag - today is a good day</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const Ci = SpecialPowers.Ci;
+
+function getMisspelledWords(editor) {
+ return editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_SPELLCHECK).toString();
+}
+
+var elem_de;
+var editor_de;
+var script;
+
+/** Test for Bug 1209414 **/
+/*
+ * All we want to do in this test is change the spelling using a right-click and selection from the menu.
+ * This is necessary since all the other tests use setCurrentDictionaries() which doesn't reflect
+ * user behaviour.
+ */
+
+let { maybeOnSpellCheck, onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async function() {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var chromeWin = actorParent.rootFrameLoader
+ .ownerElement.ownerGlobal.browsingContext.topChromeWindow;
+ var contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
+ contextMenu.addEventListener("popupshown",
+ () => sendAsyncMessage("popupshown"));
+
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install de-DE dictionary.
+ let de_DE = dir.clone();
+ de_DE.append("de-DE");
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("hidepopup", function() {
+ var state = contextMenu.state;
+
+ // Select Language from the menu. Take a look at
+ // toolkit/modules/InlineSpellChecker.sys.mjs to see how the menu works.
+ contextMenu.ownerDocument.getElementById("spell-check-dictionary-en-US")
+ .doCommand();
+ contextMenu.hidePopup();
+ return state;
+ });
+ addMessageListener("destroy", () => hunspell.removeDirectory(de_DE));
+ addMessageListener("contextMenu-not-null", () => contextMenu != null);
+ addMessageListener("de_DE-exists", () => de_DE.exists());
+ });
+ is(await script.sendQuery("contextMenu-not-null"), true,
+ "Got context menu XUL");
+ is(await script.sendQuery("de_DE-exists"), true,
+ "true expected (de_DE directory should exist)");
+ script.addMessageListener("popupshown", handlePopup);
+
+ elem_de = document.getElementById("de-DE");
+ editor_de = SpecialPowers.wrap(elem_de).editor;
+ editor_de.setSpellcheckUserOverride(true);
+
+ maybeOnSpellCheck(elem_de, function() {
+ var inlineSpellChecker = editor_de.getInlineSpellChecker(true);
+ var spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ // Check that the German dictionary is loaded and that the spell check has worked.
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "de-DE", "expected de-DE");
+ // eslint-disable-next-line no-useless-concat
+ is(getMisspelledWords(editor_de), "today" + "is" + "a" + "good" + "day", "some misspelled words expected: today is a good day");
+
+ // Focus again, just to be sure that the context-click won't trigger another spell check.
+ elem_de.focus();
+
+ // Make sure all spell checking action is done before right-click to select the en-US dictionary.
+ maybeOnSpellCheck(elem_de, function() {
+ synthesizeMouse(elem_de, 2, 2, { type: "contextmenu", button: 2 }, window);
+ });
+ });
+});
+
+async function handlePopup() {
+ var state = await script.sendQuery("hidepopup");
+ is(state, "open", "checking if popup is open");
+
+ onSpellCheck(elem_de, async function() {
+ var inlineSpellChecker = editor_de.getInlineSpellChecker(true);
+ var spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ // Check that the English dictionary is loaded and that the spell check has worked.
+ is(currentDictionaries.length, 2, "expected two dictionaries");
+ let dictionaryArray = Array.from(currentDictionaries);
+ ok(dictionaryArray.includes("de-DE"), "expected de-DE");
+ ok(dictionaryArray.includes("en-US"), "expected en-US");
+ is(getMisspelledWords(editor_de), "", "No misspelled words expected");
+
+ // Remove the fake de_DE dictionary again.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1219928.html b/editor/spellchecker/tests/test_bug1219928.html
new file mode 100644
index 0000000000..0636b981df
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1219928.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1219928
+-->
+<head>
+ <title>Test for Bug 1219928</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1219928">Mozilla Bug 1219928</a>
+<p id="display"></p>
+
+<div contenteditable id="en-US" lang="en-US">
+<p>And here a missspelled word</p>
+<style>
+<!-- and here another onnee in a style comment -->
+</style>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1219928 **/
+/* Very simple test to check that <style> blocks are skipped in the spell check */
+
+var spellchecker;
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+
+ var elem = document.getElementById("en-US");
+ elem.focus();
+
+ maybeOnSpellCheck(elem, function() {
+ var editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ var editor = editingSession.getEditorForWindow(window);
+ var selcon = editor.selectionController;
+ var sel = selcon.getSelection(selcon.SELECTION_SPELLCHECK);
+
+ is(sel.toString(), "missspelled", "one misspelled word expected: missspelled");
+
+ spellchecker = SpecialPowers.Cu.createSpellChecker();
+ spellchecker.setFilterType(spellchecker.FILTERTYPE_NORMAL);
+ spellchecker.InitSpellChecker(editor, false, spellCheckStarted);
+ });
+});
+
+function spellCheckStarted() {
+ var misspelledWord = spellchecker.GetNextMisspelledWord();
+ is(misspelledWord, "missspelled", "first misspelled word expected: missspelled");
+
+ // Without the fix, the next misspelled word was 'onnee', so we check that we don't get it.
+ misspelledWord = spellchecker.GetNextMisspelledWord();
+ isnot(misspelledWord, "onnee", "second misspelled word should not be: onnee");
+
+ spellchecker = "";
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1365383.html b/editor/spellchecker/tests/test_bug1365383.html
new file mode 100644
index 0000000000..a4b71d15bb
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1365383.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1365383
+-->
+<head>
+ <title>Test for Bug 1365383</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1365383">Mozilla Bug 1365383</a>
+<p id="display"></p>
+<div id="content">
+<textarea id="editor" spellcheck="true"></textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ let textarea = document.getElementById("editor");
+ let editor = SpecialPowers.wrap(textarea).editor;
+
+ let spellChecker = SpecialPowers.Cu.createSpellChecker();
+
+ // Callback parameter isn't set
+ spellChecker.InitSpellChecker(editor, false);
+
+ textarea.focus();
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(textarea, () => {
+ // Callback parameter isn't set
+ spellChecker.UpdateCurrentDictionary();
+
+ var canSpellCheck = spellChecker.canSpellCheck();
+ ok(canSpellCheck, "spellCheck is enabled");
+ SimpleTest.finish();
+ });
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1368544.html b/editor/spellchecker/tests/test_bug1368544.html
new file mode 100644
index 0000000000..577e90635f
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1368544.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi=id=1368544
+-->
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1368544">Mozilla Bug 1368544</a>
+<div id="display"></div>
+<textarea id=textarea></textarea>
+<pre id="test">
+</pre>
+
+<script class="testbody">
+function hasEmptyTextNode(div) {
+ return div.firstChild.nodeType === Node.TEXT_NODE && div.firstChild.length === 0;
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ let textarea = document.getElementById("textarea");
+ let editor = SpecialPowers.wrap(textarea).editor;
+
+ let spellChecker = SpecialPowers.Cu.createSpellChecker();
+ spellChecker.InitSpellChecker(editor, false);
+
+ textarea.focus();
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(textarea, () => {
+ spellChecker.UpdateCurrentDictionary(() => {
+ textarea.value = "ABC";
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child nodes");
+ sendString("D");
+ is(textarea.value, "ABCD", "D is last character");
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child nodes");
+ textarea.value = "";
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child node even if value is empty");
+
+ sendString("AAA");
+ synthesizeKey("KEY_Backspace", {repeat: 3});
+ is(textarea.value, "", "value is empty");
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child node even if value is empty");
+
+ textarea.value = "ABC";
+ SpecialPowers.wrap(textarea).setUserInput("");
+ is(textarea.value, "",
+ "textarea should become empty when setUserInput() is called with empty string");
+ ok(hasEmptyTextNode(editor.rootElement),
+ "editor of textarea should only have an empty text node when user input emulation set the value to empty");
+ todo(editor.rootElement.childNodes.length === 1, "editor of textarea should only have a single child");
+ if (editor.rootElement.childNodes.length > 1) {
+ is(editor.rootElement.childNodes.length, 2, "There should be only one additional <br> node");
+ is(editor.rootElement.lastChild.tagName.toLowerCase(), "br", "The node should be a <br> element node");
+ ok(!SpecialPowers.wrap(editor.rootElement.lastChild).isPaddingForEmptyEditor,
+ "The <br> should not be a padding <br> element");
+ }
+ textarea.value = "ABC";
+ synthesizeKey("KEY_Enter", {repeat: 2});
+ textarea.value = "";
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child node even if value is empty");
+
+ sendString("AAA");
+ is(textarea.value, "AAA", "value is AAA");
+ textarea.addEventListener("keyup", (e) => {
+ if (e.key == "Enter") {
+ textarea.value = "";
+ ok(editor.rootElement.hasChildNodes(),
+ "editor of textarea has child node even if value is empty");
+ SimpleTest.finish();
+ }
+ });
+
+ synthesizeKey("KEY_Enter");
+ });
+ });
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1402822.html b/editor/spellchecker/tests/test_bug1402822.html
new file mode 100644
index 0000000000..505bca6dbb
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1402822.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1402822
+-->
+<head>
+ <title>Test for Bug 1402822</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1402822">Mozilla Bug 1402822</a>
+<p id="display"></p>
+</div>
+
+<textarea id="editor">heute ist ein guter Tag - today is a good day</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const Ci = SpecialPowers.Ci;
+
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+function getMisspelledWords(editor) {
+ return editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_SPELLCHECK).toString();
+}
+
+/** Test for Bug 1402822 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(start);
+async function start() {
+ let script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ let dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ let hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install de-DE dictionary.
+ let de_DE = dir.clone();
+ de_DE.append("de-DE");
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("destroy", () => hunspell.removeDirectory(de_DE));
+ addMessageListener("de_DE-exists", () => de_DE.exists());
+ });
+ is(await script.sendQuery("de_DE-exists"), true,
+ "true expected (de_DE directory should exist)");
+
+ let textarea = document.getElementById("editor");
+ let editor = SpecialPowers.wrap(textarea).editor;
+ textarea.focus();
+
+ maybeOnSpellCheck(textarea, () => {
+ let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
+ ok(isc, "Inline spell checker should exist after focus and spell check");
+ let spellchecker = isc.spellChecker;
+
+ spellchecker.setCurrentDictionaries(["en-US"]).then(() => {
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "en-US", "expected en-US");
+ is(getMisspelledWords(editor), "heuteisteinguter", "some misspelled words expected: heuteisteinguter");
+ spellchecker.setCurrentDictionaries(["en-US", "de-DE"]).then(() => {
+ textarea.blur();
+ textarea.focus();
+ maybeOnSpellCheck(textarea, () => {
+ currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 2, "expected two dictionaries");
+ is(currentDictionaries[0], "en-US", "expected en-US");
+ is(currentDictionaries[1], "de-DE", "expected de-DE");
+ is(getMisspelledWords(editor), "", "No misspelled words expected");
+
+ spellchecker.setCurrentDictionaries(["de-DE"]).then(() => {
+ textarea.blur();
+ textarea.focus();
+ maybeOnSpellCheck(textarea, async function() {
+ currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "de-DE", "expected de-DE");
+ is(getMisspelledWords(editor), "todayisagoodday", "some misspelled words expected: todayisagoodday");
+
+ // Remove the fake de_DE dictionary again.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1418629.html b/editor/spellchecker/tests/test_bug1418629.html
new file mode 100644
index 0000000000..3f2c906913
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1418629.html
@@ -0,0 +1,210 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Mozilla bug 1418629</title>
+ <link rel=stylesheet href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/editor/spellchecker/tests/spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1418629">Mozilla Bug 1418629</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<input id="input1" spellcheck="true">
+<textarea id="textarea1"></textarea>
+<div id="edit1" contenteditable=true></div>
+
+<script>
+const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+
+function getEditor(input) {
+ if (input instanceof HTMLInputElement ||
+ input instanceof HTMLTextAreaElement) {
+ return SpecialPowers.wrap(input).editor;
+ }
+
+ return SpecialPowers.wrap(window).docShell.editor;
+}
+
+function resetEditableContent(input) {
+ if (input instanceof HTMLInputElement ||
+ input instanceof HTMLTextAreaElement) {
+ input.value = "";
+ return;
+ }
+ input.innerHTML = "";
+}
+
+async function test_with_single_quote(input) {
+ let misspeltWords = [];
+
+ input.focus();
+ resetEditableContent(input);
+
+ synthesizeKey("d");
+ synthesizeKey("o");
+ synthesizeKey("e");
+ synthesizeKey("s");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ let editor = getEditor(input);
+ // isSpellingCheckOk is defined in spellcheck.js
+ ok(isSpellingCheckOk(editor, misspeltWords), "no misspelt words");
+
+ synthesizeKey("n");
+ synthesizeKey("\'");
+ is(input.value || input.textContent, "doesn\'", "");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ // XXX This won't work since mozInlineSpellWordUtil::SplitDOM removes
+ // last single quote unfortunately that is during inputting.
+ // isSpellingCheckOk is defined in spellcheck.js
+ todo_is(isSpellingCheckOk(editor, misspeltWords, true), true,
+ "don't run spellchecker during inputting word");
+
+ synthesizeKey(" ");
+ is(input.value || input.textContent, "doesn\' ", "");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ misspeltWords.push("doesn");
+ // isSpellingCheckOk is defined in spellcheck.js
+ ok(isSpellingCheckOk(editor, misspeltWords), "should run spellchecker");
+}
+
+async function test_with_twice_characters(input, ch) {
+ let misspeltWords = [];
+
+ input.focus();
+ resetEditableContent(input);
+
+ synthesizeKey("d");
+ synthesizeKey("o");
+ synthesizeKey("e");
+ synthesizeKey("s");
+ synthesizeKey("n");
+ synthesizeKey(ch);
+ synthesizeKey(ch);
+ is(input.value || input.textContent, "doesn" + ch + ch, "");
+
+ // trigger spellchecker
+ synthesizeKey(" ");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ misspeltWords.push("doesn");
+ let editor = getEditor(input);
+ // isSpellingCheckOk is defined in spellcheck.js
+ ok(isSpellingCheckOk(editor, misspeltWords), "should run spellchecker");
+}
+
+async function test_between_single_quote(input) {
+ let misspeltWords = [];
+
+ input.focus();
+ resetEditableContent(input);
+
+ synthesizeKey("\'");
+ synthesizeKey("t");
+ synthesizeKey("e");
+ synthesizeKey("s");
+ synthesizeKey("t");
+ synthesizeKey("\'");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ let editor = getEditor(input);
+ ok(isSpellingCheckOk(editor, misspeltWords),
+ "don't run spellchecker between single qoute");
+}
+
+async function test_with_email(input) {
+ let misspeltWords = [];
+
+ input.focus();
+ resetEditableContent(input);
+
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("@");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey(".");
+ synthesizeKey("c");
+ synthesizeKey("o");
+ synthesizeKey("m");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ let editor = getEditor(input);
+ ok(isSpellingCheckOk(editor, misspeltWords),
+ "don't run spellchecker for email address");
+
+ synthesizeKey(" ");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ ok(isSpellingCheckOk(editor, misspeltWords),
+ "no misspelt words due to email address");
+}
+
+async function test_with_url(input) {
+ let misspeltWords = [];
+
+ input.focus();
+ resetEditableContent(input);
+
+ synthesizeKey("h");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("p");
+ synthesizeKey(":");
+ synthesizeKey("/");
+ synthesizeKey("/");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey("t");
+ synthesizeKey(".");
+ synthesizeKey("c");
+ synthesizeKey("o");
+ synthesizeKey("m");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ let editor = getEditor(input);
+ ok(isSpellingCheckOk(editor, misspeltWords),
+ "don't run spellchecker for URL");
+
+ synthesizeKey(" ");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(input, resolve); });
+ ok(isSpellingCheckOk(editor, misspeltWords),
+ "no misspelt words due to URL");
+}
+
+SimpleTest.waitForFocus(() => {
+ for (let n of ["input1", "textarea1", "edit1"]) {
+ add_task(test_with_single_quote.bind(null,
+ document.getElementById(n)));
+ add_task(test_with_twice_characters.bind(null,
+ document.getElementById(n),
+ "\'"));
+ add_task(test_with_twice_characters.bind(null,
+ document.getElementById(n),
+ String.fromCharCode(0x2019)));
+ add_task(test_between_single_quote.bind(null,
+ document.getElementById(n)));
+ add_task(test_with_email.bind(null, document.getElementById(n)));
+ add_task(test_with_url.bind(null, document.getElementById(n)));
+ }
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1497480.html b/editor/spellchecker/tests/test_bug1497480.html
new file mode 100644
index 0000000000..a32e67370d
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1497480.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1497480
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1497480</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="spellcheck.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1497480">Mozilla Bug 1497480</a>
+<p id="display"></p>
+
+<div id="outOfTarget" contenteditable>Bug 1497480</div>
+<div id="light"></div>
+
+<script>
+
+/** Test for Bug 1497480 **/
+let gMisspeltWords = [];
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+const template = document.createElement("template");
+template.innerHTML = `<div id="target" contenteditable>Test</div>`;
+
+let shadow = document.getElementById("light").attachShadow({mode: "closed"});
+shadow.appendChild(template.content.cloneNode(true));
+
+let target = shadow.getElementById("target");
+let outOfTarget = document.getElementById("outOfTarget");
+
+function getEditor() {
+ var win = window;
+ var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
+ return editingSession.getEditorForWindow(win);
+}
+
+// Wait for the page to be ready for testing
+add_task(async function() {
+ await new Promise((resolve) => {
+ SimpleTest.waitForFocus(() => {
+ SimpleTest.executeSoon(resolve);
+ }, window);
+ });
+
+ // Wait for first full spell-checking.
+ synthesizeMouseAtCenter(outOfTarget, {}, window);
+ await new Promise((resolve) => {
+ maybeOnSpellCheck(outOfTarget, function() {
+ resolve();
+ });
+ });
+});
+
+// Should perform spell-checking when anchor navigates away from ShadowDOM.
+add_task(async function() {
+ synthesizeMouseAtCenter(target, {}, window);
+ sendString(" spellechek");
+ gMisspeltWords.push("spellechek");
+ synthesizeMouseAtCenter(outOfTarget, {}, window);
+ await new Promise((resolve) => {
+ maybeOnSpellCheck(target, function() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "Spell-checking should be performed when anchor navigates away from ShadowDOM");
+ SimpleTest.executeSoon(resolve);
+ });
+ });
+});
+
+// Should perform spell-checking when pressing enter in contenteditable in ShadowDOM.
+add_task(async function() {
+ synthesizeMouseAtCenter(target, {}, window);
+ sendString(" spellechck");
+ gMisspeltWords.push("spellechck");
+ synthesizeKey("KEY_Enter", {}, window);
+ await new Promise((resolve) => {
+ maybeOnSpellCheck(target, function() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "Spell-checking should be performed when pressing enter in contenteditable in ShadowDOM");
+ SimpleTest.executeSoon(resolve);
+ });
+ });
+});
+
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1602526.html b/editor/spellchecker/tests/test_bug1602526.html
new file mode 100644
index 0000000000..62be07a53c
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1602526.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Mozilla bug 1602526</title>
+ <link rel=stylesheet href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/editor/spellchecker/tests/spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1602526">Mozilla Bug 1602526</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<div id="contenteditable" contenteditable=true>kkkk&#xf6;kkkk</div>
+
+<script>
+const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+
+function getEditor() {
+ return SpecialPowers.wrap(window).docShell.editor;
+}
+
+SimpleTest.waitForFocus(async () => {
+ let contenteditable = document.getElementById("contenteditable");
+ let misspeltWords = [];
+ misspeltWords.push("kkkk\u00f6kkkk");
+
+ contenteditable.focus();
+ window.getSelection().collapse(contenteditable.firstChild, contenteditable.firstChild.length);
+
+ synthesizeKey(" ");
+
+ // Run spell checker
+ await new Promise((resolve) => { maybeOnSpellCheck(contenteditable, resolve); });
+
+ synthesizeKey("a");
+ synthesizeKey("a");
+ synthesizeKey("a");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(contenteditable, resolve); });
+ let editor = getEditor();
+ // isSpellingCheckOk is defined in spellcheck.js
+ // eslint-disable-next-line no-undef
+ ok(isSpellingCheckOk(editor, misspeltWords), "correct word is seleced as misspell");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1761273.html b/editor/spellchecker/tests/test_bug1761273.html
new file mode 100644
index 0000000000..65e3618194
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1761273.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1761273
+-->
+<head>
+ <title>Test for Bug 1761273</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1761273">Mozilla Bug 1761273</a>
+<p id="display"></p>
+</div>
+
+<textarea id="editor" lang="en-US">heute ist ein guter Tag - today is a good day</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const Ci = SpecialPowers.Ci;
+
+let {
+ getDictionaryContentPref,
+ onSpellCheck,
+} = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+/** Test for Bug 1402822 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(start);
+async function start() {
+ let script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ let dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ let hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install de-DE dictionary.
+ let de_DE = dir.clone();
+ de_DE.append("de-DE");
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("destroy", () => hunspell.removeDirectory(de_DE));
+ addMessageListener("de_DE-exists", () => de_DE.exists());
+ });
+ is(await script.sendQuery("de_DE-exists"), true,
+ "true expected (de_DE directory should exist)");
+
+ let textarea = document.getElementById("editor");
+ textarea.focus();
+
+ onSpellCheck(textarea, async () => {
+ let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(true);
+ ok(isc, "Inline spell checker should exist after focus and spell check");
+ let spellchecker = isc.spellChecker;
+
+ // Setting the language to the language of the texteditor should not set the
+ // content preference.
+ await spellchecker.setCurrentDictionaries(["en-US"]);
+ let dictionaryContentPref = await getDictionaryContentPref();
+ is(dictionaryContentPref, "", "Content pref should be empty");
+
+ await spellchecker.setCurrentDictionaries(["en-US", "de-DE"]);
+ dictionaryContentPref = await getDictionaryContentPref();
+ is(dictionaryContentPref, "en-US,de-DE,", "Content pref should be en-US,de-DE,");
+
+ await spellchecker.setCurrentDictionaries(["de-DE"]);
+ dictionaryContentPref = await getDictionaryContentPref();
+ is(dictionaryContentPref, "de-DE,", "Content pref should be de-DE,");
+
+ // Remove the fake de_DE dictionary again.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ await spellchecker.setCurrentDictionaries([]);
+ dictionaryContentPref = await getDictionaryContentPref();
+ is(dictionaryContentPref, "", "Content pref should be empty");
+
+ SimpleTest.finish();
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1773802.html b/editor/spellchecker/tests/test_bug1773802.html
new file mode 100644
index 0000000000..7e9866817d
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1773802.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1773802
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1773802</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1773802">Mozilla Bug 1773802</a>
+<p id="display"></p>
+</div>
+
+<textarea id="editor">correct п©я─п╟п╡п╦п╩я▄п╫я▀п╧, incarrect п╫п╣п©я─п╬п╡п╦п╩я▄п╫я▀п╧</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+const Ci = SpecialPowers.Ci;
+
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+function getMisspelledWords(editor) {
+ return editor.selectionController.getSelection(Ci.nsISelectionController.SELECTION_SPELLCHECK).toString();
+}
+
+/** Test for Bug 1773802 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(start);
+async function start() {
+ let script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ let dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ let hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install ru-RU dictionary.
+ let ru_RU = dir.clone();
+ ru_RU.append("ru-RU");
+ hunspell.addDirectory(ru_RU);
+
+ addMessageListener("destroy", () => hunspell.removeDirectory(ru_RU));
+ addMessageListener("ru_RU-exists", () => ru_RU.exists());
+ });
+ is(await script.sendQuery("ru_RU-exists"), true,
+ "true expected (ru_RU directory should exist)");
+
+ let textarea = document.getElementById("editor");
+ let editor = SpecialPowers.wrap(textarea).editor;
+ textarea.focus();
+
+ maybeOnSpellCheck(textarea, () => {
+ let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
+ ok(isc, "Inline spell checker should exist after focus and spell check");
+ let spellchecker = isc.spellChecker;
+
+ spellchecker.setCurrentDictionaries(["en-US", "ru-RU"]).then(async () => {
+ textarea.blur();
+ textarea.focus();
+ maybeOnSpellCheck(textarea, async function() {
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 2, "expected two dictionaries");
+ is(currentDictionaries[0], "en-US", "expected en-US");
+ is(currentDictionaries[1], "ru-RU", "expected ru-RU");
+ is(getMisspelledWords(editor), "incarrectп╫п╣п©я─п╬п╡п╦п╩я▄п╫я▀п╧", "some misspelled words expected: incarrect п╫п╣п©я─п╬п╡п╦п╩я▄п╫я▀п╧");
+
+ // Remove the fake ru_RU dictionary again.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug1837268.html b/editor/spellchecker/tests/test_bug1837268.html
new file mode 100644
index 0000000000..1467a328b5
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug1837268.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Mozilla bug 1837268</title>
+ <link rel=stylesheet href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/editor/spellchecker/tests/spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1837268">Mozilla Bug 1837268</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<div id="contenteditable" contenteditable=true>aabbcc</div>
+
+<script>
+const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+
+function getEditor() {
+ return SpecialPowers.wrap(window).docShell.editor;
+}
+
+SimpleTest.waitForFocus(async () => {
+ let contenteditable = document.getElementById("contenteditable");
+ contenteditable.addEventListener("beforeinput", (ev) => {
+ ev.preventDefault();
+ let text = contenteditable.textContent;
+ const sel = window.getSelection();
+ let offset = sel.anchorOffset;
+ switch (ev.inputType) {
+ case "insertText":
+ text = text.substring(0, offset) + ev.data + text.substring(offset);
+ offset += 1;
+ break;
+ case "deleteContentBackward":
+ text = text.substring(0, offset - 1) + text.substring(offset);
+ offset -= 1;
+ break;
+ default:
+ return;
+ }
+ if (contenteditable.firstChild) {
+ contenteditable.firstChild.nodeValue = text;
+ } else {
+ contenteditable.textContent = text;
+ }
+ sel.collapse(contenteditable.firstChild ?? contenteditable, offset);
+ });
+
+ let misspelledWords = [];
+ misspelledWords.push("aabbc"); // One c removed.
+
+ contenteditable.focus();
+ window.getSelection().collapse(contenteditable.firstChild, contenteditable.firstChild.length);
+
+ // Run spell checker
+ await new Promise((resolve) => { maybeOnSpellCheck(contenteditable, resolve); });
+
+ synthesizeKey("KEY_Backspace");
+ synthesizeKey(" ");
+
+ await new Promise((resolve) => { maybeOnSpellCheck(contenteditable, resolve); });
+ let editor = getEditor();
+ // isSpellingCheckOk is defined in spellcheck.js
+ // eslint-disable-next-line no-undef
+ ok(isSpellingCheckOk(editor, misspelledWords), "correct word is selected as misspelled");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug338427.html b/editor/spellchecker/tests/test_bug338427.html
new file mode 100644
index 0000000000..20edb29012
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug338427.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=338427
+-->
+<head>
+ <title>Test for Bug 338427</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=338427">Mozilla Bug 338427</a>
+<p id="display"></p>
+<div id="content">
+<textarea id="editor" lang="testing-XX" spellcheck="true"></textarea>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 338427 **/
+function init() {
+ var { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ var textarea = document.getElementById("editor");
+ var editor = SpecialPowers.wrap(textarea).editor;
+ var spellchecker = editor.getInlineSpellChecker(true);
+ spellchecker.enableRealTimeSpell = true;
+ textarea.focus();
+
+ onSpellCheck(textarea, function() {
+ var list = spellchecker.spellChecker.GetDictionaryList();
+ ok(!!list.length, "At least one dictionary should be present");
+
+ var lang = list[0];
+ spellchecker.spellChecker.setCurrentDictionaries([lang]).then(() => {
+ onSpellCheck(textarea, function() {
+ try {
+ var dictionaries =
+ spellchecker.spellChecker.getCurrentDictionaries();
+ } catch (e) {}
+ is(dictionaries.length, 1, "Expected one dictionary");
+ is(dictionaries[0], lang, "Unexpected spell check dictionary");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.spellChecker.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(init);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug366682.html b/editor/spellchecker/tests/test_bug366682.html
new file mode 100644
index 0000000000..0191c599ab
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug366682.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=366682
+-->
+<head>
+ <title>Test for Bug 366682</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=366682">Mozilla Bug 366682</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 366682 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+var gMisspeltWords;
+
+function getEdit() {
+ return document.getElementById("edit");
+}
+
+function editDoc() {
+ return getEdit().contentDocument;
+}
+
+function getEditor() {
+ var win = editDoc().defaultView;
+ var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
+ return editingSession.getEditorForWindow(win);
+}
+
+function runTest() {
+ editDoc().body.innerHTML = "<div>errror and an other errror</div>";
+ gMisspeltWords = ["errror", "errror"];
+ editDoc().designMode = "on";
+ editDoc().defaultView.focus();
+
+ const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ maybeOnSpellCheck(editDoc().documentElement, evalTest);
+}
+
+function evalTest() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings accounted for.");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+
+<iframe id="edit" width="200" height="100" src="about:blank"></iframe>
+
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug432225.html b/editor/spellchecker/tests/test_bug432225.html
new file mode 100644
index 0000000000..19654eec06
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug432225.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=432225
+-->
+<head>
+ <title>Test for Bug 432225</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=432225">Mozilla Bug 432225</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 432225 **/
+
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+var gMisspeltWords = [];
+
+function getEdit() {
+ return document.getElementById("edit");
+}
+
+function editDoc() {
+ return getEdit().contentDocument;
+}
+
+function getEditor() {
+ var win = editDoc().defaultView;
+ var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
+ return editingSession.getEditorForWindow(win);
+}
+
+function runTest() {
+ editDoc().designMode = "on";
+ setTimeout(function() { addWords(100); }, 0);
+}
+
+function addWords(aLimit) {
+ if (aLimit == 0) {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings accounted for.");
+ SimpleTest.finish();
+ return;
+ }
+ getEdit().focus();
+ sendString("aa OK ");
+ gMisspeltWords.push("aa");
+ maybeOnSpellCheck(editDoc(), function() {
+ addWords(aLimit - 1);
+ });
+}
+</script>
+</pre>
+
+<iframe id="edit" width="200" height="100" src="about:blank"></iframe>
+
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug484181.html b/editor/spellchecker/tests/test_bug484181.html
new file mode 100644
index 0000000000..dab966bcde
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug484181.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=484181
+-->
+<head>
+ <title>Test for Bug 484181</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=484181">Mozilla Bug 484181</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 484181 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+var gMisspeltWords;
+
+function getEditor() {
+ var win = window;
+ var editingSession = SpecialPowers.wrap(win).docShell.editingSession;
+ return editingSession.getEditorForWindow(win);
+}
+
+function append(str) {
+ var edit = document.getElementById("edit");
+ var editor = getEditor();
+ var sel = editor.selection;
+ sel.selectAllChildren(edit);
+ sel.collapseToEnd();
+ sendString(str);
+}
+
+function runTest() {
+ gMisspeltWords = ["haz", "cheezburger"];
+ var edit = document.getElementById("edit");
+ edit.focus();
+
+ const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ maybeOnSpellCheck(edit, function() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings before editing are accounted for.");
+
+ append(" becaz I'm a lulcat!");
+ maybeOnSpellCheck(edit, function() {
+ gMisspeltWords.push("becaz");
+ gMisspeltWords.push("lulcat");
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings after typing are accounted for.");
+
+ SimpleTest.finish();
+ });
+ });
+}
+</script>
+</pre>
+
+<div><div></div><div id="edit" contenteditable="true">I can haz cheezburger</div></div>
+
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug596333.html b/editor/spellchecker/tests/test_bug596333.html
new file mode 100644
index 0000000000..bbd6aab7a0
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug596333.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=596333
+-->
+<head>
+ <title>Test for Bug 596333</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="spellcheck.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596333">Mozilla Bug 596333</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 596333 **/
+const Ci = SpecialPowers.Ci;
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+var gMisspeltWords;
+var onSpellCheck;
+
+function getEditor() {
+ return SpecialPowers.wrap(document.getElementById("edit")).editor;
+}
+
+function append(str) {
+ var edit = document.getElementById("edit");
+ edit.focus();
+ edit.selectionStart = edit.selectionEnd = edit.value.length;
+ sendString(str);
+}
+
+function getLoadContext() {
+ return SpecialPowers.wrap(window).docShell.QueryInterface(Ci.nsILoadContext);
+}
+
+function paste(str) {
+ var Cc = SpecialPowers.Cc;
+ var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+ trans.init(getLoadContext());
+ var s = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ s.data = str;
+ trans.setTransferData("text/plain", s);
+
+ let beforeInputEvent = null;
+ let inputEvent = null;
+ window.addEventListener("beforeinput", aEvent => { beforeInputEvent = aEvent; }, {once: true});
+ window.addEventListener("input", aEvent => { inputEvent = aEvent; }, {once: true});
+ getEditor().pasteTransferable(trans);
+ isnot(beforeInputEvent, null, '"beforeinput" event should be fired');
+ if (beforeInputEvent) {
+ is(beforeInputEvent.cancelable, true, '"beforeinput" event for "insertFromPaste" should be cancelable');
+ is(beforeInputEvent.inputType, "insertFromPaste", 'inputType of "beforeinput" event should be "insertFromPaste"');
+ is(beforeInputEvent.data, str, `data of "beforeinput" event should be "${str}"`);
+ is(beforeInputEvent.dataTransfer, null, 'dataTransfer of "beforeinput" event should be null on <textarea>');
+ is(beforeInputEvent.getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event should return empty array on <textarea>');
+ }
+ is(inputEvent.type, "input", '"input" event should be fired');
+ is(inputEvent.inputType, "insertFromPaste", '"inputType of "input" event should be "insertFromPaste"');
+ is(inputEvent.data, str, `data of "input" event should be "${str}"`);
+ is(inputEvent.dataTransfer, null, 'dataTransfer of "input" event should be null on <textarea>');
+ is(inputEvent.getTargetRanges().length, 0, 'getTargetRanges() of "input" event should return empty array on <textarea>');
+}
+
+function runOnFocus() {
+ var edit = document.getElementById("edit");
+
+ gMisspeltWords = ["haz", "cheezburger"];
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings before editing are accounted for.");
+ append(" becaz I'm a lulcat!");
+ onSpellCheck(edit, function() {
+ gMisspeltWords.push("becaz");
+ gMisspeltWords.push("lulcat");
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings after typing are accounted for.");
+
+ // Now, type an invalid word, and instead of hitting "space" at the end, just blur
+ // the textarea and see if the spell check after the blur event catches it.
+ append(" workd");
+ edit.blur();
+ onSpellCheck(edit, function() {
+ gMisspeltWords.push("workd");
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "All misspellings after blur are accounted for.");
+
+ // Also, test the case when we're entering the first word in a textarea
+ gMisspeltWords = ["workd"];
+ edit.value = "";
+ append("workd ");
+ onSpellCheck(edit, function() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "Misspelling in the first entered word is accounted for.");
+
+ // Make sure that pasting would also trigger spell checking for the previous word
+ gMisspeltWords = ["workd"];
+ edit.value = "";
+ append("workd");
+ paste(" x");
+ onSpellCheck(edit, function() {
+ ok(isSpellingCheckOk(getEditor(), gMisspeltWords),
+ "Misspelling is accounted for after pasting.");
+
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+
+function runTest() {
+ var edit = document.getElementById("edit");
+ edit.focus();
+
+ onSpellCheck = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ ).onSpellCheck;
+ onSpellCheck(edit, runOnFocus);
+}
+</script>
+</pre>
+
+<textarea id="edit">I can haz cheezburger</textarea>
+
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug636465.html b/editor/spellchecker/tests/test_bug636465.html
new file mode 100644
index 0000000000..aaa64c3a31
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug636465.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<title>Mozilla bug 636465</title>
+<link rel=stylesheet href="/tests/SimpleTest/test.css">
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=636465"
+ target="_blank">Mozilla Bug 636465</a>
+<input id="x" value="foobarbaz" spellcheck="true" style="background-color: transparent; border: transparent;">
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ var x = document.getElementById("x");
+ x.focus();
+ onSpellCheck(x, function() {
+ x.blur();
+ var spellCheckTrue = snapshotWindow(window);
+ x.setAttribute("spellcheck", "false");
+ var spellCheckFalse = snapshotWindow(window);
+ x.setAttribute("spellcheck", "true");
+ x.focus();
+ onSpellCheck(x, function() {
+ x.blur();
+ var spellCheckTrueAgain = snapshotWindow(window);
+ x.removeAttribute("spellcheck");
+ var spellCheckNone = snapshotWindow(window);
+ var ret = compareSnapshots(spellCheckTrue, spellCheckFalse, false)[0];
+ ok(ret,
+ "Setting the spellcheck attribute to false should work");
+ if (!ret) {
+ ok(false, "\nspellCheckTrue: " + spellCheckTrue.toDataURL() + "\nspellCheckFalse: " + spellCheckFalse.toDataURL());
+ }
+ ret = compareSnapshots(spellCheckTrue, spellCheckTrueAgain, true)[0];
+ ok(ret,
+ "Setting the spellcheck attribute back to true should work");
+ if (!ret) {
+ ok(false, "\nspellCheckTrue: " + spellCheckTrue.toDataURL() + "\nspellCheckTrueAgain: " + spellCheckTrueAgain.toDataURL());
+ }
+ ret = compareSnapshots(spellCheckNone, spellCheckFalse, true)[0];
+ ok(ret,
+ "Unsetting the spellcheck attribute should work");
+ if (!ret) {
+ ok(false, "\spellCheckNone: " + spellCheckNone.toDataURL() + "\nspellCheckFalse: " + spellCheckFalse.toDataURL());
+ }
+ SimpleTest.finish();
+ });
+ });
+}
+addLoadEvent(runTest);
+</script>
diff --git a/editor/spellchecker/tests/test_bug678842.html b/editor/spellchecker/tests/test_bug678842.html
new file mode 100644
index 0000000000..b328e5a8ff
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug678842.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=678842
+-->
+<head>
+ <title>Test for Bug 678842</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=678842">Mozilla Bug 678842</a>
+<p id="display"></p>
+<iframe id="content"></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 678842 **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+// load a subframe containing an editor with a defined unknown lang. At first
+// load, it will set dictionary to en-US. At second load, it will return current
+// dictionary. So, we can check, dictionary is correctly remembered between
+// loads.
+
+var firstLoad = true;
+var script;
+
+var loadListener = async function(evt) {
+ if (firstLoad) {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install en-GB dictionary.
+ let en_GB = dir.clone();
+ en_GB.append("en-GB");
+ hunspell.addDirectory(en_GB);
+
+ addMessageListener("en_GB-exists", () => en_GB.exists());
+ addMessageListener("destroy", () => hunspell.removeDirectory(en_GB));
+ });
+ is(await script.sendQuery("en_GB-exists"), true,
+ "true expected (en-GB directory should exist)");
+ }
+
+ var doc = evt.target.contentDocument;
+ var elem = doc.getElementById("textarea");
+ var editor = SpecialPowers.wrap(elem).editor;
+ editor.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor.getInlineSpellChecker(true);
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(elem, async function() {
+ let spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ let currentDictionary = currentDictionaries[0];
+
+ if (firstLoad) {
+ firstLoad = false;
+
+ // First time around, the dictionary defaults to the locale.
+ is(currentDictionary, "en-US", "unexpected lang " + currentDictionary + " instead of en-US");
+
+ // Select en-GB.
+ spellchecker.setCurrentDictionaries(["en-GB"]).then(() => {
+ content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug678842_subframe.html?firstload=false";
+ });
+ } else {
+ is(currentDictionary, "en-GB", "unexpected lang " + currentDictionary + " instead of en-GB");
+ content.removeEventListener("load", loadListener);
+
+ // Remove the fake en-GB dictionary again, since it's otherwise picked up by later tests.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.setCurrentDictionaries([]).then( () => {
+ SimpleTest.finish();
+ });
+ }
+ });
+};
+
+content.addEventListener("load", loadListener);
+
+content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug678842_subframe.html?firstload=true";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug697981.html b/editor/spellchecker/tests/test_bug697981.html
new file mode 100644
index 0000000000..8ef5dbcd92
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug697981.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=697981
+-->
+<head>
+ <title>Test for Bug 697981</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=697981">Mozilla Bug 697981</a>
+<p id="display"></p>
+</div>
+
+<textarea id="de-DE" lang="de-DE" onfocus="deFocus()">German heute ist ein guter Tag</textarea>
+<textarea id="en-US" lang="en-US" onfocus="enFocus()">Nogoodword today is a nice day</textarea>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function getMisspelledWords(editor) {
+ return editor.selectionController.getSelection(SpecialPowers.Ci.nsISelectionController.SELECTION_SPELLCHECK).toString();
+}
+
+var elem_de;
+var editor_de;
+var script;
+
+var { maybeOnSpellCheck, onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+/** Test for Bug 697981 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async function() {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install de-DE dictionary.
+ var de_DE = dir.clone();
+ de_DE.append("de-DE");
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("de_DE-exists", () => de_DE.exists());
+ addMessageListener("destroy", () => hunspell.removeDirectory(de_DE));
+ });
+ is(await script.sendQuery("de_DE-exists"), true,
+ "true expected (de_DE directory should exist)");
+
+ document.getElementById("de-DE").focus();
+});
+
+function deFocus() {
+ elem_de = document.getElementById("de-DE");
+ editor_de = SpecialPowers.wrap(elem_de).editor;
+ editor_de.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor_de.getInlineSpellChecker(true);
+
+ maybeOnSpellCheck(elem_de, function() {
+ var spellchecker = inlineSpellChecker.spellChecker;
+ try {
+ var currentDictionaries = spellchecker.getCurrentDictionaries();
+ } catch (e) {}
+
+ // Check that the German dictionary is loaded and that the spell check has worked.
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "de-DE", "expected de-DE");
+ is(getMisspelledWords(editor_de), "German", "one misspelled word expected: German");
+
+ // Now focus the other textarea, which requires English spelling.
+ document.getElementById("en-US").focus();
+ });
+}
+
+function enFocus() {
+ var elem_en = document.getElementById("en-US");
+ var editor_en = SpecialPowers.wrap(elem_en).editor;
+ editor_en.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor_en.getInlineSpellChecker(true);
+
+ onSpellCheck(elem_en, async function() {
+ let spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ // Check that the English dictionary is loaded and that the spell check has worked.
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "en-US", "expected en-US");
+ is(getMisspelledWords(editor_en), "Nogoodword", "one misspelled word expected: Nogoodword");
+
+ // So far all was boring. The important thing is whether the spell check result
+ // in the de-DE editor is still the same. After losing focus, no spell check
+ // updates should take place there.
+ is(getMisspelledWords(editor_de), "German", "one misspelled word expected: German");
+
+ // Remove the fake de_DE dictionary again.
+ await script.sendQuery("destroy");
+
+ // Focus again, so the spelling gets updated, but before we need to kill the focus handler.
+ elem_de.onfocus = null;
+ elem_de.blur();
+ elem_de.focus();
+
+ // After removal, the de_DE editor should refresh the spelling with en-US.
+ maybeOnSpellCheck(elem_de, function() {
+ spellchecker = inlineSpellChecker.spellChecker;
+ try {
+ currentDictionaries = spellchecker.getCurrentDictionaries();
+ } catch (e) {}
+
+ // Check that the default English dictionary is loaded and that the spell check has worked.
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ is(currentDictionaries[0], "en-US", "expected en-US");
+ // eslint-disable-next-line no-useless-concat
+ is(getMisspelledWords(editor_de), "heute" + "ist" + "ein" + "guter",
+ "some misspelled words expected: heute ist ein guter");
+
+ SimpleTest.finish();
+ });
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_bug717433.html b/editor/spellchecker/tests/test_bug717433.html
new file mode 100644
index 0000000000..322e0d47d8
--- /dev/null
+++ b/editor/spellchecker/tests/test_bug717433.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717433
+-->
+<head>
+ <title>Test for Bug 717433</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717433">Mozilla Bug 717433</a>
+<p id="display"></p>
+<iframe id="content"></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 717433 **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+// Load a subframe containing an editor with language "en". At first
+// load, it will set the dictionary to en-GB or en-US. We set the other one.
+// At second load, it will return the current dictionary. We can check that the
+// dictionary is correctly remembered between loads.
+
+var firstLoad = true;
+var expected = "";
+var script;
+
+var loadListener = async function(evt) {
+ if (firstLoad) {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install en-GB dictionary.
+ var en_GB = dir.clone();
+ en_GB.append("en-GB");
+ hunspell.addDirectory(en_GB);
+
+ addMessageListener("en_GB-exists", () => en_GB.exists());
+ addMessageListener("destroy", () => hunspell.removeDirectory(en_GB));
+ });
+ is(await script.sendQuery("en_GB-exists"), true,
+ "true expected (en-GB directory should exist)");
+ }
+
+ var doc = evt.target.contentDocument;
+ var elem = doc.getElementById("textarea");
+ var editor = SpecialPowers.wrap(elem).editor;
+ editor.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor.getInlineSpellChecker(true);
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(elem, async function() {
+ let spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries = spellchecker.getCurrentDictionaries();
+
+ is(currentDictionaries.length, 1, "expected one dictionary");
+ let currentDictionary = currentDictionaries[0];
+
+ if (firstLoad) {
+ firstLoad = false;
+
+ // First time around, we get a random dictionary based on the language "en".
+ if (currentDictionary == "en-GB") {
+ expected = "en-US";
+ } else if (currentDictionary == "en-US") {
+ expected = "en-GB";
+ } else {
+ is(true, false, "Neither en-US nor en-GB are current");
+ }
+ spellchecker.setCurrentDictionaries([expected]).then(() => {
+ content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug717433_subframe.html?firstload=false";});
+ } else {
+ is(currentDictionary, expected, expected + " expected");
+ content.removeEventListener("load", loadListener);
+
+ // Remove the fake en-GB dictionary again, since it's otherwise picked up by later tests.
+ await script.sendQuery("destroy");
+
+ // This will clear the content preferences and reset "spellchecker.dictionary".
+ spellchecker.setCurrentDictionaries([]).then(() => {
+ SimpleTest.finish();
+ });
+ }
+ });
+};
+
+content.addEventListener("load", loadListener);
+
+content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/bug717433_subframe.html?firstload=true";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_multiple_content_languages.html b/editor/spellchecker/tests/test_multiple_content_languages.html
new file mode 100644
index 0000000000..49d3bb5ace
--- /dev/null
+++ b/editor/spellchecker/tests/test_multiple_content_languages.html
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for multiple Content-Language values</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<iframe id="content"></iframe>
+
+<pre id="test">
+<script class="testbody">
+
+/** Test for multiple Content-Language values **/
+/** Visit the elements defined above and check the dictionaries we got **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+
+var tests = [
+ // text area, value of spellchecker.dictionary, result.
+ // Result: Document language.
+ [ "none", "", ["en-US", "en-GB"] ],
+
+ // Result: Element language.
+ [ "en-GB", "", ["en-GB"] ],
+ // Result: Random en-* or en-US (if application locale is en-US).
+ [ "en-ZA-not-avail", "", ["*"] ],
+ [ "en", "", ["*"] ],
+ // Result: Locale.
+ [ "ko-not-avail", "", ["en-US"] ],
+
+ // Result: Document language, plus preference value in all cases.
+ [ "none", "en-AU", ["en-US", "en-GB", "en-AU"] ],
+ [ "en-ZA-not-avail", "en-AU", ["en-AU"] ],
+ [ "ko-not-avail", "en-AU", ["en-AU"] ],
+
+ // Result: Document language, plus first matching preference language.
+ [ "none", "en-AU,en-US", ["en-US", "en-GB", "en-AU"] ],
+ // Result: First matching preference language.
+ [ "en-ZA-not-avail", "en-AU,en-US", ["en-AU"] ],
+ // Result: Fall back to preference languages.
+ [ "ko-not-avail", "en-AU,en-US", ["en-AU", "en-US"] ],
+
+ // Result: Random en-*.
+ [ "en-ZA-not-avail", "de-DE", ["*"] ],
+ // Result: Preference value.
+ [ "ko-not-avail", "de-DE", ["de-DE"] ],
+ ];
+
+var loadCount = 0;
+var retrying = false;
+var script;
+
+var loadListener = async function(evt) {
+ if (loadCount == 0) {
+ script = SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ // eslint-disable-next-line mozilla/use-services
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ dir.append("tests");
+ dir.append("editor");
+ dir.append("spellchecker");
+ dir.append("tests");
+
+ var hunspell = Cc["@mozilla.org/spellchecker/engine;1"]
+ .getService(Ci.mozISpellCheckingEngine);
+
+ // Install en-GB, en-AU and de-DE dictionaries.
+ var en_GB = dir.clone();
+ var en_AU = dir.clone();
+ var de_DE = dir.clone();
+ en_GB.append("en-GB");
+ en_AU.append("en-AU");
+ de_DE.append("de-DE");
+ hunspell.addDirectory(en_GB);
+ hunspell.addDirectory(en_AU);
+ hunspell.addDirectory(de_DE);
+
+ addMessageListener("check-existence",
+ () => [en_GB.exists(), en_AU.exists(),
+ de_DE.exists()]);
+ addMessageListener("destroy", () => {
+ hunspell.removeDirectory(en_GB);
+ hunspell.removeDirectory(en_AU);
+ hunspell.removeDirectory(de_DE);
+ });
+ });
+ var existenceChecks = await script.sendQuery("check-existence");
+ is(existenceChecks[0], true, "true expected (en-GB directory should exist)");
+ is(existenceChecks[1], true, "true expected (en-AU directory should exist)");
+ is(existenceChecks[2], true, "true expected (de-DE directory should exist)");
+ }
+
+ SpecialPowers.pushPrefEnv({set: [["spellchecker.dictionary", tests[loadCount][1]]]},
+ function() { continueTest(evt); });
+};
+
+function continueTest(evt) {
+ var doc = evt.target.contentDocument;
+ var elem = doc.getElementById(tests[loadCount][0]);
+ var editor = SpecialPowers.wrap(elem).editor;
+ editor.setSpellcheckUserOverride(true);
+ var inlineSpellChecker = editor.getInlineSpellChecker(true);
+ const is_en_US = SpecialPowers.Services.locale.appLocaleAsBCP47 == "en-US";
+
+ const { onSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ onSpellCheck(elem, async function() {
+ var spellchecker = inlineSpellChecker.spellChecker;
+ let currentDictionaries;
+ try {
+ currentDictionaries = spellchecker.getCurrentDictionaries();
+ } catch (e) {}
+
+ if (!currentDictionaries && !retrying) {
+ // It's possible for an asynchronous font-list update to cause a reflow
+ // that disrupts the async spell-check and results in not getting a
+ // current dictionary here; if that happens, we retry the same testcase
+ // by reloading the iframe without bumping loadCount.
+ info(`No current dictionary: retrying testcase ${loadCount}`);
+ retrying = true;
+ } else {
+ let expectedDictionaries = tests[loadCount][2];
+ let dictionaryArray = Array.from(currentDictionaries);
+ is(
+ dictionaryArray.length,
+ expectedDictionaries.length,
+ "Expected matching dictionary count"
+ );
+ if (expectedDictionaries[0] != "*") {
+ ok(
+ dictionaryArray.every(dict => expectedDictionaries.includes(dict)),
+ "active dictionaries should match expectation"
+ );
+ } else if (is_en_US && tests[loadCount][0].startsWith("en")) {
+ // Current application locale is en-US and content lang is en or
+ // en-unknown, so we should use en-US dictionary as default.
+ is(
+ dictionaryArray[0],
+ "en-US",
+ "expected en-US that is application locale"
+ );
+ } else {
+ let dict = dictionaryArray[0];
+ var gotEn = (dict == "en-GB" || dict == "en-AU" || dict == "en-US");
+ is(gotEn, true, "expected en-AU or en-GB or en-US");
+ }
+
+ loadCount++;
+ retrying = false;
+ }
+
+ if (loadCount < tests.length) {
+ // Load the iframe again.
+ content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/multiple_content_languages_subframe.html?firstload=false";
+ } else {
+ // Remove the fake dictionaries again, since it's otherwise picked up by later tests.
+ await script.sendQuery("destroy");
+
+ SimpleTest.finish();
+ }
+ });
+}
+
+content.addEventListener("load", loadListener);
+
+content.src = "http://mochi.test:8888/tests/editor/spellchecker/tests/multiple_content_languages_subframe.html?firstload=true";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_nsIEditorSpellCheck_ReplaceWord.html b/editor/spellchecker/tests/test_nsIEditorSpellCheck_ReplaceWord.html
new file mode 100644
index 0000000000..a92c9f2f19
--- /dev/null
+++ b/editor/spellchecker/tests/test_nsIEditorSpellCheck_ReplaceWord.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for nsIEditorSpellCheck.ReplaceWord()</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<div contenteditable spellcheck="true" lang="en-US"></div>
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ const { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+ const editor = document.querySelector("div[contenteditable]");
+ async function replaceWord(aMisspelledWord, aCorrectWord, aReplaceAll) {
+ const editorObj = SpecialPowers.wrap(window).docShell.editingSession.getEditorForWindow(window);
+ const inlineSpellChecker = editorObj.getInlineSpellChecker(true);
+ await new Promise(resolve => maybeOnSpellCheck(editor, resolve));
+ const editorSpellCheck = inlineSpellChecker.spellChecker;
+ editorObj.beginTransaction();
+ try {
+ editorSpellCheck.ReplaceWord(aMisspelledWord, aCorrectWord, aReplaceAll);
+ } catch (e) {
+ ok(false, `Unexpected exception: ${e.message}`);
+ }
+ editorObj.endTransaction();
+ editorSpellCheck.GetNextMisspelledWord();
+ }
+
+ async function testReplaceAllMisspelledWords(aCorrectWord) {
+ editor.innerHTML = "<p>def abc def<br>abc def abc</p><p>abc def abc<br>def abc def</p>";
+ editor.focus();
+ editor.getBoundingClientRect();
+ await replaceWord("abc", aCorrectWord, true);
+ is(
+ editor.innerHTML,
+ `<p>def ${aCorrectWord} def<br>${aCorrectWord} def ${aCorrectWord}</p><p>${aCorrectWord} def ${aCorrectWord}<br>def ${aCorrectWord} def</p>`,
+ `nsIEditorSpellCheck.ReplaceWord(..., true) should replace all misspelled words with ${
+ (() => {
+ if (aCorrectWord.length > "abc".length) {
+ return "longer";
+ }
+ return aCorrectWord.length < "abc".length ? "shorter" : "same length"
+ })()
+ } correct word`
+ );
+ editor.blur();
+ editor.getBoundingClientRect();
+ }
+ await testReplaceAllMisspelledWords("ABC");
+ await testReplaceAllMisspelledWords("ABC!");
+ await testReplaceAllMisspelledWords("AB");
+
+ // TODO: Add tests for not all replacing cases.
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_spellcheck_after_edit.html b/editor/spellchecker/tests/test_spellcheck_after_edit.html
new file mode 100644
index 0000000000..24806addee
--- /dev/null
+++ b/editor/spellchecker/tests/test_spellcheck_after_edit.html
@@ -0,0 +1,194 @@
+<!doctype html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Spellcheck result after edit</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+function waitForTick() {
+ return new Promise(resolve =>
+ SimpleTest.executeSoon(
+ () => requestAnimationFrame(
+ () => requestAnimationFrame(resolve)
+ )
+ )
+ );
+}
+
+async function waitForOnSpellCheck(
+ aSpellCheckSelection,
+ aEditingHost,
+ aWaitForNumberOfMisspelledWords,
+ aWhen
+) {
+ info(`Waiting for onSpellCheck (${aWhen})...`);
+ for (let retry = 0; retry < 100; retry++) {
+ await waitForTick();
+ await new Promise(resolve => maybeOnSpellCheck(aEditingHost, resolve));
+ if (aWaitForNumberOfMisspelledWords === 0) {
+ if (aSpellCheckSelection.rangeCount === 0) {
+ break;
+ }
+ } else if (aSpellCheckSelection.rangeCount >= aWaitForNumberOfMisspelledWords) {
+ break;
+ }
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ /**
+ * test object should have:
+ * init function
+ * @param normalSel The normal selection for the editing host
+ * @param editingHost The editing host of the editor
+ * @return Number of misspelled word in the editor
+ *
+ * run function
+ * @param editingHost The editing host of the editor
+ * @return Expected number of misspelled word in the editor
+ *
+ * check function
+ * @param spellCheckSel The spellcheck selection for the editing host
+ * @param editingHost The editing host of the editor
+ */
+ for (const test of [
+ {
+ init: (normalSel, editingHost) => {
+ info("Staring to test spellcheck of misspelled word after joining paragraphs");
+ editingHost.innerHTML = "<p>It is</p><p>what I want</p>";
+ normalSel.collapse(editingHost.querySelector("p + p").firstChild, 0);
+ return 0;
+ },
+ run: (editingHost) => {
+ document.execCommand("delete");
+ return 0;
+ },
+ check: (spellCheckSel, editingHost) => {
+ is(
+ spellCheckSel.rangeCount,
+ 0,
+ "The joined misspelled word shouldn't be marked as misspelled word because caret is in the word"
+ );
+ },
+ },
+ {
+ init: (normalSel, editingHost) => {
+ info("Staring to test spellcheck of correct word after joining paragraphs");
+ editingHost.innerHTML = "<p>It's beco</p><p>ming nicer</p>";
+ normalSel.collapse(editingHost.querySelector("p + p").firstChild, 0);
+ return 2;
+ },
+ run: (editingHost) => {
+ document.execCommand("delete");
+ return 0;
+ },
+ check: (spellCheckSel, editingHost) => {
+ is(
+ spellCheckSel.rangeCount,
+ 0,
+ "There shouldn't be misspelled word after joining separated word anyway"
+ );
+ },
+ },
+ {
+ init: (normalSel, editingHost) => {
+ info("Staring to test spellcheck of correct words after splitting a paragraph");
+ editingHost.innerHTML = "<p>It iswhat I want</p>";
+ normalSel.collapse(editingHost.querySelector("p").firstChild, "It is".length);
+ return 1;
+ },
+ run: (editingHost) => {
+ document.execCommand("insertParagraph");
+ return 0;
+ },
+ check: (spellCheckSel, editingHost) => {
+ is(
+ spellCheckSel.rangeCount,
+ 0,
+ "No word should be marked as misspelled after split"
+ );
+ },
+ },
+ {
+ init: (normalSel, editingHost) => {
+ info("Staring to test spellcheck of misspelled words after splitting a paragraph");
+ editingHost.innerHTML = "<p>It's becoming nicer</p>";
+ normalSel.collapse(editingHost.querySelector("p").firstChild, "It's beco".length);
+ return 0;
+ },
+ run: (editingHost) => {
+ document.execCommand("insertParagraph");
+ return 1;
+ },
+ check: (spellCheckSel, editingHost) => {
+ is(
+ spellCheckSel.rangeCount,
+ 1,
+ "The split word in the first paragraph should be marked as misspelled, but the second paragraph's should be so because of caret is in it"
+ );
+ if (!spellCheckSel.rangeCount) {
+ return;
+ }
+ is(
+ SpecialPowers.unwrap(spellCheckSel.getRangeAt(0).startContainer),
+ editingHost.querySelector("p").firstChild,
+ "First misspelled word should start in the first child of the first <p>"
+ );
+ is(
+ SpecialPowers.unwrap(spellCheckSel.getRangeAt(0).endContainer),
+ editingHost.querySelector("p").firstChild,
+ "First misspelled word should end in the first child of the first <p>"
+ );
+ is(
+ spellCheckSel.getRangeAt(0).startOffset,
+ "It's ".length,
+ "First misspelled word should start after 'It '"
+ );
+ is(
+ spellCheckSel.getRangeAt(0).endOffset,
+ "It's beco".length,
+ "First misspelled word should end by after 'bec'"
+ );
+ },
+ },
+ ]) {
+ const editingHost = document.createElement("div");
+ editingHost.setAttribute("contenteditable", "");
+ editingHost.setAttribute("spellcheck", "true");
+ document.body.appendChild(editingHost);
+ editingHost.focus();
+ const editor =
+ SpecialPowers.wrap(window).docShell.editingSession.getEditorForWindow(window);
+ const nsISelectionController = SpecialPowers.Ci.nsISelectionController;
+ const normalSel = editor.selectionController.getSelection(
+ nsISelectionController.SELECTION_NORMAL
+ );
+ const spellCheckSel = editor.selectionController.getSelection(
+ nsISelectionController.SELECTION_SPELLCHECK
+ );
+ const initialMisspelledWords = test.init(normalSel, editingHost);
+ await waitForOnSpellCheck(
+ spellCheckSel, editingHost, initialMisspelledWords, "before edit"
+ );
+ await waitForTick();
+ const expectedMisspelledWords = test.run(editingHost);
+ await waitForOnSpellCheck(
+ spellCheckSel, editingHost, expectedMisspelledWords, "after edit"
+ );
+ test.check(spellCheckSel, editingHost);
+ editingHost.remove();
+ await waitForTick();
+ }
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_spellcheck_after_pressing_navigation_key.html b/editor/spellchecker/tests/test_spellcheck_after_pressing_navigation_key.html
new file mode 100644
index 0000000000..b1f36161ac
--- /dev/null
+++ b/editor/spellchecker/tests/test_spellcheck_after_pressing_navigation_key.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1729653
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1729653</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<textarea rows="20" cols="50">That undfgdfg seems OK.</textarea>
+<script>
+let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+);
+
+function waitForTick() {
+ return new Promise(resolve => SimpleTest.executeSoon(resolve));
+}
+
+function waitForOnSpellCheck(aTextArea) {
+ info("Waiting for onSpellCheck...");
+ return new Promise(resolve => maybeOnSpellCheck(aTextArea, resolve));
+}
+
+/** Test for Bug 1729653 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ const textarea = document.querySelector("textarea");
+ textarea.focus();
+ textarea.selectionStart = textarea.selectionEnd = "That undfgdfg".length;
+ const editor = SpecialPowers.wrap(textarea).editor;
+ const nsISelectionController = SpecialPowers.Ci.nsISelectionController;
+ const selection = editor.selectionController.getSelection(nsISelectionController.SELECTION_SPELLCHECK);
+ const spellChecker = SpecialPowers.Cu.createSpellChecker();
+ spellChecker.InitSpellChecker(editor, false);
+ info("Waiting for current dictionary update...");
+ await new Promise(resolve => spellChecker.UpdateCurrentDictionary(resolve));
+ if (selection.rangeCount === 0) {
+ await waitForOnSpellCheck(textarea);
+ }
+ if (selection.rangeCount == 1) {
+ is(
+ selection.getRangeAt(0).toString(),
+ "undfgdfg",
+ "\"undfgdfg\" should be marked as misspelled word at start"
+ );
+ } else {
+ is(selection.rangeCount, 1, "We should have a misspelled word at start");
+ }
+ synthesizeKey(" ");
+ synthesizeKey("KEY_Backspace");
+ textarea.addEventListener("keydown", event => {
+ event.stopImmediatePropagation(); // This shouldn't block spellchecker to handle it.
+ }, {once: true});
+ synthesizeKey("KEY_End");
+ await waitForTick();
+ if (selection.rangeCount === 0) {
+ await waitForOnSpellCheck(textarea);
+ }
+ if (selection.rangeCount == 1) {
+ is(
+ selection.getRangeAt(0).toString(),
+ "undfgdfg",
+ "\"undfgdfg\" should be marked as misspelled word at end"
+ );
+ } else {
+ is(selection.rangeCount, 1, "We should have a misspelled word at end");
+ }
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/editor/spellchecker/tests/test_spellcheck_selection.html b/editor/spellchecker/tests/test_spellcheck_selection.html
new file mode 100644
index 0000000000..983f67c51f
--- /dev/null
+++ b/editor/spellchecker/tests/test_spellcheck_selection.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1779846: Test enableSelectionChecking=true on nsIEditorSpellCheck.InitSpellChecker</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<div contenteditable lang="en-US">missspelled</div>
+
+<script>
+add_task(async function() {
+ await new Promise(resolve => SimpleTest.waitForFocus(resolve));
+
+ let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+
+ let editingHost = document.querySelector("div[contenteditable][lang=en-US]");
+ editingHost.focus();
+
+ await new Promise(resolve => maybeOnSpellCheck(editingHost, resolve));
+
+ let editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ let editor = editingSession.getEditorForWindow(window);
+ let spellchecker = SpecialPowers.Cu.createSpellChecker();
+ spellchecker.setFilterType(spellchecker.FILTERTYPE_NORMAL);
+
+ /* Select "missspelled" in the <div>. */
+ window.getSelection().selectAllChildren(editingHost);
+
+ /* Pass true to InitSpellChecker to spellcheck the current selection of the editor.*/
+ await new Promise(resolve => spellchecker.InitSpellChecker(editor, true, resolve));
+
+ /* InitSpellChecker with enableSelectionChecking=true shouldn't throw any errors. */
+ ok(spellchecker.canSpellCheck());
+});
+</script>
diff --git a/editor/spellchecker/tests/test_suggest.html b/editor/spellchecker/tests/test_suggest.html
new file mode 100644
index 0000000000..b3e35458c7
--- /dev/null
+++ b/editor/spellchecker/tests/test_suggest.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for nsIEditorSpellChecfker.sugget</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+
+<div contenteditable id="en-US" lang="en-US">missspelled</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+add_task(async function() {
+ await new Promise(resolve => SimpleTest.waitForFocus(resolve));
+
+ let { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://testing-common/AsyncSpellCheckTestHelper.sys.mjs"
+ );
+
+ let element = document.getElementById("en-US");
+ element.focus();
+
+ await new Promise(resolve => maybeOnSpellCheck(element, resolve));
+
+ let editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ let editor = editingSession.getEditorForWindow(window);
+ let spellchecker = SpecialPowers.Cu.createSpellChecker();
+ spellchecker.setFilterType(spellchecker.FILTERTYPE_NORMAL);
+ await new Promise(resolve => spellchecker.InitSpellChecker(editor, false, resolve));
+
+ let suggestions = await spellchecker.suggest("misspelled", 5);
+ is(suggestions.length, 0, "\"misspelled\" is correct word");
+
+ suggestions = await spellchecker.suggest("missspelled", 5);
+ is(suggestions.length, 5, "\"missspelled\" isn't correct word");
+});
+</script>
+</pre>
+</body>
+</html>