Bug 1447384 - fix narrate styling falling foul of CSP, r?johannh draft
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Tue, 27 Mar 2018 17:39:22 +0100
changeset 773197 55762e81b9290a572b47ffb3f5c92edea8f52dd2
parent 772574 7b9da7139d94951431a148dcaf8a388640c91b27
push id104173
push userbmo:gijskruitbosch+bugs@gmail.com
push dateTue, 27 Mar 2018 17:07:29 +0000
reviewersjohannh
bugs1447384
milestone61.0a1
Bug 1447384 - fix narrate styling falling foul of CSP, r?johannh MozReview-Commit-ID: EsMwwxVVyFE
toolkit/components/narrate/NarrateControls.jsm
toolkit/themes/shared/jar.inc.mn
toolkit/themes/shared/narrate.css
toolkit/themes/shared/narrate/waveform.svg
--- a/toolkit/components/narrate/NarrateControls.jsm
+++ b/toolkit/components/narrate/NarrateControls.jsm
@@ -22,94 +22,49 @@ function NarrateControls(mm, win, langua
   win.addEventListener("unload", this);
 
   // Append content style sheet in document head
   let style = win.document.createElement("link");
   style.rel = "stylesheet";
   style.href = "chrome://global/skin/narrate.css";
   win.document.head.appendChild(style);
 
-  function localize(pieces, ...substitutions) {
-    let result = pieces[0];
-    for (let i = 0; i < substitutions.length; ++i) {
-      result += gStrings.GetStringFromName(substitutions[i]) + pieces[i + 1];
-    }
-    return result;
-  }
+  let elemL10nMap = {
+    ".narrate-toggle": "narrate",
+    ".narrate-skip-previous": "back",
+    ".narrate-start-stop": "start",
+    ".narrate-skip-next": "forward",
+    ".narrate-rate-input": "speed",
+  };
 
   let dropdown = win.document.createElement("ul");
   dropdown.className = "dropdown narrate-dropdown";
-  // We need inline svg here for the animation to work (bug 908634 & 1190881).
-  // eslint-disable-next-line no-unsanitized/property
   dropdown.innerHTML =
-    localize`<li>
-       <button class="dropdown-toggle button 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); }
-              30%  { transform: scaleY(1);   }
-              100% { transform: scaleY(1);   }
-            }
-
-            .waveform > rect {
-              fill: #808080;
-            }
-
-            .speaking .waveform > rect {
-              fill: #58bf43;
-              transform-box: fill-box;
-              transform-origin: 50% 50%;
-              animation-name: grow;
-              animation-duration: 1750ms;
-              animation-iteration-count: infinite;
-              animation-timing-function: linear;
-            }
-
-            .waveform > rect:nth-child(2) { animation-delay: 250ms; }
-            .waveform > rect:nth-child(3) { animation-delay: 500ms; }
-            .waveform > rect:nth-child(4) { animation-delay: 750ms; }
-            .waveform > rect:nth-child(5) { animation-delay: 1000ms; }
-            .waveform > rect:nth-child(6) { animation-delay: 1250ms; }
-            .waveform > rect:nth-child(7) { animation-delay: 1500ms; }
-
-          </style>
-          <g class="waveform">
-            <rect x="1"  y="8" width="2" height="8"  rx=".5" ry=".5" />
-            <rect x="4"  y="5" width="2" height="14" rx=".5" ry=".5" />
-            <rect x="7"  y="8" width="2" height="8"  rx=".5" ry=".5" />
-            <rect x="10" y="4" width="2" height="16" rx=".5" ry=".5" />
-            <rect x="13" y="2" width="2" height="20" rx=".5" ry=".5" />
-            <rect x="16" y="4" width="2" height="16" rx=".5" ry=".5" />
-            <rect x="19" y="7" width="2" height="10" rx=".5" ry=".5" />
-          </g>
-         </svg>
-        </button>
+    `<li>
+       <button class="dropdown-toggle button narrate-toggle" hidden></button>
     </li>
     <li class="dropdown-popup">
       <div class="narrate-row narrate-control">
-        <button disabled class="narrate-skip-previous"
-                title="${"back"}"></button>
-        <button class="narrate-start-stop" title="${"start"}"></button>
-        <button disabled class="narrate-skip-next"
-                title="${"forward"}"></button>
+        <button disabled class="narrate-skip-previous"></button>
+        <button class="narrate-start-stop"></button>
+        <button disabled class="narrate-skip-next"></button>
       </div>
       <div class="narrate-row narrate-rate">
-        <input class="narrate-rate-input" value="0" title="${"speed"}"
+        <input class="narrate-rate-input" value="0"
                step="5" max="100" min="-100" type="range">
       </div>
       <div class="narrate-row narrate-voices"></div>
       <div class="dropdown-arrow"></div>
     </li>`;
 
+  for (let [selector, stringID] of Object.entries(elemL10nMap)) {
+    dropdown.querySelector(selector).setAttribute("title",
+      gStrings.GetStringFromName(stringID));
+  }
+
   this.narrator = new Narrator(win, languagePromise);
 
   let branch = Services.prefs.getBranch("narrate.");
   let selectLabel = gStrings.GetStringFromName("selectvoicelabel");
   this.voiceSelect = new VoiceSelect(win, selectLabel);
   this.voiceSelect.element.addEventListener("change", this);
   this.voiceSelect.element.classList.add("voice-select");
   win.speechSynthesis.addEventListener("voiceschanged", this);
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -50,16 +50,17 @@ toolkit.jar:
   skin/classic/global/narrate.css                          (../../shared/narrate.css)
   skin/classic/global/narrate/arrow.svg                    (../../shared/narrate/arrow.svg)
   skin/classic/global/narrate/back.svg                     (../../shared/narrate/back.svg)
   skin/classic/global/narrate/fast.svg                     (../../shared/narrate/fast.svg)
   skin/classic/global/narrate/forward.svg                  (../../shared/narrate/forward.svg)
   skin/classic/global/narrate/slow.svg                     (../../shared/narrate/slow.svg)
   skin/classic/global/narrate/start.svg                    (../../shared/narrate/start.svg)
   skin/classic/global/narrate/stop.svg                     (../../shared/narrate/stop.svg)
+  skin/classic/global/narrate/waveform.svg                 (../../shared/narrate/waveform.svg)
   skin/classic/global/in-content/check.svg                 (../../shared/in-content/check.svg)
   skin/classic/global/in-content/check-partial.svg         (../../shared/in-content/check-partial.svg)
   skin/classic/global/in-content/dropdown.svg              (../../shared/in-content/dropdown.svg)
   skin/classic/global/in-content/help-glyph.svg            (../../shared/in-content/help-glyph.svg)
   skin/classic/global/in-content/radio.svg                 (../../shared/in-content/radio.svg)
   skin/classic/global/reader/RM-Close-24x24.svg            (../../shared/reader/RM-Close-24x24.svg)
   skin/classic/global/reader/RM-Minus-24x24.svg            (../../shared/reader/RM-Minus-24x24.svg)
   skin/classic/global/reader/RM-Plus-24x24.svg             (../../shared/reader/RM-Plus-24x24.svg)
--- a/toolkit/themes/shared/narrate.css
+++ b/toolkit/themes/shared/narrate.css
@@ -47,19 +47,24 @@ body.sepia .narrate-word-highlight {
 body.dark .narrate-word-highlight {
   border-bottom-color: #6f6f6f;
 }
 
 .narrate-dropdown {
   --border-color: #e5e5e5;
 }
 
-.narrate-toggle > svg {
-  display: block;
-  margin: 0 8px;
+.narrate-toggle {
+  margin: 0;
+  background-image: url("chrome://global/skin/narrate/waveform.svg");
+}
+
+.speaking .narrate-toggle {
+  /* This shows with an animation. */
+  background-image: url("chrome://global/skin/narrate/waveform.svg#waveform");
 }
 
 .narrate-dropdown > .dropdown-popup button {
   background-color: transparent;
 }
 
 .narrate-dropdown > .dropdown-popup button:hover:not(:disabled) {
   background-color: #eaeaea;
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/narrate/waveform.svg
@@ -0,0 +1,46 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     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); }
+    30%  { transform: scaleY(1);   }
+    100% { transform: scaleY(1);   }
+  }
+
+  .waveform > rect {
+    fill: #808080;
+  }
+
+  /* Only animate if we're using a hash to address the SVG */
+  g:target > rect {
+    fill: #58bf43;
+    transform-box: fill-box;
+    transform-origin: 50% 50%;
+    animation-name: grow;
+    animation-duration: 1750ms;
+    animation-iteration-count: infinite;
+    animation-timing-function: linear;
+  }
+
+  .waveform > rect:nth-child(2) { animation-delay: 250ms; }
+  .waveform > rect:nth-child(3) { animation-delay: 500ms; }
+  .waveform > rect:nth-child(4) { animation-delay: 750ms; }
+  .waveform > rect:nth-child(5) { animation-delay: 1000ms; }
+  .waveform > rect:nth-child(6) { animation-delay: 1250ms; }
+  .waveform > rect:nth-child(7) { animation-delay: 1500ms; }
+ </style>
+ <g id="waveform" class="waveform">
+   <rect x="1"  y="8" width="2" height="8"  rx=".5" ry=".5" />
+   <rect x="4"  y="5" width="2" height="14" rx=".5" ry=".5" />
+   <rect x="7"  y="8" width="2" height="8"  rx=".5" ry=".5" />
+   <rect x="10" y="4" width="2" height="16" rx=".5" ry=".5" />
+   <rect x="13" y="2" width="2" height="20" rx=".5" ry=".5" />
+   <rect x="16" y="4" width="2" height="16" rx=".5" ry=".5" />
+   <rect x="19" y="7" width="2" height="10" rx=".5" ry=".5" />
+ </g>
+</svg>