summaryrefslogtreecommitdiffstats
path: root/toolkit/components/narrate
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/narrate')
-rw-r--r--toolkit/components/narrate/NarrateControls.sys.mjs92
-rw-r--r--toolkit/components/narrate/test/browser_narrate_toggle.js25
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();