Bug 1270110 - Hide narrate button if no synth voices are present in browser. r=Gijs
MozReview-Commit-ID: iP7mMbeiUo
--- a/toolkit/components/narrate/NarrateControls.jsm
+++ b/toolkit/components/narrate/NarrateControls.jsm
@@ -38,17 +38,18 @@ function NarrateControls(mm, win) {
dropdown.id = "narrate-dropdown";
// We need inline svg here for the animation to work (bug 908634 & 1190881).
// The style animation can't be scoped (bug 830056).
dropdown.innerHTML =
localize`<style scoped>
@import url("chrome://global/skin/narrateControls.css");
</style>
<li>
- <button class="dropdown-toggle button" id="narrate-toggle" title="${"narrate"}">
+ <button class="dropdown-toggle button" id="narrate-toggle"
+ title="${"narrate"}" hidden>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="24" height="24" viewBox="0 0 24 24">
<style>
@keyframes grow {
0% { transform: scaleY(1); }
15% { transform: scaleY(1.5); }
15% { transform: scaleY(1.5); }
@@ -106,34 +107,37 @@ function NarrateControls(mm, win) {
<div class="dropdown-arrow"></div>
</li>`;
this.narrator = new Narrator(win);
let branch = Services.prefs.getBranch("narrate.");
let selectLabel = gStrings.GetStringFromName("selectvoicelabel");
this.voiceSelect = new VoiceSelect(win, selectLabel);
- this.voiceSelect.addOptions(this._getVoiceOptions(),
- branch.getCharPref("voice"));
this.voiceSelect.element.addEventListener("change", this);
this.voiceSelect.element.id = "voice-select";
win.speechSynthesis.addEventListener("voiceschanged", this);
dropdown.querySelector("#narrate-voices").appendChild(
this.voiceSelect.element);
dropdown.addEventListener("click", this, true);
let rateRange = dropdown.querySelector("#narrate-rate > input");
rateRange.addEventListener("input", this);
rateRange.addEventListener("mousedown", this);
rateRange.addEventListener("mouseup", this);
// The rate is stored as an integer.
rateRange.value = branch.getIntPref("rate");
+ if (this._setupVoices(branch.getCharPref("voice"))) {
+ // We disable this entire feature if there are no synthesis voices.
+ dropdown.querySelector("#narrate-toggle").hidden = false;
+ }
+
let tb = win.document.getElementById("reader-toolbar");
tb.appendChild(dropdown);
}
NarrateControls.prototype = {
handleEvent: function(evt) {
switch (evt.type) {
case "mousedown":
@@ -147,39 +151,47 @@ NarrateControls.prototype = {
break;
case "change":
this._onVoiceChange();
break;
case "click":
this._onButtonClick(evt);
break;
case "voiceschanged":
- this.voiceSelect.clear();
- this.voiceSelect.addOptions(this._getVoiceOptions(),
- Services.prefs.getCharPref("narrate.voice"));
+ // We disable this entire feature if there are no synthesis voices.
+ this._doc.getElementById("narrate-toggle").hidden =
+ !this._setupVoices(Services.prefs.getCharPref("narrate.voice"));
break;
}
},
- _getVoiceOptions: function() {
+ /**
+ * Returns true if synth voices are available.
+ */
+ _setupVoices: function(selectedVoice) {
+ this.voiceSelect.clear();
let win = this._win;
let comparer = win.Intl ?
(new Intl.Collator()).compare : (a, b) => a.localeCompare(b);
let options = win.speechSynthesis.getVoices().map(v => {
return {
label: this._createVoiceLabel(v),
value: v.voiceURI
};
}).sort((a, b) => comparer(a.label, b.label));
- options.unshift({
- label: gStrings.GetStringFromName("defaultvoice"),
- value: "automatic"
- });
- return options;
+ if (options.length) {
+ options.unshift({
+ label: gStrings.GetStringFromName("defaultvoice"),
+ value: "automatic"
+ });
+ this.voiceSelect.addOptions(options, selectedVoice);
+ }
+
+ return !!options.length;
},
_onRateInput: function(evt) {
if (!this._rateMousedown) {
AsyncPrefs.set("narrate.rate", parseInt(evt.target.value, 10));
this.narrator.setRate(this._convertRate(evt.target.value));
}
},