Bug 1270110 - Hide narrate button if no synth voices are present in browser. r=Gijs draft
authorEitan Isaacson <eitan@monotonous.org>
Fri, 15 Jul 2016 12:53:48 -0700
changeset 389218 ced91ba107515007f40438eb2bdf788bcf3e0525
parent 389217 3b4efab160e08ee4905fcf3d5c0f342d4aaf589a
child 525672 6bc2a0c164693cad91dc826c470e8f0bb29e3b20
push id23316
push userbmo:eitan@monotonous.org
push dateMon, 18 Jul 2016 18:33:00 +0000
reviewersGijs
bugs1270110
milestone50.0a1
Bug 1270110 - Hide narrate button if no synth voices are present in browser. r=Gijs MozReview-Commit-ID: iP7mMbeiUo
toolkit/components/narrate/NarrateControls.jsm
--- 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));
     }
   },