Bug 1271765 - Part 2: Desktop video control visual refresh. r=jaws
☠☠ backed out by 60af44ebfa62 ☠ ☠
authorRay Lin <ralin@mozilla.com>
Wed, 12 Oct 2016 13:31:32 +0800
changeset 321590 02d34b18d76bf7d59d09149385b00586111146e7
parent 321589 08811364762917e139d7459faf3b324f076c5e95
child 321591 235ea1c681db5aaec4b81466d65a4dcfe374c9ce
push id83647
push userkwierso@gmail.com
push dateTue, 08 Nov 2016 22:08:41 +0000
treeherdermozilla-inbound@1d0b02250149 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws
bugs1271765
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1271765 - Part 2: Desktop video control visual refresh. r=jaws MozReview-Commit-ID: 1GfyGmrhgCs
dom/media/webvtt/vtt.jsm
toolkit/content/widgets/videocontrols.xml
toolkit/locales/en-US/chrome/global/videocontrols.dtd
toolkit/themes/shared/jar.inc.mn
toolkit/themes/shared/media/TopLevelVideoDocument.css
toolkit/themes/shared/media/closeCaptionButton.png
toolkit/themes/shared/media/closeCaptionButton@2x.png
toolkit/themes/shared/media/closedCaptionButton.svg
toolkit/themes/shared/media/error.png
toolkit/themes/shared/media/fullscreenButton.png
toolkit/themes/shared/media/fullscreenButton.svg
toolkit/themes/shared/media/fullscreenButton@2x.png
toolkit/themes/shared/media/muteButton.png
toolkit/themes/shared/media/muteButton.svg
toolkit/themes/shared/media/muteButton@2x.png
toolkit/themes/shared/media/noAudio.png
toolkit/themes/shared/media/noAudio@2x.png
toolkit/themes/shared/media/pauseButton.png
toolkit/themes/shared/media/pauseButton.svg
toolkit/themes/shared/media/pauseButton@2x.png
toolkit/themes/shared/media/playButton.png
toolkit/themes/shared/media/playButton.svg
toolkit/themes/shared/media/playButton@2x.png
toolkit/themes/shared/media/scrubberThumb.png
toolkit/themes/shared/media/scrubberThumb@2x.png
toolkit/themes/shared/media/scrubberThumbWide.png
toolkit/themes/shared/media/scrubberThumbWide@2x.png
toolkit/themes/shared/media/unmuteButton.png
toolkit/themes/shared/media/unmuteButton@2x.png
toolkit/themes/shared/media/videocontrols.css
toolkit/themes/shared/media/volume-empty.png
toolkit/themes/shared/media/volume-empty@2x.png
toolkit/themes/shared/media/volume-full.png
toolkit/themes/shared/media/volume-full@2x.png
--- a/dom/media/webvtt/vtt.jsm
+++ b/dom/media/webvtt/vtt.jsm
@@ -1146,17 +1146,17 @@ this.EXPORTED_SYMBOLS = ["WebVTT"];
       overlay.removeChild(overlay.firstChild);
     }
 
     var controlBar;
     var controlBarShown;
 
     if (controls) {
       controlBar = controls.ownerDocument.getAnonymousElementByAttribute(
-        controls, "class", "controlBar");
+        controls, "anonid", "controlBar");
       controlBarShown = controlBar ? !!controlBar.clientHeight : false;
     }
 
     var paddedOverlay = window.document.createElement("div");
     paddedOverlay.style.position = "absolute";
     paddedOverlay.style.left = "0";
     paddedOverlay.style.right = "0";
     paddedOverlay.style.top = "0";
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -41,16 +41,17 @@
         </getter>
         <setter>
           <![CDATA[
           this.setAttribute("showhours", val);
           // If the duration becomes known while we're still showing the value
           // for time=0, immediately update the value to show or hide the hours.
           // It's less intrusive to do it now than when the user clicks play and
           // is looking right next to the thumb.
+          if (!this.timeLabel) return;
           var displayedTime = this.timeLabel.getAttribute("value");
           if (val && displayedTime == "0:00")
               this.timeLabel.setAttribute("value", "0:00:00");
           else if (!val && displayedTime == "0:00:00")
               this.timeLabel.setAttribute("value", "0:00");
           ]]>
         </setter>
       </property>
@@ -126,17 +127,21 @@
             switch (which) {
               case "curpos":
                 if (this.type == "scrubber") {
                     // Update the time shown in the thumb.
                     this.thumb.setTime(newValue);
                     this.Utils.positionLabel.setAttribute("value", this.thumb.timeLabel.value);
                     // Update the value bar to match the thumb position.
                     let percent = newValue / this.max;
-                    this.valueBar.value = Math.round(percent * 10000); // has max=10000
+                    if (!isNaN(percent) && percent != Infinity) {
+                      this.valueBar.value = Math.round(percent * 10000); // has max=10000
+                    } else {
+                      this.valueBar.removeAttribute("value");
+                    }
                 }
 
                 // The value of userChanged is true when changing the position with the mouse,
                 // but not when pressing an arrow key. However, the base binding sets
                 // ._userChanged in its keypress handlers, so we just need to check both.
                 if (!userChanged && !this._userChanged)
                   return;
                 this.setAttribute("value", newValue);
@@ -198,84 +203,89 @@
 
   <binding id="videoControls">
 
     <resources>
         <stylesheet src="chrome://global/content/bindings/videocontrols.css"/>
         <stylesheet src="chrome://global/skin/media/videocontrols.css"/>
     </resources>
 
-    <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-                 class="mediaControlsFrame">
-        <stack flex="1">
-            <vbox flex="1" class="statusOverlay" hidden="true">
-                <box class="statusIcon"/>
-                <label class="errorLabel" anonid="errorAborted">&error.aborted;</label>
-                <label class="errorLabel" anonid="errorNetwork">&error.network;</label>
-                <label class="errorLabel" anonid="errorDecode">&error.decode;</label>
-                <label class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</label>
-                <label class="errorLabel" anonid="errorNoSource">&error.noSource2;</label>
-                <label class="errorLabel" anonid="errorGeneric">&error.generic;</label>
-            </vbox>
+    <xbl:content xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+    xmlns="http://www.w3.org/1999/xhtml" class="mediaControlsFrame">
+        <div anonid="controlsContainer" class="controlsContainer" role="none">
+            <div anonid="statusOverlay" class="statusOverlay stackItem" hidden="true">
+                <div anonid="statusIcon" class="statusIcon"></div>
+                <span class="errorLabel" anonid="errorAborted">&error.aborted;</span>
+                <span class="errorLabel" anonid="errorNetwork">&error.network;</span>
+                <span class="errorLabel" anonid="errorDecode">&error.decode;</span>
+                <span class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</span>
+                <span class="errorLabel" anonid="errorNoSource">&error.noSource2;</span>
+                <span class="errorLabel" anonid="errorGeneric">&error.generic;</span>
+            </div>
 
-            <vbox class="controlsOverlay">
-                <stack flex="1">
-                    <spacer class="controlsSpacer" flex="1"/>
-                    <box class="clickToPlay" hidden="true" flex="1"/>
-                    <vbox class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
-                </stack>
-                <hbox class="controlBar" hidden="true">
-                    <button class="playButton"
+            <div anonid="controlsOverlay" class="controlsOverlay stackItem">
+                <div class="controlsSpacerStack" aria-hideen="true">
+                  <div anonid="controlsSpacer" class="controlsSpacer stackItem" role="none"></div>
+                  <div anonid="clickToPlay" class="clickToPlay" hidden="true"></div>
+                </div>
+                <div anonid="controlBar" class="controlBar" hidden="true">
+                    <button anonid="playButton"
+                            class="playButton"
                             playlabel="&playButton.playLabel;"
                             pauselabel="&playButton.pauseLabel;"/>
-                    <stack class="scrubberStack" flex="1">
-                        <box class="backgroundBar"/>
-                        <progressmeter class="bufferBar"/>
-                        <progressmeter class="progressBar" max="10000"/>
-                        <scale class="scrubber" movetoclick="true"/>
-                    </stack>
-                    <vbox class="durationBox">
-                        <label class="positionLabel" role="presentation"/>
-                        <label class="durationLabel" role="presentation"/>
-                    </vbox>
-                    <button class="muteButton"
+                    <div anonid="scrubberStack" class="scrubberStack progressContainer" role="none">
+                      <div class="progressBackgroundBar stackItem" role="none">
+                        <div class="progressStack" role="none">
+                          <progress anonid="bufferBar" class="bufferBar" value="0" max="100"></progress>
+                          <progress anonid="progressBar" class="progressBar" value="0" max="100"></progress>
+                        </div>
+                      </div>
+                      <input type="range" anonid="scrubber" class="scrubber"/>
+                    </div>
+                    <span anonid="positionLabel" class="positionLabel" role="presentation"></span>
+                    <span anonid="durationLabel" class="durationLabel" role="presentation"></span>
+                    <div anonid="positionDurationBox" class="positionDurationBox" role="none">
+                      &positionAndDuration.nameFormat;
+                    </div>
+                    <div anonid="controlBarSpacer" class="controlBarSpacer" hidden="true" role="none"></div>
+                    <button anonid="muteButton"
+                            class="muteButton"
                             mutelabel="&muteButton.muteLabel;"
                             unmutelabel="&muteButton.unmuteLabel;"/>
-                    <stack class="volumeStack">
-                      <box class="volumeBackground"/>
-                      <box class="volumeForeground" anonid="volumeForeground"/>
-                      <scale class="volumeControl" movetoclick="true"/>
-                    </stack>
-                    <button class="closedCaptionButton"/>
-                    <button class="fullscreenButton"
+                    <div anonid="volumeStack" class="volumeStack progressContainer" role="none">
+                      <input type="range" anonid="volumeControl" class="volumeControl" min="0" max="100" step="1"/>
+                    </div>
+                    <button anonid="closedCaptionButton" class="closedCaptionButton"/>
+                    <button anonid="fullscreenButton"
+                            class="fullscreenButton"
                             enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
                             exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
-                </hbox>
-            </vbox>
-        </stack>
+                </div>
+                <div anonid="textTrackList" class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></div>
+            </div>
+        </div>
     </xbl:content>
 
     <implementation>
 
         <constructor>
             <![CDATA[
-            this.isTouchControl = false;
+            this.isTouchControls = false;
             this.randomID = 0;
 
             this.Utils = {
                 debug : false,
                 video : null,
                 videocontrols : null,
                 controlBar : null,
                 playButton : null,
                 muteButton : null,
                 volumeControl  : null,
                 durationLabel  : null,
                 positionLabel  : null,
-                scrubberThumb  : null,
                 scrubber       : null,
                 progressBar    : null,
                 bufferBar      : null,
                 statusOverlay  : null,
                 controlsSpacer : null,
                 clickToPlay    : null,
                 controlsOverlay : null,
                 fullscreenButton : null,
@@ -288,26 +298,27 @@
                                "playing", "waiting", "canplay", "canplaythrough",
                                "seeking", "seeked", "emptied", "loadedmetadata",
                                "error", "suspend", "stalled",
                                "mozinterruptbegin", "mozinterruptend" ],
 
                 firstFrameShown : false,
                 timeUpdateCount : 0,
                 maxCurrentTimeSeen : 0,
+                isPausedByDragging: false,
                 _isAudioOnly : false,
                 get isAudioOnly() { return this._isAudioOnly; },
                 set isAudioOnly(val) {
                     this._isAudioOnly = val;
                     this.setFullscreenButtonState();
 
                     if (!this.isTopLevelSyntheticDocument)
                         return;
                     if (this._isAudioOnly) {
-                        this.video.style.height = this._controlBarHeight + "px";
+                        this.video.style.height = this.controlBar.minHeight + "px";
                         this.video.style.width = "66%";
                     } else {
                         this.video.style.removeProperty("height");
                         this.video.style.removeProperty("width");
                     }
                 },
                 suppressError : false,
 
@@ -361,16 +372,17 @@
 
                     this.setFullscreenButtonState();
 
                     var duration = Math.round(this.video.duration * 1000); // in ms
                     var currentTime = Math.round(this.video.currentTime * 1000); // in ms
                     this.log("Initial playback position is at " + currentTime + " of " + duration);
                     // It would be nice to retain maxCurrentTimeSeen, but it would be difficult
                     // to determine if the media source changed while we were detached.
+                    this.initPositionDurationBox();
                     this.maxCurrentTimeSeen = currentTime;
                     this.showPosition(currentTime, duration);
 
                     // If we have metadata, check if this is a <video> without
                     // video data, or a video with no audio track.
                     if (this.video.readyState >= this.video.HAVE_METADATA) {
                         if (this.video instanceof HTMLVideoElement &&
                             (this.video.videoWidth == 0 || this.video.videoHeight == 0))
@@ -391,41 +403,92 @@
 
                     // If the first frame hasn't loaded, kick off a throbber fade-in.
                     if (this.video.readyState >= this.video.HAVE_CURRENT_DATA)
                         this.firstFrameShown = true;
 
                     // We can't determine the exact buffering status, but do know if it's
                     // fully loaded. (If it's still loading, it will fire a progress event
                     // and we'll figure out the exact state then.)
-                    this.bufferBar.setAttribute("max", 100);
+                    this.bufferBar.max = 100;
                     if (this.video.readyState >= this.video.HAVE_METADATA)
                         this.showBuffered();
                     else
-                        this.bufferBar.setAttribute("value", 0);
+                        this.bufferBar.value = 0;
 
                     // Set the current status icon.
                     if (this.hasError()) {
                         this.clickToPlay.hidden = true;
                         this.statusIcon.setAttribute("type", "error");
                         this.updateErrorText();
                         this.setupStatusFader(true);
                     }
 
                     // An event handler for |onresize| should be added when bug 227495 is fixed.
                     this.controlBar.hidden = false;
-                    this._playButtonWidth = this.playButton.clientWidth;
-                    this._durationLabelWidth = this.durationLabel.clientWidth;
-                    this._muteButtonWidth = this.muteButton.clientWidth;
-                    this._volumeControlWidth = this.volumeControl.clientWidth;
-                    this._closedCaptionButtonWidth = this.closedCaptionButton.clientWidth;
-                    this._fullscreenButtonWidth = this.fullscreenButton.clientWidth;
-                    this._controlBarHeight = this.controlBar.clientHeight;
+
+                    let layoutControls = [
+                      ...this.controlBar.children,
+                      this.durationSpan,
+                      this.controlBar,
+                      this.clickToPlay
+                    ];
+
+                    for (let control of layoutControls) {
+                      if (!control) {
+                        break;
+                      }
+
+                      Object.defineProperties(control, {
+                        minWidth: {
+                          value: control.clientWidth,
+                          writable: true
+                        },
+                        minHeight: {
+                          value: control.clientHeight,
+                          writable: true
+                        },
+                        isAdjustableControl: {
+                          value: true
+                        },
+                        isWanted: {
+                          value: true,
+                          writable: true
+                        },
+                        hideByAdjustment: {
+                          set: (v) => {
+                            if (!!v) {
+                              control.setAttribute("hidden", "true");
+                            } else {
+                              control.removeAttribute("hidden");
+                            }
+
+                            control._isHiddenByAdjustment = !!v;
+                          },
+                          get: () => control._isHiddenByAdjustment
+                        },
+                        _isHiddenByAdjustment: {
+                          value: false,
+                          writable: true
+                        }
+                      });
+                    }
+                    // Cannot get minimal width of flexible scrubber and clickToPlay.
+                    // Rewrite to empirical value for now.
+                    this.controlBar.minHeight = 40;
+                    this.scrubberStack.minWidth = 64;
+                    this.volumeControl.minWidth = 48;
+                    this.clickToPlay.minWidth = 48;
+
+                    if (this.positionDurationBox) {
+                      this.positionDurationBox.minWidth -= this.durationSpan.minWidth;
+                    }
+
+                    this.adjustControlSize();
                     this.controlBar.hidden = true;
-                    this.adjustControlSize();
 
                     // Can only update the volume controls once we've computed
                     // _volumeControlWidth, since the volume slider implementation
                     // depends on it.
                     this.updateVolumeControls();
                 },
 
                 setupNewLoadState : function() {
@@ -438,18 +501,20 @@
                     // so that they don't get in the way of the playing video. Otherwise we'll
                     // go ahead and reveal the controls now, so they're an obvious user cue.
                     //
                     // (Note: the |controls| attribute is already handled via layout/style/html.css)
                     var shouldShow = !this.dynamicControls ||
                       (this.video.paused &&
                        !(this.video.autoplay && this.video.mozAutoplayEnabled));
                     // Hide the overlay if the video time is non-zero or if an error occurred to workaround bug 718107.
-                    this.startFade(this.clickToPlay, shouldShow && !this.isAudioOnly &&
-                                   this.video.currentTime == 0 && !this.hasError(), true);
+                    let shouldClickToPlayShow = shouldShow && !this.isAudioOnly &&
+                                                this.video.currentTime == 0 && !this.hasError();
+                    this.startFade(this.clickToPlay, shouldClickToPlayShow, true);
+                    this.startFade(this.controlsSpacer, shouldClickToPlayShow, true);
                     this.startFade(this.controlBar, shouldShow, true);
                 },
 
                 get dynamicControls() {
                     // Don't fade controls for <audio> elements.
                     var enabled = !this.isAudioOnly;
 
                     // Allow tests to explicitly suppress the fading of controls.
@@ -459,39 +524,43 @@
                     // If the video hits an error, suppress controls if it
                     // hasn't managed to do anything else yet.
                     if (!this.firstFrameShown && this.hasError())
                         enabled = false;
 
                     return enabled;
                 },
 
+                updateVolume() {
+                  const volume = this.volumeControl.value;
+                  this.setVolume(volume / 100);
+                },
+
                 updateVolumeControls() {
                     var volume = this.video.muted ? 0 : this.video.volume;
                     var volumePercentage = Math.round(volume * 100);
                     this.updateMuteButtonState();
                     this.volumeControl.value = volumePercentage;
-                    this.volumeForeground.style.paddingRight = (1 - volume) * this._volumeControlWidth + "px";
                 },
 
                 handleEvent : function (aEvent) {
                     this.log("Got media event ----> " + aEvent.type);
 
                     // If the binding is detached (or has been replaced by a
                     // newer instance of the binding), nuke our event-listeners.
                     if (this.videocontrols.randomID != this.randomID) {
                         this.terminateEventListeners();
                         return;
                     }
 
                     switch (aEvent.type) {
                         case "play":
                             this.setPlayButtonState(false);
                             this.setupStatusFader();
-                            if (!this._triggeredByControls && this.dynamicControls && this.videocontrols.isTouchControl)
+                            if (!this._triggeredByControls && this.dynamicControls && this.videocontrols.isTouchControls)
                                 this.startFadeOut(this.controlBar);
                             if (!this._triggeredByControls)
                                 this.clickToPlay.hidden = true;
                             this._triggeredByControls = false;
                             break;
                         case "pause":
                             // Little white lie: if we've internally paused the video
                             // while dragging the scrubber, don't change the button state.
@@ -580,20 +649,21 @@
                             // statusOverlay while we wait for HAVE_ENOUGH_DATA).
                             // If we've seen more than 2 timeupdate events,
                             // the count is no longer relevant to setupStatusFader.
                             if (this.timeUpdateCount <= 2)
                                 this.setupStatusFader();
 
                             // If the user is dragging the scrubber ignore the delayed seek
                             // responses (don't yank the thumb away from the user)
-                            if (this.scrubber.isDragging)
+                            if (this.scrubber.isDragging || this.scrubber.startToDrag)
                                 return;
 
                             this.showPosition(currentTime, duration);
+                            this.showBuffered();
                             break;
                         case "emptied":
                             this.bufferBar.value = 0;
                             this.showPosition(0, 0);
                             break;
                         case "seeking":
                             this.showBuffered();
                             this.statusIcon.setAttribute("type", "throbber");
@@ -739,41 +809,120 @@
                             mins = "0" + mins;
                         timeString = hours + ":" + mins + ":" + secs;
                     } else {
                         timeString = mins + ":" + secs;
                     }
                     return timeString;
                 },
 
+                initPositionDurationBox : function () {
+                  if (this.videocontrols.isTouchControls) {
+                    return;
+                  }
+
+                  const positionTextNode = Array.prototype.find.call(
+                    this.positionDurationBox.childNodes, (n) => !!~n.textContent.search("#1"));
+                  const durationSpan = this.durationSpan;
+                  const durationFormat = durationSpan.textContent;
+                  const positionFormat = positionTextNode.textContent;
+
+                  durationSpan.classList.add("duration");
+                  durationSpan.setAttribute("role", "none");
+
+                  Object.defineProperties(this.positionDurationBox, {
+                    durationSpan: {
+                      value: durationSpan
+                    },
+                    position: {
+                      set: (v) => {
+                        positionTextNode.textContent = positionFormat.replace("#1", v);
+                      }
+                    },
+                    duration: {
+                      set: (v) => {
+                        durationSpan.textContent = v ? durationFormat.replace("#2", v) : "";
+                      }
+                    }
+                  });
+                },
+
                 showDuration : function (duration) {
                     let isInfinite = (duration == Infinity);
                     this.log("Duration is " + duration + "ms.\n");
 
                     if (isNaN(duration) || isInfinite)
                         duration = this.maxCurrentTimeSeen;
 
                     // Format the duration as "h:mm:ss" or "m:ss"
                     let timeString = isInfinite ? "" : this.formatTime(duration);
-                    this.durationLabel.setAttribute("value", timeString);
+                    if (this.videocontrols.isTouchControls) {
+                      this.durationLabel.setAttribute("value", timeString);
+                    } else {
+                      this.positionDurationBox.duration = timeString;
+                    }
 
                     // "durationValue" property is used by scale binding to
                     // generate accessible name.
                     this.scrubber.durationValue = timeString;
 
                     // If the duration is over an hour, thumb should show h:mm:ss instead of mm:ss
-                    this.scrubberThumb.showHours = (duration >= 3600000);
 
                     this.scrubber.max = duration;
                     // XXX Can't set increment here, due to bug 473103. Also, doing so causes
                     // snapping when dragging with the mouse, so we can't just set a value for
                     // the arrow-keys.
                     this.scrubber.pageIncrement = Math.round(duration / 10);
                 },
 
+                pauseVideoDuringDragging: function() {
+                  if (!this.video.paused &&
+                      !this.isPausedByDragging &&
+                      this.scrubber.isDragging) {
+                    this.isPausedByDragging = true;
+                    this.video.pause();
+                  }
+                },
+
+                onScrubberInput: function(e) {
+                  const duration = Math.round(this.video.duration * 1000); // in ms
+                  let time = this.scrubber.value;
+
+                  if (!this.scrubber.startToDrag || this.scrubber.isDragging) {
+                    this.seekToPosition(time);
+                    this.showPosition(time, duration);
+                  }
+
+                  this.scrubber.startToDrag = true;
+                },
+
+                onScrubberChange: function(e) {
+                  this.scrubber.startToDrag = false;
+                  this.scrubber.isDragging = false;
+
+                  if (this.isPausedByDragging) {
+                    this.video.play();
+                    this.isPausedByDragging = false;
+                  }
+                },
+
+                updateScrubberProgress() {
+                  if (this.videocontrols.isTouchControls) {
+                    return;
+                  }
+
+                  const positionPercent = this.scrubber.value / this.scrubber.max * 100;
+
+                  if (!isNaN(positionPercent) && positionPercent != Infinity) {
+                    this.progressBar.value = positionPercent;
+                  } else {
+                    this.progressBar.value = 0;
+                  }
+                },
+
                 seekToPosition : function(newPosition) {
                     newPosition /= 1000; // convert from ms
                     this.log("+++ seeking to " + newPosition);
                     if (this.videocontrols.isGonk) {
                         // We use fastSeek() on B2G, and an accurate (but slower)
                         // seek on other platforms (that are likely to be higher
                         // perf).
                         this.video.fastSeek(newPosition);
@@ -793,18 +942,25 @@
                     // it, or the video is a stream), then we want to fudge the duration
                     // by using the maximum playback position that's been seen.
                     if (currentTime > this.maxCurrentTimeSeen)
                         this.maxCurrentTimeSeen = currentTime;
                     this.showDuration(duration);
 
                     this.log("time update @ " + currentTime + "ms of " + duration + "ms");
 
-                    this.positionLabel.setAttribute("value", this.formatTime(currentTime));
+                    let positionTime = this.formatTime(currentTime);
+
                     this.scrubber.value = currentTime;
+                    if (this.videocontrols.isTouchControls) {
+                      this.positionLabel.setAttribute("value", positionTime);
+                    } else {
+                      this.positionDurationBox.position = positionTime;
+                      this.updateScrubberProgress();
+                    }
                 },
 
                 showBuffered : function() {
                     function bsearch(haystack, needle, cmp) {
                         var length = haystack.length;
                         var low = 0;
                         var high = length;
                         while (low < high) {
@@ -826,18 +982,19 @@
                             return 1;
                         } else if (time >= buffered.start(i)) {
                             return 0;
                         }
                         return -1;
                     }
 
                     var duration = Math.round(this.video.duration * 1000);
-                    if (isNaN(duration))
+                    if (isNaN(duration) || duration == Infinity) {
                         duration = this.maxCurrentTimeSeen;
+                    }
 
                     // Find the range that the current play position is in and use that
                     // range for bufferBar.  At some point we may support multiple ranges
                     // displayed in the bar.
                     var currentTime = this.video.currentTime;
                     var buffered = this.video.buffered;
                     var index = bsearch(buffered, currentTime, bufferedCompare);
                     var endTime = 0;
@@ -865,18 +1022,19 @@
                         Utils.startFade(Utils.controlBar, false);
                         Utils._hideControlsTimeout = 0;
                         Utils._controlsHiddenByTimeout = true;
                     }
                 },
                 HIDE_CONTROLS_TIMEOUT_MS : 2000,
                 onMouseMove : function (event) {
                     // Pause playing video when the mouse is dragging over the control bar.
-                    if (this.scrubber.isDragging) {
-                      this.scrubber.pauseVideoDuringDragging();
+                    if (this.scrubber.startToDrag) {
+                      this.scrubber.isDragging = true;
+                      this.pauseVideoDuringDragging();
                     }
 
                     // If the controls are static, don't change anything.
                     if (!this.dynamicControls)
                         return;
 
                     clearTimeout(this._hideControlsTimeout);
 
@@ -954,25 +1112,29 @@
                 },
 
                 startFade : function (element, fadeIn, immediate) {
                     if (element.classList.contains("controlBar") && fadeIn) {
                         // Bug 493523, the scrubber doesn't call valueChanged while hidden,
                         // so our dependent state (eg, timestamp in the thumb) will be stale.
                         // As a workaround, update it manually when it first becomes unhidden.
                         if (element.hidden)
+                          if (this.videocontrols.isTouchControls) {
                             this.scrubber.valueChanged("curpos", this.video.currentTime * 1000, false);
+                          } else {
+                            this.scrubber.value = this.video.currentTime * 1000;
+                          }
                     }
 
                     if (immediate)
                         element.setAttribute("immediate", true);
                     else
                         element.removeAttribute("immediate");
 
-                    if (fadeIn) {
+                    if (fadeIn && !(element.isAdjustableControl && element.hideByAdjustment)) {
                         element.hidden = false;
                         // force style resolution, so that transition begins
                         // when we remove the attribute.
                         element.clientTop;
                         element.removeAttribute("fadeout");
                         if (element.classList.contains("controlBar"))
                             this.controlsSpacer.removeAttribute("hideCursor");
                     } else {
@@ -990,17 +1152,19 @@
                         return;
 
                     var element = event.originalTarget;
 
                     // Nothing to do when a fade *in* finishes.
                     if (!element.hasAttribute("fadeout"))
                         return;
 
-                    this.scrubber.dragStateChanged(false);
+                    if (this.videocontrols.isTouchControls) {
+                      this.scrubber.dragStateChanged(false);
+                    }
                     element.hidden = true;
                 },
 
                 _triggeredByControls: false,
 
                 startPlay : function () {
                     this._triggeredByControls = true;
                     this.hideClickToPlay();
@@ -1097,24 +1261,27 @@
                 hideClickToPlay : function () {
                     let videoHeight = this.video.clientHeight;
                     let videoWidth = this.video.clientWidth;
 
                     // The play button will animate to 3x its size. This
                     // shows the animation unless the video is too small
                     // to show 2/3 of the animation.
                     let animationScale = 2;
-                    if (this._overlayPlayButtonHeight * animationScale > (videoHeight - this._controlBarHeight)||
-                        this._overlayPlayButtonWidth * animationScale > videoWidth) {
-                        this.clickToPlay.setAttribute("immediate", "true");
-                        this.clickToPlay.hidden = true;
+                    let animationMinSize = this.clickToPlay.minWidth * animationScale;
+
+                    if (animationMinSize > videoWidth ||
+                        animationMinSize > (videoHeight - this.controlBar.minHeight)) {
+                      this.clickToPlay.setAttribute("immediate", "true");
+                      this.clickToPlay.hidden = true;
                     } else {
                         this.clickToPlay.removeAttribute("immediate");
                     }
                     this.clickToPlay.setAttribute("fadeout", "true");
+                    this.controlsSpacer.setAttribute("fadeout", "true");
                 },
 
                 setPlayButtonState : function(aPaused) {
                   if (aPaused)
                       this.playButton.setAttribute("paused", "true");
                   else
                       this.playButton.removeAttribute("paused");
 
@@ -1248,43 +1415,48 @@
                     event.preventDefault(); // Prevent page scrolling
                 },
 
                 isSupportedTextTrack : function(textTrack) {
                   return textTrack.kind == "subtitles" ||
                          textTrack.kind == "captions";
                 },
 
+                get isClosedCaptionAvailable() {
+                  return this.overlayableTextTracks.length && !this.videocontrols.isTouchControls;
+                },
+
                 get overlayableTextTracks() {
                   return Array.prototype.filter.call(this.video.textTracks, this.isSupportedTextTrack);
                 },
 
                 isClosedCaptionOn : function () {
                   for (let tt of this.overlayableTextTracks) {
                     if (tt.mode === "showing") {
                       return true;
                     }
                   }
 
                   return false;
                 },
 
                 setClosedCaptionButtonState : function () {
-                  if (!this.overlayableTextTracks.length || this.videocontrols.isTouchControl) {
+                  if (!this.isClosedCaptionAvailable) {
                     this.closedCaptionButton.setAttribute("hidden", "true");
                     return;
                   }
 
                   this.closedCaptionButton.removeAttribute("hidden");
 
                   if (this.isClosedCaptionOn()) {
                     this.closedCaptionButton.setAttribute("enabled", "true");
                   } else {
                     this.closedCaptionButton.removeAttribute("enabled");
                   }
+                  this.adjustControlSize();
 
                   let ttItems = this.textTrackList.childNodes;
 
                   for (let tti of ttItems) {
                     const idx = +tti.getAttribute("index");
 
                     if (idx == this.currentTextTrackIndex) {
                       tti.setAttribute("on", "true");
@@ -1348,43 +1520,21 @@
                 },
 
                 onControlBarTransitioned : function () {
                   this.textTrackList.setAttribute("hidden", "true");
                   this.video.dispatchEvent(new CustomEvent("controlbarchange"));
                 },
 
                 toggleClosedCaption : function () {
-                  if (this.overlayableTextTracks.length === 1) {
-                    const lastTTIdx = this.overlayableTextTracks[0].index;
-                    this.changeTextTrack(this.isClosedCaptionOn() ? 0 : lastTTIdx);
-                    return;
-                  }
-
                   if (this.textTrackList.hasAttribute("hidden")) {
                     this.textTrackList.removeAttribute("hidden");
                   } else {
                     this.textTrackList.setAttribute("hidden", "true");
                   }
-
-                  let maxButtonWidth = 0;
-
-                  for (let tti of this.textTrackList.childNodes) {
-                    if (tti.clientWidth > maxButtonWidth) {
-                      maxButtonWidth = tti.clientWidth;
-                    }
-                  }
-
-                  if (maxButtonWidth > this.video.clientWidth) {
-                    maxButtonWidth = this.video.clientWidth;
-                  }
-
-                  for (let tti of this.textTrackList.childNodes) {
-                    tti.style.width = maxButtonWidth + "px";
-                  }
                 },
 
                 onTextTrackAdd : function (trackEvent) {
                   this.addNewTextTrack(trackEvent.track);
                   this.setClosedCaptionButtonState();
                 },
 
                 onTextTrackRemove : function (trackEvent) {
@@ -1409,16 +1559,21 @@
                       this.video.dispatchEvent(new CustomEvent("texttrackchange"));
                     }
                   }
 
                   this.setClosedCaptionButtonState();
                 },
 
                 initTextTracks : function () {
+                  if (!this.isClosedCaptionAvailable) {
+                    this.closedCaptionButton.setAttribute("hidden", "true");
+                    return;
+                  }
+
                   const offLabel = this.textTrackList.getAttribute("offlabel");
 
                   this.addNewTextTrack({
                     label: offLabel,
                     kind: "subtitles"
                   });
 
                   for (let tt of this.overlayableTextTracks) {
@@ -1446,118 +1601,156 @@
                 },
 
                 get isTopLevelSyntheticDocument() {
                   let doc = this.video.ownerDocument;
                   let win = doc.defaultView;
                   return doc.mozSyntheticDocument && win === win.top;
                 },
 
-                _playButtonWidth : 0,
-                _durationLabelWidth : 0,
-                _muteButtonWidth : 0,
-                _volumeControlWidth : 0,
-                _closedCaptionButtonWidth : 0,
-                _fullscreenButtonWidth : 0,
-                _controlBarHeight : 0,
-                _overlayPlayButtonHeight : 64,
-                _overlayPlayButtonWidth : 64,
-                _controlBarPaddingEnd: 8,
                 adjustControlSize : function adjustControlSize() {
-                    let doc = this.video.ownerDocument;
+                  if (!this.controlBar.minWidth || this.videocontrols.isTouchControls) {
+                    return;
+                  }
+
+                  let videoWidth = this.video.clientWidth;
+                  let videoHeight = this.video.clientHeight;
+                  const minControlBarPaddingWidth = 18;
+
+                  if (this.video.readyState >= this.video.HAVE_METADATA) {
+                    if (!this.isAudioOnly && this.video.videoWidth && this.video.videoHeight) {
+                      let rect = this.video.getBoundingClientRect();
+                      let widthRatio = rect.width / this.video.videoWidth;
+                      let heightRatio = rect.height / this.video.videoHeight;
+                      let resizedWidth = this.video.videoWidth * Math.min(widthRatio, heightRatio);
+
+                      this.controlsContainer.style.width = `${resizedWidth}px`;
+                      this.controlsContainer.style.left = `${(videoWidth - resizedWidth) / 2}px`;
 
-                    // The scrubber has |flex=1|, therefore |minScrubberWidth|
-                    // was generated by empirical testing.
-                    let minScrubberWidth = 25;
-                    let minWidthAllControls = this._playButtonWidth +
-                                              minScrubberWidth +
-                                              this._durationLabelWidth +
-                                              this._muteButtonWidth +
-                                              this._volumeControlWidth +
-                                              this._closedCaptionButtonWidth +
-                                              this._fullscreenButtonWidth;
+                      videoWidth = resizedWidth;
+                    } else {
+                      this.controlsContainer.style.width = "";
+                      this.controlsContainer.style.left = "";
+                    }
+                  }
+                  // Hide and show control in order.
+                  const prioritizedControls = [
+                    this.playButton,
+                    this.muteButton,
+                    this.fullscreenButton,
+                    this.closedCaptionButton,
+                    this.positionDurationBox,
+                    this.scrubberStack,
+                    this.durationSpan,
+                    this.volumeStack
+                  ];
 
-                    let isFullscreenUnavailable = this.controlBar.hasAttribute("fullscreen-unavailable");
-                    if (isFullscreenUnavailable) {
-                        // When the fullscreen button is hidden we add margin-end to the volume stack.
-                        minWidthAllControls -= this._fullscreenButtonWidth - this._controlBarPaddingEnd;
+                  if (this.controlBar.hasAttribute("fullscreen-unavailable")) {
+                    this.fullscreenButton.isWanted = false;
+                  }
+                  if (!this.isClosedCaptionAvailable) {
+                    this.closedCaptionButton.isWanted = false;
+                  }
+                  if (this.muteButton.hasAttribute("noAudio")) {
+                    this.volumeStack.isWanted = false;
+                  }
+
+                  let widthUsed = minControlBarPaddingWidth;
+                  let preventAppendControl = false;
+
+                  for (let control of prioritizedControls) {
+                    if (!control.isWanted) {
+                      control.hideByAdjustment = true;
+                      continue;
                     }
 
-                    let minHeightForControlBar = this._controlBarHeight;
-                    let minWidthOnlyPlayPause = this._playButtonWidth + this._muteButtonWidth;
-
-                    let isAudioOnly = this.isAudioOnly;
-                    let videoHeight = isAudioOnly ? minHeightForControlBar : this.video.clientHeight;
-                    let videoWidth = isAudioOnly ? minWidthAllControls : this.video.clientWidth;
+                    control.hideByAdjustment = preventAppendControl ||
+                                               widthUsed + control.minWidth > videoWidth;
 
-                    // Adapt the size of the controls to the size of the video
-                    if (this.video.readyState >= this.video.HAVE_METADATA) {
-                      if (!this.isAudioOnly && this.video.videoWidth && this.video.videoHeight) {
-                        var rect = this.video.getBoundingClientRect();
-                        var widthRatio = rect.width / this.video.videoWidth;
-                        var heightRatio = rect.height / this.video.videoHeight;
-                        var width = this.video.videoWidth * Math.min(widthRatio, heightRatio);
+                    if (control.hideByAdjustment) {
+                      preventAppendControl = true;
+                    } else {
+                      widthUsed += control.minWidth;
+                    }
+                  }
+
+                  if (this.durationSpan.hideByAdjustment) {
+                    this.positionDurationBox.setAttribute("positionOnly", "true");
+                  } else {
+                    this.positionDurationBox.removeAttribute("positionOnly");
+                  }
 
-                        this.controlsOverlay.setAttribute("scaled", true);
-                        this.controlsOverlay.style.width = width + "px";
-                        this.controlsSpacer.style.width = width + "px";
-                        this.controlBar.style.width = width + "px";
-                      } else {
-                        this.controlsOverlay.removeAttribute("scaled");
-                        this.controlsOverlay.style.width = "";
-                        this.controlsSpacer.style.width = "";
-                        this.controlBar.style.width = "";
-                      }
-                    }
+                  if (videoHeight < this.controlBar.minHeight ||
+                      widthUsed === minControlBarPaddingWidth) {
+                    this.controlBar.setAttribute("size", "hidden");
+                    this.controlBar.hideByAdjustment = true;
+                  } else {
+                    this.controlBar.removeAttribute("size");
+                    this.controlBar.hideByAdjustment = false;
+                  }
+
+                  // Use flexible spacer to separate controls when scrubber is hidden.
+                  // As long as muteButton hidden, which means only play button presents,
+                  // hide spacer and make playButton centered.
+                  this.controlBarSpacer.hidden = !this.scrubberStack.hidden || this.muteButton.hidden;
 
-                    if ((this._overlayPlayButtonHeight + this._controlBarHeight) > videoHeight ||
-                        this._overlayPlayButtonWidth > videoWidth) {
-                        this.clickToPlay.hidden = true;
-                    } else if (this.clickToPlay.hidden &&
-                               !this.video.played.length &&
-                               this.video.paused) {
-                        // Check this.video.paused to handle when a video is
-                        // playing but hasn't processed any frames yet
-                        this.clickToPlay.hidden = false;
+                  // Adjust clickToPlayButton size.
+                  const minVideoSideLength = Math.min(videoWidth, videoHeight);
+                  const clickToPlayViewRatio = 0.15;
+                  const clickToPlayScaledSize = Math.max(
+                    this.clickToPlay.minWidth, minVideoSideLength * clickToPlayViewRatio);
+
+                  if (clickToPlayScaledSize >= videoWidth ||
+                    (clickToPlayScaledSize + this.controlBar.minHeight / 2 >= videoHeight / 2 )) {
+
+                    this.clickToPlay.hideByAdjustment = true;
+                  } else {
+                    if (this.clickToPlay.hidden && !this.video.played.length && this.video.paused) {
+                      this.clickToPlay.hideByAdjustment = false;
                     }
-
-                    let size = "normal";
-                    if (videoHeight < minHeightForControlBar)
-                        size = "hidden";
-                    else if (videoWidth < minWidthOnlyPlayPause)
-                        size = "hidden";
-                    else if (videoWidth < minWidthAllControls)
-                        size = "small";
-                    this.controlBar.setAttribute("size", size);
+                    this.clickToPlay.style.width = `${clickToPlayScaledSize}px`;
+                    this.clickToPlay.style.height = `${clickToPlayScaledSize}px`;
+                  }
                 },
 
                 init : function (binding) {
                     this.video = binding.parentNode;
                     this.videocontrols = binding;
 
-                    this.statusIcon    = document.getAnonymousElementByAttribute(binding, "class", "statusIcon");
-                    this.controlBar    = document.getAnonymousElementByAttribute(binding, "class", "controlBar");
-                    this.playButton    = document.getAnonymousElementByAttribute(binding, "class", "playButton");
-                    this.muteButton    = document.getAnonymousElementByAttribute(binding, "class", "muteButton");
-                    this.volumeControl = document.getAnonymousElementByAttribute(binding, "class", "volumeControl");
-                    this.progressBar   = document.getAnonymousElementByAttribute(binding, "class", "progressBar");
-                    this.bufferBar     = document.getAnonymousElementByAttribute(binding, "class", "bufferBar");
-                    this.scrubber      = document.getAnonymousElementByAttribute(binding, "class", "scrubber");
-                    this.scrubberThumb = document.getAnonymousElementByAttribute(this.scrubber, "class", "scale-thumb");
-                    this.durationLabel = document.getAnonymousElementByAttribute(binding, "class", "durationLabel");
-                    this.positionLabel = document.getAnonymousElementByAttribute(binding, "class", "positionLabel");
-                    this.statusOverlay = document.getAnonymousElementByAttribute(binding, "class", "statusOverlay");
-                    this.controlsOverlay = document.getAnonymousElementByAttribute(binding, "class", "controlsOverlay");
-                    this.controlsSpacer     = document.getAnonymousElementByAttribute(binding, "class", "controlsSpacer");
-                    this.clickToPlay        = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay");
-                    this.fullscreenButton   = document.getAnonymousElementByAttribute(binding, "class", "fullscreenButton");
-                    this.volumeForeground   = document.getAnonymousElementByAttribute(binding, "anonid", "volumeForeground");
-                    this.closedCaptionButton = document.getAnonymousElementByAttribute(binding, "class", "closedCaptionButton");
-                    this.textTrackList = document.getAnonymousElementByAttribute(binding, "class", "textTrackList");
+                    this.controlsContainer    = document.getAnonymousElementByAttribute(binding, "anonid", "controlsContainer");
+                    this.statusIcon    = document.getAnonymousElementByAttribute(binding, "anonid", "statusIcon");
+                    this.controlBar    = document.getAnonymousElementByAttribute(binding, "anonid", "controlBar");
+                    this.playButton    = document.getAnonymousElementByAttribute(binding, "anonid", "playButton");
+                    this.controlBarSpacer    = document.getAnonymousElementByAttribute(binding, "anonid", "controlBarSpacer");
+                    this.muteButton    = document.getAnonymousElementByAttribute(binding, "anonid", "muteButton");
+                    this.volumeStack   = document.getAnonymousElementByAttribute(binding, "anonid", "volumeStack");
+                    this.volumeControl = document.getAnonymousElementByAttribute(binding, "anonid", "volumeControl");
+                    this.progressBar   = document.getAnonymousElementByAttribute(binding, "anonid", "progressBar");
+                    this.bufferBar     = document.getAnonymousElementByAttribute(binding, "anonid", "bufferBar");
+                    this.scrubberStack = document.getAnonymousElementByAttribute(binding, "anonid", "scrubberStack");
+                    this.scrubber      = document.getAnonymousElementByAttribute(binding, "anonid", "scrubber");
+                    this.durationLabel = document.getAnonymousElementByAttribute(binding, "anonid", "durationLabel");
+                    this.positionLabel = document.getAnonymousElementByAttribute(binding, "anonid", "positionLabel");
+                    this.positionDurationBox   = document.getAnonymousElementByAttribute(binding, "anonid", "positionDurationBox");
+                    this.statusOverlay = document.getAnonymousElementByAttribute(binding, "anonid", "statusOverlay");
+                    this.controlsOverlay = document.getAnonymousElementByAttribute(binding, "anonid", "controlsOverlay");
+                    this.controlsSpacer     = document.getAnonymousElementByAttribute(binding, "anonid", "controlsSpacer");
+                    this.clickToPlay        = document.getAnonymousElementByAttribute(binding, "anonid", "clickToPlay");
+                    this.fullscreenButton   = document.getAnonymousElementByAttribute(binding, "anonid", "fullscreenButton");
+                    this.closedCaptionButton = document.getAnonymousElementByAttribute(binding, "anonid", "closedCaptionButton");
+                    this.textTrackList = document.getAnonymousElementByAttribute(binding, "anonid", "textTrackList");
 
+                    if (this.positionDurationBox) {
+                      this.durationSpan = this.positionDurationBox.getElementsByTagName("span")[0];
+                    }
+
+                    // XXX controlsContainer is a desktop only element. To determine whether
+                    // isTouchControls or not during the whole initialization process, get
+                    // this state overridden here.
+                    this.videocontrols.isTouchControls = !this.controlsContainer;
                     this.isAudioOnly = (this.video instanceof HTMLAudioElement);
                     this.setupInitialState();
                     this.setupNewLoadState();
                     this.initTextTracks();
 
                     // Use the handleEvent() callback for all media events.
                     // Only the "error" event listener must capture, so that it can trap error
                     // events from <source> children, which don't bubble. But we use capture
@@ -1574,40 +1767,47 @@
 
                     // Helper function to add an event listener to the given element
                     function addListener(elem, eventName, func) {
                       let boundFunc = func.bind(self);
                       self.controlListeners.push({ item: elem, event: eventName, func: boundFunc });
                       elem.addEventListener(eventName, boundFunc, { mozSystemGroup: true });
                     }
 
-                    addListener(this.muteButton, "command", this.toggleMute);
-                    addListener(this.closedCaptionButton, "command", this.toggleClosedCaption);
+                    addListener(this.muteButton, "click", this.toggleMute);
+                    addListener(this.closedCaptionButton, "click", this.toggleClosedCaption);
+                    addListener(this.fullscreenButton, "click", this.toggleFullscreen);
                     addListener(this.playButton, "click", this.clickToPlayClickHandler);
-                    addListener(this.fullscreenButton, "command", this.toggleFullscreen);
                     addListener(this.clickToPlay, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "click", this.clickToPlayClickHandler);
                     addListener(this.controlsSpacer, "dblclick", this.toggleFullscreen);
 
                     addListener(this.videocontrols, "resizevideocontrols", this.adjustControlSize);
                     addListener(this.videocontrols, "transitionend", this.onTransitionEnd);
                     addListener(this.video.ownerDocument, "mozfullscreenchange", this.onFullscreenChange);
-                    addListener(this.videocontrols, "transitionend", this.onControlBarTransitioned);
+                    addListener(this.controlBar, "transitionend", this.onControlBarTransitioned);
                     addListener(this.video.ownerDocument, "fullscreenchange", this.onFullscreenChange);
                     addListener(this.video, "keypress", this.keyHandler);
-                    addListener(this.video.textTracks, "addtrack", this.onTextTrackAdd);
-                    addListener(this.video.textTracks, "removetrack", this.onTextTrackRemove);
 
                     addListener(this.videocontrols, "dragstart", function(event) {
                         event.preventDefault(); // prevent dragging of controls image (bug 517114)
                     });
 
+                    if (!this.videocontrols.isTouchControls) {
+                      addListener(this.scrubber, "input", this.onScrubberInput);
+                      addListener(this.scrubber, "change", this.onScrubberChange);
+                      addListener(this.volumeControl, "input", this.updateVolume);
+                      addListener(this.video.textTracks, "addtrack", this.onTextTrackAdd);
+                      addListener(this.video.textTracks, "removetrack", this.onTextTrackRemove);
+                    }
+
                     this.log("--- videocontrols initialized ---");
                 }
             };
+
             this.Utils.init(this);
             ]]>
         </constructor>
         <destructor>
             <![CDATA[
             this.Utils.terminateEventListeners();
             // randomID used to be a <field>, which meant that the XBL machinery
             // undefined the property when the element was unbound. The code in
@@ -1616,89 +1816,91 @@
             delete this.randomID;
             ]]>
         </destructor>
 
     </implementation>
 
     <handlers>
         <handler event="mouseover">
-            if (!this.isTouchControl)
+            if (!this.isTouchControls)
                 this.Utils.onMouseInOut(event);
         </handler>
         <handler event="mouseout">
-            if (!this.isTouchControl)
+            if (!this.isTouchControls)
                 this.Utils.onMouseInOut(event);
         </handler>
         <handler event="mousemove">
-            if (!this.isTouchControl)
+            if (!this.isTouchControls)
                 this.Utils.onMouseMove(event);
         </handler>
     </handlers>
   </binding>
 
   <binding id="touchControls" extends="chrome://global/content/bindings/videocontrols.xml#videoControls">
 
     <xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" class="mediaControlsFrame">
         <stack flex="1">
-            <vbox flex="1" class="statusOverlay" hidden="true">
-                <box class="statusIcon"/>
+            <vbox anonid="statusOverlay" flex="1" class="statusOverlay" hidden="true">
+                <box anonid="statusIcon" class="statusIcon"/>
                 <label class="errorLabel" anonid="errorAborted">&error.aborted;</label>
                 <label class="errorLabel" anonid="errorNetwork">&error.network;</label>
                 <label class="errorLabel" anonid="errorDecode">&error.decode;</label>
                 <label class="errorLabel" anonid="errorSrcNotSupported">&error.srcNotSupported;</label>
                 <label class="errorLabel" anonid="errorNoSource">&error.noSource2;</label>
                 <label class="errorLabel" anonid="errorGeneric">&error.generic;</label>
             </vbox>
 
-            <vbox class="controlsOverlay">
-                <spacer class="controlsSpacer" flex="1"/>
+            <vbox anonid="controlsOverlay" class="controlsOverlay">
+                <spacer anonid="controlsSpacer" class="controlsSpacer" flex="1"/>
                 <box flex="1" hidden="true">
-                    <box class="clickToPlay" hidden="true" flex="1"/>
-                    <vbox class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
+                    <box anonid="clickToPlay" class="clickToPlay" hidden="true" flex="1"/>
+                    <vbox anonid="textTrackList" class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></vbox>
                 </box>
-                <vbox class="controlBar" hidden="true">
+                <vbox anonid="controlBar" class="controlBar" hidden="true">
                     <hbox class="buttonsBar">
-                        <button class="playButton"
+                        <button anonid="playButton"
+                                class="playButton"
                                 playlabel="&playButton.playLabel;"
                                 pauselabel="&playButton.pauseLabel;"/>
-                        <label class="positionLabel" role="presentation"/>
-                        <stack class="scrubberStack">
+                        <label anonid="positionLabel" class="positionLabel" role="presentation"/>
+                        <stack anonid="scrubberStack" class="scrubberStack">
                             <box class="backgroundBar"/>
                             <progressmeter class="flexibleBar" value="100"/>
-                            <progressmeter class="bufferBar"/>
-                            <progressmeter class="progressBar" max="10000"/>
-                            <scale class="scrubber" movetoclick="true"/>
+                            <progressmeter anonid="bufferBar" class="bufferBar"/>
+                            <progressmeter anonid="progressBar" class="progressBar" max="10000"/>
+                            <scale anonid="scrubber" class="scrubber" movetoclick="true"/>
                         </stack>
-                        <label class="durationLabel" role="presentation"/>
-                        <button class="muteButton"
+                        <label anonid="durationLabel" class="durationLabel" role="presentation"/>
+                        <button anonid="muteButton"
+                                class="muteButton"
                                 mutelabel="&muteButton.muteLabel;"
                                 unmutelabel="&muteButton.unmuteLabel;"/>
-                        <stack class="volumeStack">
-                          <box class="volumeBackground"/>
-                          <box class="volumeForeground" anonid="volumeForeground"/>
-                          <scale class="volumeControl" movetoclick="true"/>
+                        <stack anonid="volumeStack" class="volumeStack">
+                          <box anonid="volumeBackground" class="volumeBackground"/>
+                          <box anonid="volumeForeground" class="volumeForeground"/>
+                          <scale anonid="volumeControl" class="volumeControl" movetoclick="true"/>
                         </stack>
-                        <button class="castingButton" hidden="true"
+                        <button anonid="castingButton" class="castingButton" hidden="true"
                                 aria-label="&castingButton.castingLabel;"/>
-                        <button class="closedCaptionButton" hidden="true"/>
-                        <button class="fullscreenButton"
+                        <button anonid="closedCaptionButton" class="closedCaptionButton" hidden="true"/>
+                        <button anonid="fullscreenButton"
+                            class="fullscreenButton"
                             enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
                             exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
                     </hbox>
                 </vbox>
             </vbox>
         </stack>
     </xbl:content>
 
     <implementation>
-
         <constructor>
           <![CDATA[
-          this.isTouchControl = true;
+          this.isTouchControls = true;
           this.TouchUtils = {
             videocontrols: null,
             video: null,
             controlsTimer: null,
             controlsTimeout: 5000,
             positionLabel: null,
             castingButton: null,
 
@@ -1823,17 +2025,17 @@
               this.Utils.scrubber.addEventListener("touchstart", function() {
                 self.clearTimer();
               }, false);
               this.Utils.scrubber.addEventListener("touchend", function() {
                 self.delayHideControls(self.controlsTimeout);
               }, false);
               this.Utils.muteButton.addEventListener("click", function() { self.delayHideControls(self.controlsTimeout); }, false);
 
-              this.castingButton = document.getAnonymousElementByAttribute(binding, "class", "castingButton");
+              this.castingButton = document.getAnonymousElementByAttribute(binding, "anonid", "castingButton");
               this.castingButton.addEventListener("command", function() {
                 self.startCasting();
               }, false);
 
               this.video.addEventListener("media-videoCasting", function (e) {
                 if (!e.isTrusted)
                   return;
                 self.updateCasting(e.detail);
@@ -1853,17 +2055,18 @@
               // transitioned into or out of fullscreen mode, and we don't want
               // the controls to remain visible. this.controlsTimeout is a full
               // 5s, which feels too long after the transition.
               if (this.video.currentTime !== 0) {
                 this.delayHideControls(this.Utils.HIDE_CONTROLS_TIMEOUT_MS);
               }
             }
           };
-          this.TouchUtils.init(this);
+
+          this.TouchUtils.init(this)
           this.dispatchEvent(new CustomEvent("VideoBindingAttached"));
       ]]>
       </constructor>
       <destructor>
           <![CDATA[
           // XBL destructors don't appear to be inherited properly, so we need
           // to do this here in addition to the videoControls destructor. :-(
           delete this.randomID;
--- a/toolkit/locales/en-US/chrome/global/videocontrols.dtd
+++ b/toolkit/locales/en-US/chrome/global/videocontrols.dtd
@@ -32,8 +32,18 @@
 <!ENTITY error.generic "Video playback aborted due to an unknown error.">
 
 <!-- LOCALIZATION NOTE (scrubberScale.nameFormat): the #1 string is the current
 media position, and the #2 string is the total duration. For example, when at
 the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be
 "6:00:00", result string would be "5:00 of 6:00:00 elapsed".
 -->
 <!ENTITY scrubberScale.nameFormat "#1 of #2 elapsed">
+
+<!-- LOCALIZATION NOTE (positionAndDuration.nameFormat): the #1 string is the current
+media position, and the #2 string is the total duration. For example, when at
+the 5 minute mark in a 6 hour long video, #1 would be "5:00" and #2 would be
+"6:00:00", result string would be "5:00 / 6:00:00".
+Note that #2 is not always avaiable. For example, when at the 5 minute mark in an
+unknown duration video, #1 would be "5:00" and string which is surrounded by <span>
+would be deleted, result string would be "5:00".
+-->
+<!ENTITY positionAndDuration.nameFormat "#1<span> / #2</span>">
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -59,41 +59,24 @@ toolkit.jar:
   skin/classic/global/reader/RM-Content-Width-Plus-44x16.svg             (../../shared/reader/RM-Content-Width-Plus-44x16.svg)
   skin/classic/global/reader/RM-Line-Height-Minus-38x14.svg            (../../shared/reader/RM-Line-Height-Minus-38x14.svg)
   skin/classic/global/reader/RM-Line-Height-Plus-38x24.svg             (../../shared/reader/RM-Line-Height-Plus-38x24.svg)
   skin/classic/global/media/TopLevelImageDocument.css      (../../shared/media/TopLevelImageDocument.css)
   skin/classic/global/media/TopLevelVideoDocument.css      (../../shared/media/TopLevelVideoDocument.css)
   skin/classic/global/media/imagedoc-lightnoise.png        (../../shared/media/imagedoc-lightnoise.png)
   skin/classic/global/media/imagedoc-darknoise.png         (../../shared/media/imagedoc-darknoise.png)
 * skin/classic/global/media/videocontrols.css              (../../shared/media/videocontrols.css)
-  skin/classic/global/media/pauseButton.png                (../../shared/media/pauseButton.png)
-  skin/classic/global/media/pauseButton@2x.png             (../../shared/media/pauseButton@2x.png)
-  skin/classic/global/media/playButton.png                 (../../shared/media/playButton.png)
-  skin/classic/global/media/playButton@2x.png              (../../shared/media/playButton@2x.png)
-  skin/classic/global/media/muteButton.png                 (../../shared/media/muteButton.png)
-  skin/classic/global/media/muteButton@2x.png              (../../shared/media/muteButton@2x.png)
-  skin/classic/global/media/unmuteButton.png               (../../shared/media/unmuteButton.png)
-  skin/classic/global/media/unmuteButton@2x.png            (../../shared/media/unmuteButton@2x.png)
-  skin/classic/global/media/noAudio.png                    (../../shared/media/noAudio.png)
-  skin/classic/global/media/noAudio@2x.png                 (../../shared/media/noAudio@2x.png)
-  skin/classic/global/media/closeCaptionButton.png         (../../shared/media/closeCaptionButton.png)
-  skin/classic/global/media/closeCaptionButton@2x.png      (../../shared/media/closeCaptionButton@2x.png)
-  skin/classic/global/media/fullscreenButton.png           (../../shared/media/fullscreenButton.png)
-  skin/classic/global/media/fullscreenButton@2x.png        (../../shared/media/fullscreenButton@2x.png)
-  skin/classic/global/media/scrubberThumb.png              (../../shared/media/scrubberThumb.png)
-  skin/classic/global/media/scrubberThumb@2x.png           (../../shared/media/scrubberThumb@2x.png)
-  skin/classic/global/media/scrubberThumbWide.png          (../../shared/media/scrubberThumbWide.png)
-  skin/classic/global/media/scrubberThumbWide@2x.png       (../../shared/media/scrubberThumbWide@2x.png)
+  skin/classic/global/media/pauseButton.svg                (../../shared/media/pauseButton.svg)
+  skin/classic/global/media/playButton.svg                 (../../shared/media/playButton.svg)
+  skin/classic/global/media/muteButton.svg                 (../../shared/media/muteButton.svg)
+  skin/classic/global/media/closedCaptionButton.svg        (../../shared/media/closedCaptionButton.svg)
+  skin/classic/global/media/fullscreenButton.svg           (../../shared/media/fullscreenButton.svg)
   skin/classic/global/media/error.png                      (../../shared/media/error.png)
   skin/classic/global/media/throbber.png                   (../../shared/media/throbber.png)
   skin/classic/global/media/stalled.png                    (../../shared/media/stalled.png)
-  skin/classic/global/media/volume-empty.png               (../../shared/media/volume-empty.png)
-  skin/classic/global/media/volume-empty@2x.png            (../../shared/media/volume-empty@2x.png)
-  skin/classic/global/media/volume-full.png                (../../shared/media/volume-full.png)
-  skin/classic/global/media/volume-full@2x.png             (../../shared/media/volume-full@2x.png)
   skin/classic/global/media/clicktoplay-bgtexture.png      (../../shared/media/clicktoplay-bgtexture.png)
   skin/classic/global/media/videoClickToPlayButton.svg     (../../shared/media/videoClickToPlayButton.svg)
 #ifdef MOZ_PLACES
   skin/classic/mozapps/places/defaultFavicon.png           (../../shared/places/defaultFavicon.png)
   skin/classic/mozapps/places/defaultFavicon@2x.png        (../../shared/places/defaultFavicon@2x.png)
   skin/classic/mozapps/places/defaultFavicon-inverted.png  (../../shared/places/defaultFavicon-inverted.png)
   skin/classic/mozapps/places/defaultFavicon-inverted@2x.png (../../shared/places/defaultFavicon-inverted@2x.png)
 #endif
--- a/toolkit/themes/shared/media/TopLevelVideoDocument.css
+++ b/toolkit/themes/shared/media/TopLevelVideoDocument.css
@@ -3,10 +3,11 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 body {
   background-image: url("chrome://global/skin/media/imagedoc-darknoise.png");
   background-color: rgb(33,33,33); /* Average color of that ^ image. */
 }
 
 video {
-  box-shadow: 0 0 15px #000;
+  border: 1px #000000 solid;
+  box-shadow: 0 0 5px rgba(0,0,0,0.6);
 }
deleted file mode 100644
index 469310fb1b21ed705925decd18710dd0df37d915..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 03350789221b94d830ebc4dafb1b6e4447f79f71..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/closedCaptionButton.svg
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #ffffff;
+    }
+    use[id$="-hover"] {
+      fill: #48a0f7;
+    }
+    use[id$="-active"] {
+      fill: #2d89e6;
+    }
+    use[id$="-focus"] {
+      fill: #48a0f7;
+    }
+    use[id$="-disabled"] {
+      fill: #ffffff;
+    }
+  </style>
+  <symbol id="cc-off-shape">
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M16.531,16.107H5.267l1.982-2H15c0.6,0,1-0.4,1-1V5.274
+      l1.946-1.964C17.963,3.399,18,3.483,18,3.576v11.031C18,15.407,17.331,16.107,16.531,16.107z M14.016,8.506h-1.218l1.005-1.014
+      C13.913,7.789,13.984,8.128,14.016,8.506z M11.786,12.361c-0.828,0-1.476-0.326-1.913-0.902l1.09-1.101
+      c0.136,0.323,0.374,0.541,0.796,0.541c0.514,0,0.695-0.44,0.756-1.014h1.535C13.908,11.43,13.071,12.361,11.786,12.361z
+       M1.496,16.106C0.697,16.104,0,15.406,0,14.607V3.576c0-0.8,0.7-1.5,1.5-1.5h12.846L16.299,0l1.316,1.283L2.615,17.13L1.496,16.106
+      z M3,4.107c-0.6,0-1,0.4-1,1v8c0,0.6,0.4,1,1,1h0.029l2.031-2.16c-0.757-0.503-1.191-1.457-1.191-2.744
+      c0-1.936,1.069-3.14,2.428-3.14c1.357,0,2.136,0.76,2.361,2.059l3.777-4.016H3z M8.298,8.506H7.355
+      c-0.047-0.623-0.49-1.23-0.99-1.23c-0.561,0-1.337,0.84-1.337,1.995c0,0.674,0.381,1.427,0.95,1.702L8.298,8.506z"/>
+  </symbol>
+
+  <symbol id="cc-shape">
+    <path d="M16.531,1.984H1.5c-0.8,0-1.5,0.7-1.5,1.5v11.031c0,0.8,0.7,1.5,1.5,1.5h15.031
+      c0.8,0,1.469-0.7,1.469-1.5V3.484C18,2.684,17.331,1.984,16.531,1.984z M16,13.016c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1v-8
+      c0-0.6,0.4-1,1-1h12c0.6,0,1,0.4,1,1V13.016z M6.426,10.807c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745
+      c0.5,0,0.818,0.357,0.866,0.98h1.484C8.585,6.877,7.785,5.972,6.297,5.972c-1.359,0-2.428,1.205-2.428,3.14
+      c0,1.944,0.974,3.157,2.583,3.157c1.285,0,2.153-0.93,2.295-2.476H7.244C7.183,10.367,6.94,10.807,6.426,10.807z M11.759,10.807
+      c-0.811,0-0.96-0.789-0.96-1.628c0-1.155,0.338-1.745,0.899-1.745c0.5,0,0.756,0.357,0.803,0.98h1.515
+      c-0.129-1.537-0.898-2.443-2.385-2.443c-1.359,0-2.396,1.205-2.396,3.14c0,1.944,0.943,3.157,2.552,3.157
+      c1.285,0,2.122-0.93,2.264-2.476h-1.535C12.454,10.367,12.273,10.807,11.759,10.807z"/>
+  </symbol>
+  <use id="cc" xlink:href="#cc-shape"/>
+  <use id="cc-hover" xlink:href="#cc-shape"/>
+  <use id="cc-active" xlink:href="#cc-shape"/>
+  <use id="cc-focus" xlink:href="#cc-shape"/>
+  <use id="cc-disabled" xlink:href="#cc-shape"/>
+
+  <use id="cc-off" xlink:href="#cc-off-shape"/>
+  <use id="cc-off-hover" xlink:href="#cc-off-shape"/>
+  <use id="cc-off-active" xlink:href="#cc-off-shape"/>
+  <use id="cc-off-focus" xlink:href="#cc-off-shape"/>
+  <use id="cc-off-disabled" xlink:href="#cc-off-shape"/>
+</svg>
index 58e37283a73eff086420d0969544fa8bc5dd7924..362a293f223fe1c29751b711de8c48de950ebd49
GIT binary patch
literal 20345
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
D)qq~X
deleted file mode 100644
index ffbc3d5ae4627d9beb7c4e8c6ef80542e7e1414c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/fullscreenButton.svg
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #ffffff;
+    }
+    use[id$="-hover"] {
+      fill: #48a0f7;
+    }
+    use[id$="-active"] {
+      fill: #2d89e6;
+    }
+    use[id$="-focus"] {
+      fill: #48a0f7;
+    }
+    use[id$="-disabled"] {
+      fill: #ffffff;
+    }
+  </style>
+  <symbol id="fullscreen-shape">
+    <path d="M6.728,10.188l-3.235,3.094l0.017-2.267l-1.513-0.016l0,5l4.987-0.008l0.011-1.537l-2.281-0.022
+  l3.097-3.158L6.728,10.188z M14.453,11.004l-0.022,2.281l-3.158-3.097l-1.086,1.083l3.094,3.235l-2.267-0.017l-0.016,1.514l5,0
+  l-0.008-4.988L14.453,11.004z M11.015,2.01l-0.011,1.537l2.281,0.022l-3.097,3.158l1.083,1.086l3.235-3.094L14.49,6.986
+  l1.513,0.016v-5L11.015,2.01z M6.986,3.511l0.016-1.514l-5,0L2.01,6.985l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+  L4.718,3.494L6.986,3.511z"/>
+  </symbol>
+  <symbol id="unfullscreen-shape">
+    <path d="M2.047,11.135l-0.011,1.537l2.281,0.022L1.22,15.851l1.083,1.086l3.235-3.094l-0.017,2.268l1.513,0.016
+  l0-5L2.047,11.135z M13.781,12.587l2.267,0.017l0.016-1.514l-5,0l0.008,4.988l1.537,0.011l0.022-2.281l3.158,3.097l1.086-1.083
+  L13.781,12.587z M16.058,5.578l-2.281-0.021l3.097-3.158l-1.083-1.086l-3.235,3.094l0.017-2.267L11.06,2.123v5l4.988-0.008
+  L16.058,5.578z M5.516,2.098L5.494,4.379L2.336,1.283L1.25,2.365L4.344,5.6L2.077,5.583L2.06,7.097l5,0L7.053,2.109L5.516,2.098z"/>
+  </symbol>
+  <use id="fullscreen" xlink:href="#fullscreen-shape"/>
+  <use id="fullscreen-hover" xlink:href="#fullscreen-shape"/>
+  <use id="fullscreen-active" xlink:href="#fullscreen-shape"/>
+  <use id="fullscreen-focus" xlink:href="#fullscreen-shape"/>
+  <use id="fullscreen-disabled" xlink:href="#fullscreen-shape"/>
+
+  <use id="unfullscreen" xlink:href="#unfullscreen-shape"/>
+  <use id="unfullscreen-hover" xlink:href="#unfullscreen-shape"/>
+  <use id="unfullscreen-active" xlink:href="#unfullscreen-shape"/>
+  <use id="unfullscreen-focus" xlink:href="#unfullscreen-shape"/>
+  <use id="unfullscreen-disabled" xlink:href="#unfullscreen-shape"/>
+</svg>
deleted file mode 100644
index b09ebbd43ce8dd62741992a0d00df1018897d164..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 894480761d0742015ffcfcc2d28bc10e88cbe4a9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/muteButton.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #ffffff;
+    }
+    use[id$="-hover"] {
+      fill: #48a0f7;
+    }
+    use[id$="-active"] {
+      fill: #2d89e6;
+    }
+    use[id$="-focus"] {
+      fill: #48a0f7;
+    }
+    use[id$="-disabled"] {
+      fill: #ffffff;
+    }
+  </style>
+  <symbol id="unmute-shape">
+    <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+    V1.735L5.035,5.367H3.52z M12.059,9c0-0.727-0.484-1.211-1.211-1.211v2.422C11.574,10.211,12.059,9.727,12.059,9z M14.48,9
+    c0-1.695-1.211-3.148-2.785-3.512l-0.363,1.09C12.422,6.82,13.27,7.789,13.27,9c0,1.211-0.848,2.18-1.938,2.422l0.484,1.09
+    C13.27,12.148,14.48,10.695,14.48,9z M12.543,3.188l-0.484,1.09C14.238,4.883,15.691,6.82,15.691,9c0,2.18-1.453,4.117-3.512,4.601
+    l0.484,1.09c2.422-0.605,4.238-2.906,4.238-5.691C16.902,6.215,15.086,3.914,12.543,3.188z"/>
+  </symbol>
+  <symbol id="mute-shape">
+    <path d="M3.52,5.367c-1.332,0-2.422,1.09-2.422,2.422v2.422c0,1.332,1.09,2.422,2.422,2.422h1.516l4.102,3.633
+      V1.735L5.035,5.367H3.52z"/>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M12.155,12.066l-1.138-1.138l4.872-4.872l1.138,1.138
+      L12.155,12.066z"/>
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M10.998,7.204l1.138-1.138l4.872,4.872l-1.138,1.138L10.998,7.204
+		z"/>
+  </symbol>
+  <symbol id="noaudio-shape">
+	  <path d="M14.901,3.571l-4.412,3.422V1.919L6.286,5.46H4.869c-1.298,0-2.36,1.062-2.36,2.36v2.36
+		c0,1.062,0.708,1.888,1.652,2.242l-2.242,1.77l1.18,1.416L16.081,4.987L14.901,3.571z M10.489,16.081V11.36l-2.669,2.36
+		L10.489,16.081z"/>
+  </symbol>
+  <use id="unmute" xlink:href="#unmute-shape"/>
+  <use id="unmute-hover" xlink:href="#unmute-shape"/>
+  <use id="unmute-active" xlink:href="#unmute-shape"/>
+  <use id="unmute-focus" xlink:href="#unmute-shape"/>
+  <use id="unmute-disabled" xlink:href="#unmute-shape"/>
+
+  <use id="mute" xlink:href="#mute-shape"/>
+  <use id="mute-hover" xlink:href="#mute-shape"/>
+  <use id="mute-active" xlink:href="#mute-shape"/>
+  <use id="mute-focus" xlink:href="#mute-shape"/>
+  <use id="mute-disabled" xlink:href="#mute-shape"/>
+
+  <use id="noaudio" xlink:href="#noaudio-shape"/>
+</svg>
deleted file mode 100644
index b2cd21c5ebc271dbe97d72207827cb48e0b0fa05..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3db8c973b6594cb8820661564eb8dacb673c09b9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 7de728b2d7a17a75cc0e5b44da308e7a9e8db013..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 944098ca19a4f63331ae852606c3907285e7e203..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/pauseButton.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #ffffff;
+    }
+    use[id$="-hover"] {
+      fill: #48a0f7;
+    }
+    use[id$="-active"] {
+      fill: #2d89e6;
+    }
+    use[id$="-focus"] {
+      fill: #48a0f7;
+    }
+    use[id$="-disabled"] {
+      fill: #ffffff;
+    }
+  </style>
+
+
+  <symbol id="pause-shape">
+    <path fill-rule="evenodd" clip-rule="evenodd" d="M6.002,1.953C5.172,1.953,4.5,2.626,4.5,3.455v11.08
+        c0,0.83,0.672,1.502,1.502,1.502c0.829,0,1.502-0.672,1.502-1.502V3.455C7.504,2.626,6.831,1.953,6.002,1.953z M12,1.953
+        c-0.828,0-1.5,0.672-1.5,1.5v11.094c0,0.828,0.672,1.5,1.5,1.5s1.5-0.672,1.5-1.5V3.453C13.5,2.625,12.828,1.953,12,1.953z"/>
+  </symbol>
+
+  <use id="pause" xlink:href="#pause-shape"/>
+  <use id="pause-hover" xlink:href="#pause-shape"/>
+  <use id="pause-active" xlink:href="#pause-shape"/>
+  <use id="pause-focus" xlink:href="#pause-shape"/>
+  <use id="pause-disalbed" xlink:href="#pause-shape"/>
+</svg>
deleted file mode 100644
index df22919419082d251064811ac730eea234008ad6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 11e2731df64f9dd00623f263150d8a651ee1b1bc..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/media/playButton.svg
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="18px" height="18px" viewBox="0 0 18 18">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: #ffffff;
+    }
+    use[id$="-hover"] {
+      fill: #48a0f7;
+    }
+    use[id$="-active"] {
+      fill: #2d89e6;
+    }
+    use[id$="-focus"] {
+      fill: #48a0f7;
+    }
+    use[id$="-disabled"] {
+      fill: #ffffff;
+    }
+    use[id$="-clicktoplay"] {
+      fill: #000000;
+    }
+  </style>
+
+  <symbol id="play-shape">
+    <path d="M3.243,15.155c0,0.845,0.593,1.157,1.317,0.707l9.659-6.041c0.727-0.453,0.722-1.193,0-1.645L4.556,2.137
+    C3.827,1.682,3.237,2.014,3.237,2.844v12.312H3.243z"/>
+  </symbol>
+
+  <use id="play" xlink:href="#play-shape"/>
+  <use id="play-hover" xlink:href="#play-shape"/>
+  <use id="play-active" xlink:href="#play-shape"/>
+  <use id="play-focus" xlink:href="#play-shape"/>
+  <use id="play-clicktoplay" xlink:href="#play-shape"/>
+</svg>
deleted file mode 100644
index fb20075b24af20c17d2ab0f301e04b74c0fbdb50..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f159627631c536f0def829f45f3715f198db1a5e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f8790f46724c449f053f490ff840f279da03964b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 96d2ed75140584a92940e65b4f5d1f7a60a133c3..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 52c9d72727e8d6c263c288454003b06cb2e819b9..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5b76e2fa45d8481ca0a81e524c4a20508adfc284..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 86f21859eee91db51bab2e89ce0b05f2378f2dd3..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/shared/media/videocontrols.css
+++ b/toolkit/themes/shared/media/videocontrols.css
@@ -1,440 +1,487 @@
 /* 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/. */
 
-@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
-@namespace html url("http://www.w3.org/1999/xhtml");
+@namespace xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace url("http://www.w3.org/1999/xhtml");
+
+
+video > xul|videocontrols,
+audio > xul|videocontrols {
+  writing-mode: horizontal-tb;
+  width: 100%;
+  height: 100%;
+  display: inline-block;
+}
+
+.controlsContainer [hidden="true"],
+.controlBar[hidden] {
+  display: none;
+}
+
+.controlBar[size="hidden"] {
+  display: none;
+}
+
+.controlsContainer,
+.progressContainer {
+  position: relative;
+  height: 100%;
+}
+
+.stackItem {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.statusOverlay {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  background-color: rgb(80,80,80);
+}
+
+.controlsOverlay {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  position: relative;
+}
+
+.controlsSpacerStack {
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+  justify-content: center;
+  align-items: center;
+}
+
+.controlsSpacer {
+  background-color: rgba(255,255,255,.4);
+}
 
 .controlBar {
-  height: 28px;
-  background-color: rgba(35,31,32,.74);
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  overflow: hidden;
+  height: 40px;
+  padding: 0 9px;
+  background-color: rgba(26,26,26,.8);
 }
 
 .playButton,
 .muteButton,
 .closedCaptionButton,
 .fullscreenButton {
+  height: 100%;
+  min-height: 30px;
+  min-width: 30px;
+  padding: 6px;
+  border: 0;
+  margin: 0;
   background-color: transparent;
   background-repeat: no-repeat;
   background-position: center;
-  -moz-appearance: none;   /* Remove the native button appearance and styling */
-  margin: 0;
-  padding: 0;
-  min-height: 28px;
-  min-width: 28px;
-  border: none;
-  opacity: 0.7;
-}
-
-.playButton:hover,
-.muteButton:hover,
-.closedCaptionButton:hover,
-.fullscreenButton:hover {
-  opacity: 1;
-}
-
-.playButton:hover:active,
-.muteButton:hover:active,
-.closedCaptionButton:hover:active,
-.fullscreenButton:hover:active {
-  opacity: 0.4;
+  background-origin: content-box;
+  background-clip: content-box;
 }
 
 .playButton {
-  background-image: url(chrome://global/skin/media/pauseButton.png);
-  margin-right: -22px; /* 1/2 of scrubber thumb width, for overhang. */
-  position: relative; /* Trick to work around negative margin interfering with clicking on the button. */
+  background-image: url(chrome://global/skin/media/pauseButton.svg#pause);
+}
+.playButton:hover {
+  background-image: url(chrome://global/skin/media/pauseButton.svg#pause-hover);
+}
+.playButton:hover:active {
+  background-image: url(chrome://global/skin/media/pauseButton.svg#pause-active);
 }
-
 .playButton[paused] {
-  background-image: url(chrome://global/skin/media/playButton.png);
+  background-image: url(chrome://global/skin/media/playButton.svg#play);
+}
+.playButton[paused]:hover {
+  background-image: url(chrome://global/skin/media/playButton.svg#play-hover);
+}
+.playButton[paused]:hover:active {
+  background-image: url(chrome://global/skin/media/playButton.svg#play-active);
 }
 
 .muteButton {
-  background-image: url(chrome://global/skin/media/muteButton.png);
-  min-width: 33px;
+  background-image: url(chrome://global/skin/media/muteButton.svg#unmute);
+}
+.muteButton:hover {
+  background-image: url(chrome://global/skin/media/muteButton.svg#unmute-hover);
+}
+.muteButton:hover:active {
+  background-image: url(chrome://global/skin/media/muteButton.svg#unmute-active);
 }
 .muteButton[muted] {
-  background-image: url(chrome://global/skin/media/unmuteButton.png);
+  background-image: url(chrome://global/skin/media/muteButton.svg#mute);
+}
+.muteButton[muted]:hover {
+  background-image: url(chrome://global/skin/media/muteButton.svg#mute-hover);
 }
-
-.muteButton[noAudio] {
-  background-image: url(chrome://global/skin/media/noAudio.png);
+.muteButton[muted]:hover:active {
+  background-image: url(chrome://global/skin/media/muteButton.svg#mute-active);
 }
-
+.muteButton[noAudio],
+.muteButton[noAudio]:hover,
+.muteButton[noAudio]:hover:active {
+  background-image: url(chrome://global/skin/media/muteButton.svg#noaudio);
+}
 .muteButton[noAudio] + .volumeStack {
   display: none;
 }
 
 .closedCaptionButton {
-  background-image: url(chrome://global/skin/media/closeCaptionButton.png);
-  background-position: 4px;
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off);
 }
-
+.closedCaptionButton:hover {
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-hover);
+}
+.closedCaptionButton:hover:active {
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-off-active);
+}
 .closedCaptionButton[enabled] {
-  opacity: 1;
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc);
 }
-
-.closedCaptionButton[hidden] {
-  display: none;
+.closedCaptionButton[enabled]:hover {
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-hover);
+}
+.closedCaptionButton[enabled]:hover:active {
+  background-image: url(chrome://global/skin/media/closedCaptionButton.svg#cc-active);
 }
 
 .fullscreenButton {
-  background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 16, 16, 0);
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen);
+}
+.fullscreenButton:hover {
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-hover);
+}
+.fullscreenButton:hover:active {
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#fullscreen-active);
+}
+.fullscreenButton[fullscreened] {
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen);
+}
+.fullscreenButton[fullscreened]:hover {
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-hover);
+}
+.fullscreenButton[fullscreened]:hover:active {
+  background-image: url(chrome://global/skin/media/fullscreenButton.svg#unfullscreen-active);
+}
+
+.controlBarSpacer {
+  flex-grow: 1;
+}
+
+.volumeControl::-moz-range-thumb,
+.scrubber::-moz-range-thumb {
+  height: 13px;
+  width: 13px;
+  border: none;
+  border-radius: 50%;
+  background-color: #ffffff;
+  filter: drop-shadow(0px 0px 5px rgba(0,0,0,0.65));
 }
 
-.fullscreenButton[fullscreened] {
-  background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton.png"), 0, 32, 16, 16);
+.volumeControl::-moz-focus-outer,
+.scrubber::-moz-focus-outer {
+  border: 0;
+}
+
+.progressBackgroundBar {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
 }
 
-.volumeControl {
-  width: 32px;
-  opacity: 0;
+.progressStack {
+  position: relative;
+  width: 100%;
+  height: 5px;
+}
+
+.scrubberStack {
+  min-width: 48px;
+  flex-basis: 48px;
+  flex-grow: 2;
+  flex-shrink: 0;
+  margin: 0 9px;
+}
+
+.volumeStack {
+  max-width: 60px;
+  min-width: 48px;
+  flex-grow: 1;
+  flex-shrink: 0;
+  margin-right: 6px;
+  margin-left: 4px;
 }
 
+.bufferBar,
+.progressBar,
+.scrubber,
 .volumeBackground,
-.volumeForeground {
-  background-repeat: no-repeat;
-  background-position: center;
-  width: 32px;
+.volumeControl {
+  bottom: 0;
+  left: 0;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+  border: 0;
+  border-radius: 2.5px;
+  margin: 0;
+  background: none;
+  background-color: transparent;
+}
+
+.bufferBar,
+.volumeBackground {
+  background-color: rgba(0,0,0,0.7);
+}
+
+.bufferBar::-moz-progress-bar,
+.progressBar::-moz-progress-bar,
+.volumeBackground::-moz-meter-bar {
+  height: 100%;
+  padding: 0;
+  margin: 0;
+  border: 0;
+  border-radius: 2.5px;
+  background: none;
 }
 
-.volumeBackground {
-  background-image: url(chrome://global/skin/media/volume-empty.png);
+.scrubber::-moz-range-thumb,
+.volumeControl::-moz-range-thumb {
+  width: 13px;
+  height: 13px;
+  background-color: #ffffff;
+  filter: drop-shadow(5px #000000)
+}
+
+.scrubber:hover::-moz-range-thumb,
+.volumeControl:hover::-moz-range-thumb {
+  background-color: #48a0f7;
+}
+
+.scrubber:active::-moz-range-thumb,
+.volumeControl:active::-moz-range-thumb {
+  background-color: #2d89e6;
+}
+
+.scrubber::-moz-range-track,
+.scrubber::-moz-range-progress {
+  background-color: transparent;
 }
 
-.volumeForeground {
-  background-image: url(chrome://global/skin/media/volume-full.png);
-  background-clip: content-box;
+.volumeControl::-moz-range-progress,
+.volumeControl::-moz-range-track {
+  height: 5px;
+  border-radius: 2.5px;
+}
+
+.volumeControl::-moz-range-progress {
+  background-color: #ffffff;
+}
+
+.volumeControl::-moz-range-track {
+  background-color: rgba(0,0,0,0.7);
+}
+
+
+.bufferBar::-moz-progress-bar {
+  background-color: rgba(255,255,255,0.3);
+  border-radius: 2.5px;
+}
+
+.progressBar::-moz-progress-bar {
+  background-color: #00b6f0;
 }
 
 .textTrackList {
-  display: -moz-box;
-  -moz-appearance: none;
-  -moz-box-pack: end;
-  -moz-box-align: end;
-  padding: 0;
+  position: absolute;
+  right: 5px;
+  bottom: 45px;
+  max-width: 80%;
+  border: 1px solid #000000;
+  border-radius: 2.5px;
+  padding: 5px 0;
+  vertical-align: middle;
+  font-size: 12px;
+  background-color: #000000;
+  opacity: 0.7;
 }
 
-.textTrackList[hidden] {
+.textTrackList > .textTrackItem {
+  display: block;
+  width: 100%;
+  height: 30px;
+  padding: 2px 10px;
+  border: none;
+  margin: 0;
+  white-space: nowrap;
+  overflow: hidden;
+  text-align: left;
+  text-overflow: ellipsis;
+  color: #ffffff;
+  background-color: transparent;
+}
+
+.textTrackList > .textTrackItem:hover {
+  background-color: #444444;
+}
+
+.textTrackList > .textTrackItem[on] {
+  color: #00b6f0;
+}
+
+.positionLabel,
+.durationLabel {
   display: none;
 }
 
-.textTrackList > html|*.textTrackItem {
-  -moz-appearance: none;
-  -moz-box-align: start;
-  text-align: start;
-  overflow: hidden;
-  margin: 0;
-  padding: 2px 10px;
-  -moz-margin-end: 10px;
-  border: none;
-  color: rgba(255,255,255,.5);
-  background-color: rgba(35,31,32,.74);
+.positionDurationBox {
+  min-width: 9ch;
+  text-align: center;
+  padding-inline-start: 1px;
+  padding-inline-end: 9px;
   white-space: nowrap;
-}
-
-.textTrackList > html|*.textTrackItem[on] {
-  color: white;
-  background-color: black;
+  font: message-box;
+  font-size: 13px;
+  font-size-adjust: 0.6;
+  color: #ffffff;
 }
 
-.textTrackList > html|*.textTrackItem:hover {
-  background-color: rgba(0,0,0,.55);
-}
-
-.controlBar[fullscreen-unavailable] {
-  /* This value is duplicated in the videocontrols.xml adjustControlSize function. */
-  padding-inline-end: 8px;
-}
-
-.volumeControl .scale-thumb {
-  min-width: 0;
-  opacity: 0;
-}
-
-.durationBox {
-  -moz-box-pack: center;
-}
-
-.durationLabel {
-  margin-left: -22px; /* 1/2 of scrubber thumb width, for overhang. */
-  padding-left: 8px; /* don't bump into the scrubber bar */
-  color: rgba(255,255,255,.75);
-  font: message-box;
-  font-size: 11px;
+.positionDurationBox[positionOnly] {
+  min-width: 4ch;
 }
 
 %ifdef XP_MACOSX
-.durationLabel {
-  padding-top: 2px; /* center vertically with scrubber bar */
-}
-%else
-.durationLabel {
-  padding-top: 0; /* center vertically with scrubber bar */
+.positionDurationBox {
+  font-size-adjust: unset;
 }
 %endif
 
-.positionLabel {
-  display: none;
-}
-
-.backgroundBar {
-  /* margin top/bottom: make bar 8px tall (control height = 28, minus 2 * 10 margin) */
-  /* margin left/right: 1/2 of scrubber thumb width, for overhang. */
-  margin: 10px 22px;
-  background-color: rgba(255,255,255,.5);
-  border-radius: 2.5px;
-}
-
-.bufferBar,
-.progressBar {
-  /* margin top/bottom: make bar 8px tall (control height = 28, minus 2 * 10 margin) */
-  /* margin left/right: 1/2 of scrubber thumb width, for overhang. */
-  margin: 10px 22px;
-  -moz-appearance: none;
-  border: none;
-  background-color: transparent;
-  min-width: 0;
-  min-height: 0;
-}
-
-/* .progress-bar is an element inside the <progressmeter> implementation. */
-.bufferBar .progress-bar {
-  /*
-   * Note that this is drawn on top of the .backgroundBar. So although this
-   * has the same background-color specified, the semitransparent
-   * compositing gives it a different visual appearance.
-   */
-  background-color: rgba(255,255,255,.5);
-  border-radius: 2.5px;
-  -moz-appearance: none;
-}
-
-.progressBar .progress-bar {
-  background-color: white;
-  border-radius: 2.5px;
-  -moz-appearance: none;
-}
-
-/* .scale-slider is an element inside the <scale> implementation. */
-.scrubber .scale-slider,
-.volumeControl .scale-slider {
-  /* Hide the default horizontal bar. */
-  -moz-appearance: none;
-  background: none;
-  margin: 0;
-}
-
-.scrubber .scale-slider {
-  /* abs(margin-top) + margin-bottom + bar height == timeThumb height */
-  margin-top: -10px;
-  margin-bottom: 10px;
-}
-/* .scale-thumb is an element inside the <scale> implementation. */
-.scrubber .scale-thumb,
-.volumeControl .scale-thumb {
-  /* Override the default thumb appearance with a custom image. */
-  -moz-appearance: none;
-  background: transparent;
-  border: none;
-}
-
-.timeThumb {
-  background: url(chrome://global/skin/media/scrubberThumb.png) no-repeat center;
-  min-width: 45px;
-  min-height: 28px;
-  -moz-box-pack: center;
-}
-
-.timeThumb[showhours="true"] {
-  background-image: url(chrome://global/skin/media/scrubberThumbWide.png);
-}
-
-.timeLabel {
-  color: rgba(255,255,255,.75);
-  font: message-box;
-  font-size: 10px;
-  text-shadow: rgba(0,0,0,.3) 0 1px;
-  padding-top: 7px;
-}
-
-%ifdef XP_MACOSX
-.timeLabel {
-  padding-top: 7px; /* center vertically with scrubber bar */
-}
-%else
-.timeLabel {
-  padding-top: 5px; /* center vertically with scrubber bar */
-}
-%endif
-
-.statusOverlay {
-  -moz-box-align: center;
-  -moz-box-pack: center;
-  background-color: rgba(0,0,0,.55);
+.duration {
+  display: inline-block;
+  white-space: pre;
+  color: #929292;
 }
 
 .statusIcon {
-  margin-bottom: 28px; /* same height as .controlBar, to keep icon centered above it */
   width: 36px;
   height: 36px;
+  margin-bottom: 20px;
 }
 
 .statusIcon[type="throbber"] {
   background: url(chrome://global/skin/media/throbber.png) no-repeat center;
 }
 
 .statusIcon[type="throbber"][stalled] {
   background: url(chrome://global/skin/media/stalled.png) no-repeat center;
 }
 
 .statusIcon[type="error"] {
+  min-width: 70px;
+  min-height: 60px;
   background: url(chrome://global/skin/media/error.png) no-repeat center;
+  background-size: contain;
 }
 
 /* Overlay Play button */
 .clickToPlay {
-  width: 64px;
-  height: 64px;
-  -moz-box-pack: center;
-  -moz-box-align: center;
-  opacity: 0.7;
-  background-image: url(chrome://global/skin/media/clicktoplay-bgtexture.png),
-                    url(chrome://global/skin/media/videoClickToPlayButton.svg);
-  background-repeat: repeat, no-repeat;
-  background-position: center, center;
-  background-size: auto, 64px 64px;
-  background-color: hsla(0,0%,10%,.5);
-}
-.clickToPlay:hover {
-  opacity: 1;
+  min-width: 48px;
+  min-height: 48px;
+  border-radius: 50%;
+  background-image: url(chrome://global/skin/media/playButton.svg#play);
+  background-repeat: no-repeat;
+  background-position: 54% 50%;
+  background-size: 40% 40%;
+  background-color: #1a1a1a;
+  opacity: 0.8;
+  position: relative;
+  top: 20px;
 }
 
-/* Statistics formatting */
-html|*.statsDiv {
-  position: relative;
-}
-html|td {
-  height: 1em;
-  max-height: 1em;
-  padding: 0 2px;
+.controlsSpacerStack:hover > .clickToPlay,
+.clickToPlay:hover {
+  opacity: 0.55;
 }
-html|table {
-  font-family: Helvetica, Arial, sans-serif;
-  font-size: 11px;
-  color: white;
-  text-shadow:
-    -1px -1px 0 #000,
-    1px -1px 0 #000,
-    -1px 1px 0 #000,
-    1px 1px 0 #000;
-  min-width: 100%;
-  background: rgba(68,68,68,.7);
-  table-layout: fixed;
-  border-collapse: collapse;
-  position: absolute;
+
+.controlsSpacerStack:hover > .clickToPlay[fadeout] {
+  opacity: 0;
+}
+
+.controlBar[fullscreen-unavailable] .fullscreenButton {
+  display: none;
 }
 
 /* CSS Transitions */
 .clickToPlay {
-  transition-property: opacity, background-size;
+  transition-property: transform, opacity;
   transition-duration: 400ms, 400ms;
 }
-.clickToPlay[fadeout] {
-  background-size: auto, 192px 192px;
+
+.controlsSpacer[fadeout] {
   opacity: 0;
 }
+
+.clickToPlay[fadeout] {
+  transform: scale(3);
+  opacity: 0;
+}
+
 .clickToPlay[fadeout][immediate] {
   transition-property: opacity, background-size;
   transition-duration: 0s, 0s;
 }
 .controlBar:not([immediate]) {
   transition-property: opacity;
   transition-duration: 200ms;
 }
 .controlBar[fadeout] {
   opacity: 0;
 }
 .volumeStack:not([immediate]) {
   transition-property: opacity, margin-top;
   transition-duration: 200ms, 200ms;
 }
-.volumeStack[fadeout] {
-  opacity: 0;
-  margin-top: 0;
-}
 .statusOverlay:not([immediate]) {
   transition-property: opacity;
   transition-duration: 300ms;
   transition-delay: 750ms;
 }
 .statusOverlay[fadeout] {
   opacity: 0;
 }
 
 /* Error description formatting */
 .errorLabel {
-  font-family: Helvetica, Arial, sans-serif;
-  font-size: 11px;
-  color: #bbb;
-  text-shadow:
-    -1px -1px 0 #000,
-    1px -1px 0 #000,
-    -1px 1px 0 #000,
-    1px 1px 0 #000;
   padding: 0 10px;
   text-align: center;
+  font: message-box;
+  font-size: 14px;
+  color: #ffffff;
 }
 
-@media (min-resolution: 2dppx) {
-  .playButton {
-    background-image: url(chrome://global/skin/media/pauseButton@2x.png);
-    background-size: 28px 28px;
-  }
-  .playButton[paused] {
-    background-image: url(chrome://global/skin/media/playButton@2x.png);
-    background-size: 28px 28px;
-  }
-  .volumeBackground {
-    background-image: url(chrome://global/skin/media/volume-empty@2x.png);
-    background-size: 32px 16px;
-  }
-  .volumeForeground {
-    background-image: url(chrome://global/skin/media/volume-full@2x.png);
-    background-size: 32px 16px;
-  }
-  .muteButton {
-    background-image: url(chrome://global/skin/media/muteButton@2x.png);
-    background-size: 33px 28px;
-  }
-  .muteButton[muted] {
-    background-image: url(chrome://global/skin/media/unmuteButton@2x.png);
-    background-size: 33px 28px;
-  }
-  .muteButton[noAudio] {
-    background-image: url(chrome://global/skin/media/noAudio@2x.png);
-    background-size: 33px 28px;
-  }
-  .closedCaptionButton {
-    background-image: url(chrome://global/skin/media/closeCaptionButton@2x.png);
-    background-position: 4px;
-    background-size: 28px 28px;
-  }
-  .fullscreenButton {
-    background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton@2x.png"), 0, 32, 32, 0);
-    background-size: 16px 16px;
-  }
-  .fullscreenButton[fullscreened] {
-    background-image: -moz-image-rect(url("chrome://global/skin/media/fullscreenButton@2x.png"), 0, 64, 32, 32);
-    background-size: 16px 16px;
-  }
-  .timeThumb {
-    background-image: url(chrome://global/skin/media/scrubberThumb@2x.png);
-    background-size: 33px 28px;
-  }
-  .timeThumb[showhours="true"] {
-    background-image: url(chrome://global/skin/media/scrubberThumbWide@2x.png);
-    background-size: 45px 28px;
-  }
+.errorLabel {
+  display: none;
 }
+
+[error="errorAborted"]         > [anonid="errorAborted"],
+[error="errorNetwork"]         > [anonid="errorNetwork"],
+[error="errorDecode"]          > [anonid="errorDecode"],
+[error="errorSrcNotSupported"] > [anonid="errorSrcNotSupported"],
+[error="errorNoSource"]        > [anonid="errorNoSource"],
+[error="errorGeneric"]         > [anonid="errorGeneric"] {
+  display: inline;
+}
deleted file mode 100644
index 589abfbd5816bb3ec179a3fdf6530106356dba8e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ca494e89366563cbb509761eb1f8f3bcae60e2cd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4398a569b8c76d0b42fd2900abfcdec1747a0483..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5bf63c7ee25a0a7c2fbfee5db50b95b20093b1e8..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001