diff options
Diffstat (limited to 'toolkit/components/narrate')
-rw-r--r-- | toolkit/components/narrate/NarrateControls.sys.mjs | 92 | ||||
-rw-r--r-- | toolkit/components/narrate/test/browser_narrate_toggle.js | 25 |
2 files changed, 95 insertions, 22 deletions
diff --git a/toolkit/components/narrate/NarrateControls.sys.mjs b/toolkit/components/narrate/NarrateControls.sys.mjs index 316d2a0e34..ed2f8ff124 100644 --- a/toolkit/components/narrate/NarrateControls.sys.mjs +++ b/toolkit/components/narrate/NarrateControls.sys.mjs @@ -23,9 +23,9 @@ export function NarrateControls(win, languagePromise) { win.document.head.appendChild(style); let elemL10nMap = { - ".narrate-skip-previous": "back", + ".narrate-skip-previous": "previous-label", ".narrate-start-stop": "start-label", - ".narrate-skip-next": "forward", + ".narrate-skip-next": "next-label", ".narrate-rate-input": "speed", }; @@ -72,23 +72,34 @@ export function NarrateControls(win, languagePromise) { let narrateSkipPrevious = win.document.createElement("button"); narrateSkipPrevious.className = "narrate-skip-previous"; narrateSkipPrevious.disabled = true; + narrateSkipPrevious.ariaKeyShortcuts = "ArrowLeft"; narrateControl.appendChild(narrateSkipPrevious); let narrateStartStop = win.document.createElement("button"); narrateStartStop.className = "narrate-start-stop"; + narrateStartStop.ariaKeyShortcuts = "N"; narrateControl.appendChild(narrateStartStop); + let narrateSkipNext = win.document.createElement("button"); + narrateSkipNext.className = "narrate-skip-next"; + narrateSkipNext.disabled = true; + narrateSkipNext.ariaKeyShortcuts = "ArrowRight"; + narrateControl.appendChild(narrateSkipNext); + win.document.addEventListener("keydown", function (event) { if (win.document.hasFocus() && event.key === "n") { narrateStartStop.click(); } + //Arrow key direction also hardcoded for RTL in order to be + //consistent with playback arrows in UI panel + if (win.document.hasFocus() && event.key === "ArrowLeft") { + narrateSkipPrevious.click(); + } + if (win.document.hasFocus() && event.key === "ArrowRight") { + narrateSkipNext.click(); + } }); - let narrateSkipNext = win.document.createElement("button"); - narrateSkipNext.className = "narrate-skip-next"; - narrateSkipNext.disabled = true; - narrateControl.appendChild(narrateSkipNext); - let narrateRateInput = win.document.createElement("input"); narrateRateInput.className = "narrate-rate-input"; narrateRateInput.setAttribute("value", "0"); @@ -98,16 +109,44 @@ export function NarrateControls(win, languagePromise) { narrateRateInput.setAttribute("type", "range"); narrateRate.appendChild(narrateRateInput); - for (let [selector, stringID] of Object.entries(elemL10nMap)) { - if (selector === ".narrate-start-stop") { - let shortcut = gStrings.GetStringFromName("narrate-key-shortcut"); - let label = gStrings.formatStringFromName(stringID, [shortcut]); - - dropdown.querySelector(selector).setAttribute("title", label); + function setShortcutAttribute( + keyShortcut, + stringID, + selector, + isString = false + ) { + let shortcut; + if (isString) { + shortcut = keyShortcut; } else { - dropdown - .querySelector(selector) - .setAttribute("title", gStrings.GetStringFromName(stringID)); + shortcut = gStrings.GetStringFromName(keyShortcut); + } + let label = gStrings.formatStringFromName(stringID, [shortcut]); + + dropdown.querySelector(selector).setAttribute("title", label); + } + + for (const [selector, stringID] of Object.entries(elemL10nMap)) { + switch (selector) { + case ".narrate-start-stop": + setShortcutAttribute("narrate-key-shortcut", stringID, selector); + break; + + // Arrow direction also hardcoded for RTL in order to be + // consistent with playback arrows in UI panel + case ".narrate-skip-previous": + setShortcutAttribute("←", stringID, selector, true); + break; + + case ".narrate-skip-next": + setShortcutAttribute("→", stringID, selector, true); + break; + + default: + dropdown + .querySelector(selector) + .setAttribute("title", gStrings.GetStringFromName(stringID)); + break; } } @@ -282,15 +321,26 @@ NarrateControls.prototype = { dropdown.classList.toggle("speaking", speaking); let startStopButton = this._doc.querySelector(".narrate-start-stop"); - let shortcutId = gStrings.GetStringFromName("narrate-key-shortcut"); + let skipPreviousButton = this._doc.querySelector(".narrate-skip-previous"); + let skipNextButton = this._doc.querySelector(".narrate-skip-next"); + + skipPreviousButton.disabled = !speaking; + skipNextButton.disabled = !speaking; + + let narrateShortcutId = gStrings.GetStringFromName("narrate-key-shortcut"); + let skipPreviousShortcut = "←"; + let skipNextShortcut = "→"; startStopButton.title = gStrings.formatStringFromName( speaking ? "stop-label" : "start-label", - [shortcutId] + [narrateShortcutId] ); - - this._doc.querySelector(".narrate-skip-previous").disabled = !speaking; - this._doc.querySelector(".narrate-skip-next").disabled = !speaking; + skipPreviousButton.title = gStrings.formatStringFromName("previous-label", [ + skipPreviousShortcut, + ]); + skipNextButton.title = gStrings.formatStringFromName("next-label", [ + skipNextShortcut, + ]); }, _createVoiceLabel(voice) { diff --git a/toolkit/components/narrate/test/browser_narrate_toggle.js b/toolkit/components/narrate/test/browser_narrate_toggle.js index 54de276001..a7713f01b1 100644 --- a/toolkit/components/narrate/test/browser_narrate_toggle.js +++ b/toolkit/components/narrate/test/browser_narrate_toggle.js @@ -4,6 +4,8 @@ // This test verifies that the keyboard shortcut "n" will Start/Stop the // narration of an article in readermode when the article is in focus. +// This test also verifies that the keyboard shortcut "←" (left arrow) will +// skip the narration backward, while "→" (right arrow) skips it forward. registerCleanupFunction(teardown); @@ -11,18 +13,39 @@ add_task(async function testToggleNarrate() { setup(); await spawnInNewReaderTab(TEST_ARTICLE, async function () { + let TEST_VOICE = "urn:moz-tts:fake:teresa"; let $ = content.document.querySelector.bind(content.document); + let prefChanged = NarrateTestUtils.waitForPrefChange("narrate.voice"); + NarrateTestUtils.selectVoice(content, TEST_VOICE); + await prefChanged; + await NarrateTestUtils.waitForNarrateToggle(content); let eventUtils = NarrateTestUtils.getEventUtils(content); NarrateTestUtils.isStoppedState(content, ok); + let promiseEvent = ContentTaskUtils.waitForEvent(content, "paragraphstart"); $(NarrateTestUtils.TOGGLE).focus(); eventUtils.synthesizeKey("n", {}, content); + let speechinfo = (await promiseEvent).detail; + let paragraph = speechinfo.paragraph; + + NarrateTestUtils.isStartedState(content, ok); + + promiseEvent = ContentTaskUtils.waitForEvent(content, "paragraphstart"); + eventUtils.synthesizeKey("KEY_ArrowRight", {}, content); + speechinfo = (await promiseEvent).detail; + isnot(speechinfo.paragraph, paragraph, "next paragraph is being spoken"); + + NarrateTestUtils.isStartedState(content, ok); + + promiseEvent = ContentTaskUtils.waitForEvent(content, "paragraphstart"); + eventUtils.synthesizeKey("KEY_ArrowLeft", {}, content); + speechinfo = (await promiseEvent).detail; + is(speechinfo.paragraph, paragraph, "first paragraph being spoken"); - await ContentTaskUtils.waitForEvent(content, "paragraphstart"); NarrateTestUtils.isStartedState(content, ok); $(NarrateTestUtils.TOGGLE).focus(); |