summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js')
-rw-r--r--browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js334
1 files changed, 334 insertions, 0 deletions
diff --git a/browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js b/browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js
new file mode 100644
index 0000000000..6f556a58dd
--- /dev/null
+++ b/browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js
@@ -0,0 +1,334 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let contextMenu;
+
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+const example_base =
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ "http://example.com/browser/browser/base/content/test/contextMenu/";
+const MAIN_URL = example_base + "subtst_contextmenu_input.html";
+
+add_task(async function test_setup() {
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, MAIN_URL);
+
+ const chrome_base =
+ "chrome://mochitests/content/browser/browser/base/content/test/contextMenu/";
+ const contextmenu_common = chrome_base + "contextmenu_common.js";
+ /* import-globals-from contextmenu_common.js */
+ Services.scriptloader.loadSubScript(contextmenu_common, this);
+});
+
+add_task(async function test_text_input_spellcheck() {
+ await test_contextmenu(
+ "#input_spellcheck_no_value",
+ [
+ "context-undo",
+ false,
+ "context-redo",
+ false,
+ "---",
+ null,
+ "context-cut",
+ null, // ignore the enabled/disabled states; there are race conditions
+ // in the edit commands but they're not relevant for what we're testing.
+ "context-copy",
+ null,
+ "context-paste",
+ null, // ignore clipboard state
+ "context-delete",
+ null,
+ "context-selectall",
+ null,
+ "---",
+ null,
+ "spell-check-enabled",
+ true,
+ "spell-dictionaries",
+ true,
+ [
+ "spell-check-dictionary-en-US",
+ true,
+ "---",
+ null,
+ "spell-add-dictionaries",
+ true,
+ ],
+ null,
+ ],
+ {
+ waitForSpellCheck: true,
+ async preCheckContextMenuFn() {
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ async function () {
+ let doc = content.document;
+ let input = doc.getElementById("input_spellcheck_no_value");
+ input.setAttribute("spellcheck", "true");
+ input.clientTop; // force layout flush
+ }
+ );
+ },
+ }
+ );
+});
+
+add_task(async function test_text_input_spellcheckwrong() {
+ await test_contextmenu(
+ "#input_spellcheck_incorrect",
+ [
+ "*prodigality",
+ true, // spelling suggestion
+ "spell-add-to-dictionary",
+ true,
+ "---",
+ null,
+ "context-undo",
+ null,
+ "context-redo",
+ null,
+ "---",
+ null,
+ "context-cut",
+ null,
+ "context-copy",
+ null,
+ "context-paste",
+ null, // ignore clipboard state
+ "context-delete",
+ null,
+ "context-selectall",
+ null,
+ "---",
+ null,
+ "spell-check-enabled",
+ true,
+ "spell-dictionaries",
+ true,
+ [
+ "spell-check-dictionary-en-US",
+ true,
+ "---",
+ null,
+ "spell-add-dictionaries",
+ true,
+ ],
+ null,
+ ],
+ { waitForSpellCheck: true }
+ );
+});
+
+const kCorrectItems = [
+ "context-undo",
+ false,
+ "context-redo",
+ false,
+ "---",
+ null,
+ "context-cut",
+ null,
+ "context-copy",
+ null,
+ "context-paste",
+ null, // ignore clipboard state
+ "context-delete",
+ null,
+ "context-selectall",
+ null,
+ "---",
+ null,
+ "spell-check-enabled",
+ true,
+ "spell-dictionaries",
+ true,
+ [
+ "spell-check-dictionary-en-US",
+ true,
+ "---",
+ null,
+ "spell-add-dictionaries",
+ true,
+ ],
+ null,
+];
+
+add_task(async function test_text_input_spellcheckcorrect() {
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ });
+});
+
+add_task(async function test_text_input_spellcheck_deadactor() {
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ keepMenuOpen: true,
+ });
+ let wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
+
+ // Now the menu is open, and spellcheck is running, switch to another tab and
+ // close the original:
+ let tab = gBrowser.selectedTab;
+ await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.org");
+ BrowserTestUtils.removeTab(tab);
+ // Ensure we've invalidated the actor
+ await TestUtils.waitForCondition(
+ () => wgp.isClosed,
+ "Waiting for actor to be dead after tab closes"
+ );
+ contextMenu.hidePopup();
+
+ // Now go back to the input testcase:
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, MAIN_URL);
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ MAIN_URL
+ );
+
+ // Check the menu still looks the same, keep it open again:
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ keepMenuOpen: true,
+ });
+
+ // Now navigate the tab, after ensuring there's an unload listener, so
+ // we don't end up in bfcache:
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.body.setAttribute("onunload", "");
+ });
+ wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
+
+ const NEW_URL = MAIN_URL.replace(".com", ".org");
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, NEW_URL);
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ NEW_URL
+ );
+ // Ensure we've invalidated the actor
+ await TestUtils.waitForCondition(
+ () => wgp.isClosed,
+ "Waiting for actor to be dead after onunload"
+ );
+ contextMenu.hidePopup();
+
+ // Check the menu *still* looks the same (and keep it open again):
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ keepMenuOpen: true,
+ });
+
+ // Check what happens if the actor stays alive by loading the same page
+ // again; now the context menu stuff should be destroyed by the menu
+ // hiding, nothing else.
+ wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
+ BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, NEW_URL);
+ await BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ NEW_URL
+ );
+ contextMenu.hidePopup();
+
+ // Check the menu still looks the same:
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ });
+ // And test it a last time without any navigation:
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ });
+});
+
+add_task(async function test_text_input_spellcheck_multilingual() {
+ if (AppConstants.platform == "macosx") {
+ todo(
+ false,
+ "Need macOS support for closemenu attributes in order to " +
+ "stop the spellcheck menu closing, see bug 1796007."
+ );
+ return;
+ }
+ let sandbox = sinon.createSandbox();
+ registerCleanupFunction(() => sandbox.restore());
+
+ // We need to mock InlineSpellCheckerUI.mRemote's properties, but
+ // InlineSpellCheckerUI.mRemote won't exist until we initialize the context
+ // menu, so do that and then manually reinit the spellcheck bits so
+ // we control them:
+ await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
+ waitForSpellCheck: true,
+ keepMenuOpen: true,
+ });
+ sandbox
+ .stub(InlineSpellCheckerUI.mRemote, "dictionaryList")
+ .get(() => ["en-US", "nl-NL"]);
+ let setterSpy = sandbox.spy();
+ sandbox
+ .stub(InlineSpellCheckerUI.mRemote, "currentDictionaries")
+ .get(() => ["en-US"])
+ .set(setterSpy);
+ // Re-init the spellcheck items:
+ InlineSpellCheckerUI.clearDictionaryListFromMenu();
+ gContextMenu.initSpellingItems();
+
+ let dictionaryMenu = document.getElementById("spell-dictionaries-menu");
+ let menuOpen = BrowserTestUtils.waitForPopupEvent(dictionaryMenu, "shown");
+ dictionaryMenu.parentNode.openMenu(true);
+ await menuOpen;
+ checkMenu(dictionaryMenu, [
+ "spell-check-dictionary-nl-NL",
+ true,
+ "spell-check-dictionary-en-US",
+ true,
+ "---",
+ null,
+ "spell-add-dictionaries",
+ true,
+ ]);
+ is(
+ dictionaryMenu.children.length,
+ 4,
+ "Should have 2 dictionaries, a separator and 'add more dictionaries' item in the menu."
+ );
+
+ let dictionaryEventPromise = BrowserTestUtils.waitForEvent(
+ document,
+ "spellcheck-changed"
+ );
+ dictionaryMenu.activateItem(
+ dictionaryMenu.querySelector("[data-locale-code*=nl]")
+ );
+ let event = await dictionaryEventPromise;
+ Assert.deepEqual(
+ event.detail?.dictionaries,
+ ["en-US", "nl-NL"],
+ "Should have sent right dictionaries with event."
+ );
+ ok(setterSpy.called, "Should have set currentDictionaries");
+ Assert.deepEqual(
+ setterSpy.firstCall?.args,
+ [["en-US", "nl-NL"]],
+ "Should have called setter with single argument array of 2 dictionaries."
+ );
+ // Allow for the menu to potentially close:
+ await new Promise(r => Services.tm.dispatchToMainThread(r));
+ // Check it hasn't:
+ is(
+ dictionaryMenu.closest("menupopup").state,
+ "open",
+ "Main menu should still be open."
+ );
+ contextMenu.hidePopup();
+});
+
+add_task(async function test_cleanup() {
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});