Bug 1244592 - Fix non-autoplay click-to-play - r?cpearce draft
authorGerald Squelart <gsquelart@mozilla.com>
Tue, 02 Feb 2016 09:32:35 +1100
changeset 327774 e652c6e3394c7ac3218e6cba422b49c61c4dc96e
parent 327605 d07dbd40dcd209124149f331f60dd55c8da33fbe
child 513756 b7e4a9cd79c3db6b9d09e4a34474a32a9941d5e7
push id10299
push usergsquelart@mozilla.com
push dateMon, 01 Feb 2016 22:32:58 +0000
reviewerscpearce
bugs1244592
milestone47.0a1
Bug 1244592 - Fix non-autoplay click-to-play - r?cpearce When media.autoplay.enabled==false, only videos may only be started through direct user actions. Unfortunately, for straight video files, the click-to-play handler defers video.play() to a 0-timeout (to allow other scripts to consume the click if they wish), which means the play() action arrives *outside* of the user- interaction window; in which case it will be blocked. The easiest solution is to send a play() then pause() immediately in the click handler, so that the play() is not blocked; a side-effect is that the media element remembers that user interaction has started, and will not block future play()'s.
toolkit/content/widgets/videocontrols.xml
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1085,28 +1085,49 @@
 
                 onFullscreenChange: function () {
                     if (this.isVideoInFullScreen()) {
                         Utils._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
                     }
                     this.setFullscreenButtonState();
                 },
 
+                clickToPlayInteractWithElement : function() {
+                    // First time here, do play and pause now to simulate user
+                    // interaction.
+                    if (this.video.paused || this.video.ended) {
+                        this.video.playbackRate = this.video.defaultPlaybackRate;
+                        this.video.play();
+                        this.video.pause();
+                    } else {
+                        this.video.pause();
+                        this.video.playbackRate = this.video.defaultPlaybackRate;
+                        this.video.play();
+                    }
+                    // Future calls will do nothing.
+                    this.clickToPlayInteractWithElement = function() {};
+                },
                 clickToPlayClickHandler : function(e) {
                     if (e.button != 0)
                         return;
                     if (this.hasError() && !this.suppressError) {
                         // Errors that can be dismissed should be placed here as we discover them.
                         if (this.video.error.code != this.video.error.MEDIA_ERR_ABORTED)
                             return;
                         this.statusOverlay.hidden = true;
                         this.suppressError = true;
                         return;
                     }
 
+                    // If not already started, send a quick play (and pause),
+                    // so the media element thinks user interaction has happened
+                    // when calling play() again in the timeout below (otherwise
+                    // media.autoplay.enabled==false might block it).
+                    this.clickToPlayInteractWithElement();
+
                     // Read defaultPrevented asynchronously, since Web content
                     // may want to consume the "click" event but will only
                     // receive it after us.
                     let self = this;
                     setTimeout(function clickToPlayCallback() {
                         if (!e.defaultPrevented)
                             self.togglePause();
                     }, 0);