Bug 885506 - Port |Bug 840745 - default html5 audio/video player controls should facilitate changing playback rate| r=Neil
authorJens Hatlak <jh@junetz.de>
Wed, 17 Jul 2013 01:30:13 +0800
changeset 12758 9cf4459806ca03cadd52d0a690361bc800c7990f
parent 12757 3ba0cc41732a044652c87a8f5d1cee21f2ac9a0a
child 12759 5528186da5a0a185010949852ea6f5ff56595854
push id9341
push userphilip.chee@gmail.com
push dateTue, 16 Jul 2013 17:30:51 +0000
treeherdercomm-central@9cf4459806ca [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersNeil
bugs885506, 840745
Bug 885506 - Port |Bug 840745 - default html5 audio/video player controls should facilitate changing playback rate| r=Neil a=Callek CLOSED TREE
suite/browser/test/mochitest/test_contextmenu.html
suite/common/contentAreaContextOverlay.xul
suite/common/nsContextMenu.js
suite/locales/en-US/chrome/common/contentAreaCommands.dtd
--- a/suite/browser/test/mochitest/test_contextmenu.html
+++ b/suite/browser/test/mochitest/test_contextmenu.html
@@ -380,16 +380,21 @@ function runTest(testNum) {
         break;
 
     case 9:
         // Context menu for a video (with a VALID media source)
         checkContextMenu(["popupwindow-reject",         true,
                           "---",                        null,
                           "context-media-play",         true,
                           "context-media-mute",         true,
+                          "context-media-playbackrate", true,
+                              ["context-media-playbackrate-050", true,
+                               "context-media-playbackrate-100", true,
+                               "context-media-playbackrate-150", true,
+                               "context-media-playbackrate-200", true], null,
                           "context-media-hidecontrols", true,
                           "context-video-showstats",    true,
                           "context-video-fullscreen",   true,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
@@ -400,32 +405,42 @@ function runTest(testNum) {
         break;
 
     case 10:
         // Context menu for a video (with an audio-only file)
         checkContextMenu(["popupwindow-reject",         true,
                           "---",                        null,
                           "context-media-play",         true,
                           "context-media-mute",         true,
+                          "context-media-playbackrate", true,
+                              ["context-media-playbackrate-050", true,
+                               "context-media-playbackrate-100", true,
+                               "context-media-playbackrate-150", true,
+                               "context-media-playbackrate-200", true], null,
                           "context-media-showcontrols", true,
                           "---",                        null,
                           "context-copyaudiourl",       true,
                           "---",                        null,
                           "context-saveaudio",          true,
                           "context-sendaudio",          true]);
         closeContextMenu();
         openContextMenuFor(video_bad); // Invoke context menu for next test.
         break;
 
     case 11:
         // Context menu for a video (with an INVALID media source)
         checkContextMenu(["popupwindow-reject",         true,
                           "---",                        null,
                           "context-media-play",         false,
                           "context-media-mute",         false,
+                          "context-media-playbackrate", false,
+                              ["context-media-playbackrate-050", null,
+                               "context-media-playbackrate-100", null,
+                               "context-media-playbackrate-150", null,
+                               "context-media-playbackrate-200", null], null,
                           "context-media-hidecontrols", false,
                           "context-video-showstats",    false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
@@ -436,16 +451,21 @@ function runTest(testNum) {
         break;
 
     case 12:
         // Context menu for a video (with an INVALID media source)
         checkContextMenu(["popupwindow-reject",         true,
                           "---",                        null,
                           "context-media-play",         false,
                           "context-media-mute",         false,
+                          "context-media-playbackrate", false,
+                              ["context-media-playbackrate-050", null,
+                               "context-media-playbackrate-100", null,
+                               "context-media-playbackrate-150", null,
+                               "context-media-playbackrate-200", null], null,
                           "context-media-hidecontrols", false,
                           "context-video-showstats",    false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          false,
                           "context-copyvideourl",       false,
                           "---",                        null,
                           "context-savevideo",          false,
@@ -492,16 +512,21 @@ function runTest(testNum) {
         break;
 
     case 14:
         // Context menu for a video in an iframe
         checkContextMenu(["popupwindow-reject",         true,
                           "---",                        null,
                           "context-media-play",         true,
                           "context-media-mute",         true,
+                          "context-media-playbackrate", true,
+                              ["context-media-playbackrate-050", true,
+                               "context-media-playbackrate-100", true,
+                               "context-media-playbackrate-150", true,
+                               "context-media-playbackrate-200", true], null,
                           "context-media-hidecontrols", true,
                           "context-video-showstats",    true,
                           "context-video-fullscreen",   true,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
--- a/suite/common/contentAreaContextOverlay.xul
+++ b/suite/common/contentAreaContextOverlay.xul
@@ -99,16 +99,44 @@
       <menuitem id="context-media-mute"
                 label="&mediaMute.label;"
                 accesskey="&mediaMute.accesskey;"
                 oncommand="gContextMenu.mediaCommand('mute');"/>
       <menuitem id="context-media-unmute"
                 label="&mediaUnmute.label;"
                 accesskey="&mediaUnmute.accesskey;"
                 oncommand="gContextMenu.mediaCommand('unmute');"/>
+      <menu id="context-media-playbackrate"
+            label="&mediaPlaybackRate.label;"
+            accesskey="&mediaPlaybackRate.accesskey;">
+        <menupopup id="media-playbackrate-popup"
+                   oncommand="gContextMenu.mediaCommand('playbackRate',
+                              event.target.id.replace(/.*-/, '') / 100);">
+          <menuitem id="context-media-playbackrate-050"
+                    label="&mediaPlaybackRate050.label;"
+                    accesskey="&mediaPlaybackRate050.accesskey;"
+                    type="radio"
+                    name="playbackrate"/>
+          <menuitem id="context-media-playbackrate-100"
+                    label="&mediaPlaybackRate100.label;"
+                    accesskey="&mediaPlaybackRate100.accesskey;"
+                    type="radio"
+                    name="playbackrate"/>
+          <menuitem id="context-media-playbackrate-150"
+                    label="&mediaPlaybackRate150.label;"
+                    accesskey="&mediaPlaybackRate150.accesskey;"
+                    type="radio"
+                    name="playbackrate"/>
+          <menuitem id="context-media-playbackrate-200"
+                    label="&mediaPlaybackRate200.label;"
+                    accesskey="&mediaPlaybackRate200.accesskey;"
+                    type="radio"
+                    name="playbackrate"/>
+        </menupopup>
+      </menu>
       <menuitem id="context-media-showcontrols"
                 label="&mediaShowControls.label;"
                 accesskey="&mediaShowControls.accesskey;"
                 oncommand="gContextMenu.mediaCommand('showcontrols');"/>
       <menuitem id="context-media-hidecontrols"
                 label="&mediaHideControls.label;"
                 accesskey="&mediaHideControls.accesskey;"
                 oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
--- a/suite/common/nsContextMenu.js
+++ b/suite/common/nsContextMenu.js
@@ -372,35 +372,41 @@ nsContextMenu.prototype = {
     var onMedia = (this.onVideo || this.onAudio);
     // Several mutually exclusive items... play/pause, mute/unmute, show/hide
     this.showItem("context-media-play",
                   onMedia && (this.target.paused || this.target.ended));
     this.showItem("context-media-pause",
                   onMedia && !this.target.paused && !this.target.ended);
     this.showItem("context-media-mute", onMedia && !this.target.muted);
     this.showItem("context-media-unmute", onMedia && this.target.muted);
+    this.showItem("context-media-playbackrate", onMedia);
     this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
     this.showItem("context-media-hidecontrols", onMedia && this.target.controls);
     this.showItem("context-video-fullscreen", this.onVideo);
 
     var statsShowing = this.onVideo &&
                        this.target.wrappedJSObject.mozMediaStatisticsShowing;
     this.showItem("context-video-showstats",
                   this.onVideo && this.target.controls && !statsShowing);
     this.showItem("context-video-hidestats",
                   this.onVideo && this.target.controls && statsShowing);
 
     // Disable them when there isn't a valid media source loaded.
     if (onMedia) {
+      this.setItemAttr("context-media-playbackrate-050", "checked", this.target.playbackRate == 0.5);
+      this.setItemAttr("context-media-playbackrate-100", "checked", this.target.playbackRate == 1.0);
+      this.setItemAttr("context-media-playbackrate-150", "checked", this.target.playbackRate == 1.5);
+      this.setItemAttr("context-media-playbackrate-200", "checked", this.target.playbackRate == 2.0);
       var hasError = this.target.error != null ||
                      this.target.networkState == this.target.NETWORK_NO_SOURCE;
       this.setItemAttr("context-media-play", "disabled", hasError);
       this.setItemAttr("context-media-pause", "disabled", hasError);
       this.setItemAttr("context-media-mute", "disabled", hasError);
       this.setItemAttr("context-media-unmute", "disabled", hasError);
+      this.setItemAttr("context-media-playbackrate", "disabled", hasError);
       this.setItemAttr("context-media-showcontrols", "disabled", hasError);
       this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
       if (this.onVideo) {
         let canSave = this.target.readyState >= this.target.HAVE_CURRENT_DATA;
         this.setItemAttr("context-video-saveimage", "disabled", !canSave);
         this.setItemAttr("context-video-fullscreen", "disabled", hasError);
         this.setItemAttr("context-video-showstats", "disabled", hasError);
         this.setItemAttr("context-video-hidestats", "disabled", hasError);
@@ -1367,32 +1373,35 @@ nsContextMenu.prototype = {
         if (sibling.getAttribute("hidden") != "true")
           return true;
         sibling = sibling.previousSibling;
       }
     }
     return false;
   },
 
-  mediaCommand: function(aCommand) {
+  mediaCommand: function(aCommand, aData) {
     var media = this.target;
 
     switch (aCommand) {
       case "play":
         media.play();
         break;
       case "pause":
         media.pause();
         break;
       case "mute":
         media.muted = true;
         break;
       case "unmute":
         media.muted = false;
         break;
+      case "playbackRate":
+        media.playbackRate = aData;
+        break;
       case "hidecontrols":
         media.removeAttribute("controls");
         break;
       case "showcontrols":
         media.setAttribute("controls", "true");
         break;
       case "showstats":
       case "hidestats":
--- a/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
+++ b/suite/locales/en-US/chrome/common/contentAreaCommands.dtd
@@ -99,16 +99,29 @@
 <!ENTITY mediaPlay.label              "Play">
 <!ENTITY mediaPlay.accesskey          "P">
 <!ENTITY mediaPause.label             "Pause">
 <!ENTITY mediaPause.accesskey         "P">
 <!ENTITY mediaMute.label              "Mute">
 <!ENTITY mediaMute.accesskey          "M">
 <!ENTITY mediaUnmute.label            "Unmute">
 <!ENTITY mediaUnmute.accesskey        "m">
+<!ENTITY mediaPlaybackRate.label        "Playback Speed">
+<!ENTITY mediaPlaybackRate.accesskey    "b">
+<!ENTITY mediaPlaybackRate050.label     "Slow Motion (½×)">
+<!ENTITY mediaPlaybackRate050.accesskey "S">
+<!ENTITY mediaPlaybackRate100.label     "Normal Speed">
+<!ENTITY mediaPlaybackRate100.accesskey "N">
+<!ENTITY mediaPlaybackRate150.label     "High Speed (1½×)">
+<!ENTITY mediaPlaybackRate150.accesskey "H">
+<!ENTITY mediaPlaybackRate200.label     "Double Speed">
+<!ENTITY mediaPlaybackRate200.accesskey "D">
+<!-- LOCALIZATION NOTE: The access keys for "Show Controls" and
+"Hide Controls" are the same because the two context-menu
+items are mutually exclusive. -->
 <!ENTITY mediaShowControls.label      "Show Media Controls">
 <!ENTITY mediaShowControls.accesskey  "a">
 <!ENTITY mediaHideControls.label      "Hide Media Controls">
 <!ENTITY mediaHideControls.accesskey  "a">
 <!ENTITY videoFullScreen.label        "Full Screen">
 <!ENTITY videoFullScreen.accesskey    "F">
 <!ENTITY videoSaveImage.label         "Save Snapshot As…">
 <!ENTITY videoSaveImage.accesskey     "S">