334 lines
8.9 KiB
JavaScript
334 lines
8.9 KiB
JavaScript
/* 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.startLoadingURIString(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.startLoadingURIString(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.startLoadingURIString(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);
|
|
});
|