From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../contextMenu/browser_contextmenu_spellcheck.js | 334 +++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js (limited to 'browser/base/content/test/contextMenu/browser_contextmenu_spellcheck.js') 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); +}); -- cgit v1.2.3