Bug 1444489 - Part VI, Enlarge the size of controls on mobile r=e7358d9c+590837,Gijs
authorTimothy Guan-tin Chien <timdream@gmail.com>
Fri, 09 Mar 2018 17:00:51 -0800
changeset 409401 f13695146a00
parent 409400 6bb4c1e01a69
child 409402 0f95b59a3e75
push id61512
push usertimdream@gmail.com
push dateThu, 22 Mar 2018 01:55:34 +0000
treeherderautoland@99fc41ec7ce9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerse7358d9c
bugs1444489, 590837
milestone61.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 1444489 - Part VI, Enlarge the size of controls on mobile r=e7358d9c+590837,Gijs Enlarge controls by around 1.3x, which is the size of the original touch controls. MozReview-Commit-ID: kpgFFIW2hh
browser/base/content/test/static/browser_parsable_css.js
toolkit/content/tests/widgets/test_videocontrols_size.html
toolkit/content/widgets/videocontrols.xml
toolkit/themes/shared/media/videocontrols.css
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -111,16 +111,18 @@ let propNameWhitelist = [
   // Bug 1442300
   {propName: "--in-content-category-background",
    isFromDevTools: false},
   // These custom properties are retrieved directly from CSSOM
   // in videocontrols.xml to get pre-defined style instead of computed
   // dimensions, which is why they are not referenced by CSS.
   {propName: "--clickToPlay-width",
    isFromDevTools: false},
+  {propName: "--playButton-width",
+   isFromDevTools: false},
   {propName: "--muteButton-width",
    isFromDevTools: false},
   {propName: "--castingButton-width",
    isFromDevTools: false},
   {propName: "--closedCaptionButton-width",
    isFromDevTools: false},
   {propName: "--fullscreenButton-width",
    isFromDevTools: false},
--- a/toolkit/content/tests/widgets/test_videocontrols_size.html
+++ b/toolkit/content/tests/widgets/test_videocontrols_size.html
@@ -28,21 +28,23 @@
 
 <pre id="test">
 <script clas="testbody" type="application/javascript">
   SimpleTest.waitForExplicitFinish();
 
   const videoElems = [...document.getElementsByTagName("video")];
   const testCases = [];
 
-  const buttonWidth = 30;
-  const minSrubberWidth = 48;
-  const minControlBarHeight = 40;
-  const minControlBarWidth = 48;
-  const minClickToPlaySize = 48;
+  const isTouchControl = navigator.appVersion.includes("Android");
+
+  const buttonWidth = isTouchControl ? 40 : 30;
+  const minSrubberWidth = isTouchControl ? 64 : 48;
+  const minControlBarHeight = isTouchControl ? 52 : 40;
+  const minControlBarWidth = isTouchControl ? 58 : 48;
+  const minClickToPlaySize = isTouchControl ? 64 : 48;
 
   function getElementName(elem) {
     return elem.getAttribute("anonid") || elem.getAttribute("class");
   }
 
   function testButton(btn) {
     if (btn.hidden) return;
 
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -34,17 +34,17 @@
 
       <div anonid="controlsOverlay" class="controlsOverlay stackItem">
         <div class="controlsSpacerStack">
           <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"
+                  class="button playButton"
                   playlabel="&playButton.playLabel;"
                   pauselabel="&playButton.pauseLabel;"
                   tabindex="-1"/>
           <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" tabindex="-1"></progress>
                 <progress anonid="progressBar" class="progressBar" value="0" max="100" tabindex="-1"></progress>
@@ -54,28 +54,28 @@
           </div>
           <span anonid="positionLabel" class="positionLabel" role="presentation"></span>
           <span anonid="durationLabel" class="durationLabel" role="presentation"></span>
           <span anonid="positionDurationBox" class="positionDurationBox" aria-hidden="true">
             &positionAndDuration.nameFormat;
           </span>
           <div anonid="controlBarSpacer" class="controlBarSpacer" hidden="true" role="none"></div>
           <button anonid="muteButton"
-                  class="muteButton"
+                  class="button muteButton"
                   mutelabel="&muteButton.muteLabel;"
                   unmutelabel="&muteButton.unmuteLabel;"
                   tabindex="-1"/>
           <div anonid="volumeStack" class="volumeStack progressContainer" role="none">
             <input type="range" anonid="volumeControl" class="volumeControl" min="0" max="100" step="1" tabindex="-1"/>
           </div>
-          <button anonid="castingButton" class="castingButton"
+          <button anonid="castingButton" class="button castingButton"
                   aria-label="&castingButton.castingLabel;"/>
-          <button anonid="closedCaptionButton" class="closedCaptionButton"/>
+          <button anonid="closedCaptionButton" class="button closedCaptionButton"/>
           <button anonid="fullscreenButton"
-                  class="fullscreenButton"
+                  class="button fullscreenButton"
                   enterfullscreenlabel="&fullscreenButton.enterfullscreenlabel;"
                   exitfullscreenlabel="&fullscreenButton.exitfullscreenlabel;"/>
         </div>
         <div anonid="textTrackList" class="textTrackList" hidden="true" offlabel="&closedCaption.off;"></div>
       </div>
     </div>
   </xbl:content>
 
@@ -1698,33 +1698,37 @@
         this.castingButton = document.getAnonymousElementByAttribute(binding, "anonid", "castingButton");
         this.closedCaptionButton = document.getAnonymousElementByAttribute(binding, "anonid", "closedCaptionButton");
         this.textTrackList = document.getAnonymousElementByAttribute(binding, "anonid", "textTrackList");
 
         if (this.positionDurationBox) {
           this.durationSpan = this.positionDurationBox.getElementsByTagName("span")[0];
         }
 
+        this.videocontrols.isTouchControls =
+          navigator.appVersion.includes("Android");
+        if (this.videocontrols.isTouchControls) {
+          this.controlsContainer.classList.add("touch");
+        }
+
         this.controlBarComputedStyles = getComputedStyle(this.controlBar);
 
         // Hide and show control in certain order.
         this.prioritizedControls = [
           this.playButton,
           this.muteButton,
           this.fullscreenButton,
           this.castingButton,
           this.closedCaptionButton,
           this.positionDurationBox,
           this.scrubberStack,
           this.durationSpan,
           this.volumeStack
         ];
 
-        this.videocontrols.isTouchControls =
-          navigator.appVersion.includes("Android");
         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
@@ -2020,62 +2024,69 @@
 
       hasError() {
         return (this.video.error != null || this.video.networkState == this.video.NETWORK_NO_SOURCE);
       },
 
       handleEvent(aEvent) {
         // If the binding is detached (or has been replaced by a
         // newer instance of the binding), nuke our event-listeners.
-        if (this.binding.randomID != this.randomID) {
+        if (this.videocontrols.randomID != this.randomID) {
           this.terminateEventListeners();
           return;
         }
 
         switch (aEvent.type) {
           case "play":
             this.noControlsOverlay.hidden = true;
             break;
           case "playing":
             this.noControlsOverlay.hidden = true;
             break;
         }
       },
 
       blockedVideoHandler() {
-        if (this.binding.randomID != this.randomID) {
+        if (this.videocontrols.randomID != this.randomID) {
           this.terminateEventListeners();
           return;
         } else if (this.hasError()) {
           this.noControlsOverlay.hidden = true;
           return;
         }
         this.noControlsOverlay.hidden = false;
       },
 
       clickToPlayClickHandler(e) {
-        if (this.binding.randomID != this.randomID) {
+        if (this.videocontrols.randomID != this.randomID) {
           this.terminateEventListeners();
           return;
         } else if (e.button != 0) {
           return;
         }
 
         this.noControlsOverlay.hidden = true;
         this.video.play();
       },
 
       init(binding) {
-        this.binding = binding;
+        this.videocontrols = binding;
         this.randomID = Math.random();
-        this.binding.randomID = this.randomID;
+        this.videocontrols.randomID = this.randomID;
         this.video = binding.parentNode;
+        this.controlsContainer = document.getAnonymousElementByAttribute(binding, "anonid", "controlsContainer");
         this.clickToPlay       = document.getAnonymousElementByAttribute(binding, "anonid", "clickToPlay");
         this.noControlsOverlay = document.getAnonymousElementByAttribute(binding, "anonid", "controlsContainer");
 
+        this.videocontrols.isTouchControls =
+          navigator.appVersion.includes("Android");
+        if (this.videocontrols.isTouchControls) {
+          this.controlsContainer.classList.add("touch");
+        }
+
         let self = this;
         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.clickToPlay, "click", this.clickToPlayClickHandler);
         addListener(this.video, "MozNoControlsBlockedVideo", this.blockedVideoHandler);
--- a/toolkit/themes/shared/media/videocontrols.css
+++ b/toolkit/themes/shared/media/videocontrols.css
@@ -18,35 +18,56 @@ audio > xul|videocontrols {
   text-align: left;
   list-style-image: none !important;
   font: normal normal normal 100%/normal sans-serif !important;
   text-decoration: none !important;
 }
 
 .controlsContainer {
   --clickToPlay-size: 48px;
+  --button-size: 30px;
+  --timer-size: 40px;
+  --timer-long-size: 60px;
+  --track-size: 5px;
+  --thumb-size: 13px;
+  --label-font-size: 13px;
+}
+.controlsContainer.touch {
+  --clickToPlay-size: 64px;
+  --button-size: 40px;
+  --timer-size: 52px;
+  --timer-long-size: 78px;
+  --track-size: 7px;
+  --thumb-size: 16px;
+  --label-font-size: 16px;
 }
 
 /* Some CSS custom properties defined here are referenced by videocontrols.xml in JavaScript */
 .controlBar {
   /* Do not delete: these variables are accessed by JavaScript directly.
      see videocontrols.xml and search for |-width|. */
   --clickToPlay-width: var(--clickToPlay-size);
-  --playButton-width: 30px;
-  --playButton-height: var(--playButton-width);
+  --playButton-width: var(--button-size);
   --scrubberStack-width: 64px;
-  --muteButton-width: 30px;
+  --muteButton-width: var(--button-size);
   --volumeStack-width: 48px;
-  --castingButton-width: 30px;
-  --closedCaptionButton-width: 30px;
-  --fullscreenButton-width: 30px;
-  --positionDurationBox-width: 40px;
-  --durationSpan-width: 40px;
-  --positionDurationBox-width-long: 60px;
-  --durationSpan-width-long: 60px;
+  --castingButton-width: var(--button-size);
+  --closedCaptionButton-width: var(--button-size);
+  --fullscreenButton-width: var(--button-size);
+  --positionDurationBox-width: var(--timer-size);
+  --durationSpan-width: var(--timer-size);
+  --positionDurationBox-width-long: var(--timer-long-size);
+  --durationSpan-width-long: var(--timer-long-size);
+}
+
+.touch .controlBar {
+  /* Do not delete: these variables are accessed by JavaScript directly.
+     see videocontrols.xml and search for |-width|. */
+  --scrubberStack-width: 84px;
+  --volumeStack-width: 64px;
 }
 
 .controlsContainer [hidden="true"],
 .controlBar[hidden] {
   display: none;
 }
 
 .controlBar[size="hidden"] {
@@ -101,49 +122,45 @@ audio > xul|videocontrols {
   justify-content: center;
   align-items: center;
   overflow: hidden;
   height: 40px;
   padding: 0 9px;
   background-color: rgba(26,26,26,.8);
 }
 
-.playButton,
-.muteButton,
-.castingButton,
-.closedCaptionButton,
-.fullscreenButton {
+.touch .controlBar {
+  height: 52px;
+}
+
+.controlBar > .button {
   height: 100%;
-  min-width: var(--playButton-width);
-  min-height: var(--playButton-height);
+  min-width: var(--button-size);
+  min-height: var(--button-size);
   padding: 6px;
   border: 0;
   margin: 0;
   background-color: transparent;
   background-repeat: no-repeat;
   background-position: center;
   background-origin: content-box;
   background-clip: content-box;
   -moz-context-properties: fill;
   fill: #ffffff;
 }
 
-.playButton:hover,
-.muteButton:hover,
-.castingButton:hover,
-.closedCaptionButton:hover,
-.fullscreenButton:hover {
+.touch .controlBar > .button {
+  background-size: 24px 24px;
+}
+
+.controlBar > .button:hover {
   fill: #48a0f7;
 }
 
-.playButton:hover:active,
-.muteButton:hover:active,
-.castingButton:hover:active,
-.closedCaptionButton:hover:active,
-.fullscreenButton:hover:active {
+.controlBar > .button:hover:active {
   fill: #2d89e6;
 }
 
 .playButton {
   background-image: url(chrome://global/skin/media/pauseButton.svg);
 }
 .playButton[paused] {
   background-image: url(chrome://global/skin/media/playButton.svg);
@@ -188,18 +205,18 @@ audio > xul|videocontrols {
 }
 
 .controlBarSpacer {
   flex-grow: 1;
 }
 
 .volumeControl::-moz-range-thumb,
 .scrubber::-moz-range-thumb {
-  height: 13px;
-  width: 13px;
+  height: var(--thumb-size);
+  width: var(--thumb-size);
   border: none;
   border-radius: 50%;
   background-color: #ffffff;
   filter: drop-shadow(0px 0px 2px rgba(0,0,0,0.65));
 }
 
 .volumeControl::-moz-focus-outer,
 .scrubber::-moz-focus-outer {
@@ -211,17 +228,17 @@ audio > xul|videocontrols {
   flex-direction: column;
   justify-content: center;
   align-items: center;
 }
 
 .progressStack {
   position: relative;
   width: 100%;
-  height: 5px;
+  height: var(--track-size);
 }
 
 .scrubberStack {
   /* minus margin to get basis of required width */
   min-width: calc(var(--scrubberStack-width) - 18px);
   flex-basis: calc(var(--scrubberStack-width) - 18px);
   flex-grow: 2;
   flex-shrink: 0;
@@ -244,17 +261,17 @@ audio > xul|videocontrols {
 .volumeControl {
   bottom: 0;
   left: 0;
   position: absolute;
   width: 100%;
   height: 100%;
   padding: 0;
   border: 0;
-  border-radius: 2.5px;
+  border-radius: calc(var(--track-size) / 2);
   margin: 0;
   background: none;
   background-color: transparent;
 }
 
 .bufferBar,
 .volumeBackground {
   background-color: rgba(0,0,0,0.7);
@@ -262,17 +279,17 @@ audio > xul|videocontrols {
 
 .bufferBar::-moz-progress-bar,
 .progressBar::-moz-progress-bar,
 .volumeBackground::-moz-meter-bar {
   height: 100%;
   padding: 0;
   margin: 0;
   border: 0;
-  border-radius: 2.5px;
+  border-radius: calc(var(--track-size) / 2);
   background: none;
 }
 
 .scrubber:hover::-moz-range-thumb,
 .volumeControl:hover::-moz-range-thumb {
   background-color: #48a0f7;
 }
 
@@ -283,56 +300,60 @@ audio > xul|videocontrols {
 
 .scrubber::-moz-range-track,
 .scrubber::-moz-range-progress {
   background-color: transparent;
 }
 
 .volumeControl::-moz-range-progress,
 .volumeControl::-moz-range-track {
-  height: 5px;
-  border-radius: 2.5px;
+  height: var(--track-size);
+  border-radius: calc(var(--track-size) / 2);
 }
 
 .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;
+  border-radius: calc(var(--track-size) / 2);
 }
 
 .progressBar::-moz-progress-bar {
   background-color: #00b6f0;
 }
 
 .textTrackList {
   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;
 }
 
+.touch .textTrackList {
+  bottom: 58px;
+}
+
 .textTrackList > .textTrackItem {
   display: block;
   width: 100%;
-  height: 30px;
+  height: var(--button-size);
+  font-size: var(--label-font-size);
   padding: 2px 10px;
   border: none;
   margin: 0;
   white-space: nowrap;
   overflow: hidden;
   text-align: left;
   text-overflow: ellipsis;
   color: #ffffff;
@@ -353,17 +374,17 @@ audio > xul|videocontrols {
 }
 
 .positionDurationBox {
   text-align: center;
   padding-inline-start: 1px;
   padding-inline-end: 9px;
   white-space: nowrap;
   font: message-box;
-  font-size: 13px;
+  font-size: var(--label-font-size);
   font-size-adjust: 0.55;
   color: #ffffff;
 }
 
 %ifdef XP_MACOSX
 .positionDurationBox {
   font-size-adjust: unset;
   font-family: "Helvetica Neue", "Helvetica", sans-serif;