Bug 1181055 - video play button does not work if the VIDEO element has onclick handler calling play(). r?gijs
MozReview-Commit-ID: c6V9LMIdK0
--- a/toolkit/content/tests/widgets/chrome.ini
+++ b/toolkit/content/tests/widgets/chrome.ini
@@ -14,8 +14,9 @@ skip-if = os == 'linux' # Bug 1115088
[test_menubar.xul]
skip-if = os == 'mac'
[test_popupanchor.xul]
skip-if = os == 'android'
[test_popupreflows.xul]
[test_tree_column_reorder.xul]
skip-if = toolkit == 'android'
[test_videocontrols.html]
+[test_videocontrols_onclickplay.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_videocontrols_onclickplay.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Video controls test</title>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="head.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+
+<div id="content">
+ <video id="video" controls mozNoDynamicControls preload="auto"></video>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+var video = document.getElementById("video");
+
+function startMediaLoad() {
+ // Kick off test once video has loaded, in its canplaythrough event handler.
+ video.src = "seek_with_sound.ogg";
+ video.addEventListener("canplaythrough", runTest, false);
+}
+
+function loadevent(event) {
+ SpecialPowers.pushPrefEnv({"set": [["media.cache_size", 40000]]}, startMediaLoad);
+}
+
+window.addEventListener("load", loadevent, false);
+
+function runTest() {
+ video.addEventListener("click", function() {
+ this.play();
+ });
+ ok(video.paused, "video should be paused initially");
+
+ new Promise(resolve => {
+ let timeupdates = 0;
+ video.addEventListener("timeupdate", function timeupdate() {
+ ok(!video.paused, "video should not get paused after clicking in middle");
+
+ if (++timeupdates == 3) {
+ video.removeEventListener("timeupdate", timeupdate);
+ resolve();
+ }
+ });
+
+ synthesizeMouseAtCenter(video, {}, window);
+ }).then(function() {
+ new Promise(resolve => {
+ video.addEventListener("pause", function onpause() {
+ setTimeout(() => {
+ ok(video.paused, "video should still be paused 200ms after pause request");
+ // When the video reaches the end of playback it is automatically paused.
+ // Check during the pause event that the video has not reachd the end
+ // of playback.
+ ok(!video.ended, "video should not have paused due to playback ending");
+ resolve();
+ }, 200);
+ });
+
+ synthesizeMouse(video, 10, video.clientHeight - 10, {}, window);
+ }).then(SimpleTest.finish);
+ });
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -1091,34 +1091,40 @@
onFullscreenChange: function () {
if (this.isVideoInFullScreen()) {
Utils._hideControlsTimeout = setTimeout(this._hideControlsFn, this.HIDE_CONTROLS_TIMEOUT_MS);
}
this.setFullscreenButtonState();
},
clickToPlayClickHandler : function(e) {
- if (e.button != 0)
+ if (e.button != undefined && 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;
}
- // 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();
+ // Read defaultPrevented and the playback state asynchronously,
+ // since Web content may want to consume the "click" event
+ // but will only receive it after us. If web content
+ // doesn't use preventDefault but still toggles playback,
+ // we will treat that act the same as a call to preventDefault()
+ // so the web content-initiated toggle is not reverted.
+ let previousState = this.video.paused;
+ setTimeout(() => {
+ if (e.defaultPrevented ||
+ this.video.paused != previousState) {
+ return;
+ }
+ this.togglePause();
}, 0);
},
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
@@ -1365,17 +1371,17 @@
}
return false;
}
return isDescendant(event.target) && isDescendant(event.relatedTarget);
},
log : function (msg) {
if (this.debug)
- dump("videoctl: " + msg + "\n");
+ console.log("videoctl: " + msg + "\n");
},
get isTopLevelSyntheticDocument() {
let doc = this.video.ownerDocument;
let win = doc.defaultView;
return doc.mozSyntheticDocument && win === win.top;
},
@@ -1507,17 +1513,17 @@
// 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, false);
}
addListener(this.muteButton, "command", this.toggleMute);
- addListener(this.playButton, "command", this.togglePause);
+ 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, "fullscreenchange", this.onFullscreenChange);