Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Unified Diff: third_party/WebKit/LayoutTests/media/media-play-promise.html

Issue 1576283003: Have HTMLMediaElement::play() return a Promise. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review comments and cancel tasks Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/LayoutTests/media/media-play-promise.html
diff --git a/third_party/WebKit/LayoutTests/media/media-play-promise.html b/third_party/WebKit/LayoutTests/media/media-play-promise.html
new file mode 100644
index 0000000000000000000000000000000000000000..77b4ac76d873b04ea315ffba91cc0df810ef6f1d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/media/media-play-promise.html
@@ -0,0 +1,414 @@
+<html>
+<head>
+<script src=media-file.js></script>
+<!-- TODO(mlamouri): use testharness.js, see https://crbug.com/588956 -->
+<script src=video-test.js></script>
+
+<script>
+ // This is testing the behavior of play() with regards to the returned
+ // promise. This test file is creating a small framework in order to be able
+ // to test different cases easily and independently of each other.
+ //
+ // All tests have to be part of the TESTS array. When the page is loaded,
+ // first function in the array is run. A test is considered done when the
+ // promise returned by mediaElement.play() is resolved or rejected. Each
+ // test then needs to call play() once which wraps this logic. When a test
+ // is finished, the next test in the array is run until the entire array
+ // was processed.
+ //
+ // Each test should start by printing its name in order to facilitate reading
+ // the output.
+
+ function runNextTestOrFinish()
+ {
+ currentTest++;
+ if (currentTest >= TESTS.length) {
+ endTest();
+ return;
+ }
+
+ consoleWrite("");
+ TESTS[currentTest]();
+ }
+
+ function play()
+ {
+ consoleWrite("play()");
+ mediaElement.play().then(function() {
+ consoleWrite("arguments.length: " + arguments.length);
+ consoleWrite("Promise resolved with " + arguments[0]);
+ }, function(e) {
+ consoleWrite("arguments.length: " + arguments.length);
+ consoleWrite("Promise failed with " + e.name + ": " + e.message);
philipj_slow 2016/02/25 14:06:48 Off-topic: I noticed in another review that DOMExc
+ }).then(runNextTestOrFinish);
+ }
+
+ function playWithUserGesture()
+ {
+ var target = document.querySelector("p");
+ target.onclick = function() {
+ play();
+ target.onclick = null;
+ };
+
+ var boundingRect = target.getBoundingClientRect();
+ var x = boundingRect.left + (boundingRect.width / 2);
+ var y = boundingRect.top + (boundingRect.height / 2);
+
+ // Assuming running in Blink LayoutTests.
+ eventSender.mouseMoveTo(x, y);
+ eventSender.mouseDown();
+ eventSender.mouseUp();
+ }
+
+ var currentTest = -1;
+
+ var TESTS = [
+ // Test that play() on an element that doesn't have enough data will
+ // return a promise that resolves successfuly.
+ function playBeforeCanPlay()
+ {
+ consoleWrite("playBeforeCanPlay()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('loadedmetadata');
+ waitForEvent('loadeddata');
+ waitForEvent('canplay');
+ waitForEvent('playing');
+
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_NOTHING);
+ play();
+ },
+
+ // Test that play() on an element that has enough data will return a
+ // promise that resolves successfuly.
+ function playWhenCanPlay()
+ {
+ consoleWrite("playWhenCanPlay()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+
+ waitForEvent('canplay', function() {
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_FUTURE_DATA, ">=");
+ testExpected("mediaElement.paused", true);
+
+ play();
+ });
+ },
+
+ function playAfterPlaybackStarted()
+ {
+ consoleWrite("playAfterPlaybackStarted()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ mediaElement.preload = "auto";
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing', function() {
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_ENOUGH_DATA);
+ testExpected("mediaElement.paused", false);
+
+ play();
+ });
+
+ waitForEvent('canplaythrough', function() {
+ run("mediaElement.play()");
+ });
+ },
+
+ // Test that play() on an element when media playback requires a gesture
+ // returns a resolved promise if there is a user gesture.
+ function playRequiresUserGestureAndHasIt()
+ {
+ consoleWrite("playRequiresUserGestureAndHasIt()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(true);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ playWithUserGesture();
+ },
+
+ // Test that play() on an element when media playback requires a gesture
+ // returns a rejected promise if there is no user gesture.
+ function playRequiresUserGestureAndDoesNotHaveIt()
+ {
+ consoleWrite("playRequiresUserGestureAndDoesNotHaveIt()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(true);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ play();
+ },
+
+ // Test that play() on an element with an unsupported content will
+ // return a rejected promise.
+ function playNotSupportedContent()
+ {
+ consoleWrite("playNotSupportedContent()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "data:,");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+ });
+ play();
+ },
+
+ // Test that play() returns a resolved promise if called after the
+ // element suffered from a decode error.
+ // This test doesn't test a spec behaviour but tests that the Blink
+ // implementation properly changed after the spec changed.
+ function playDecodeError()
+ {
+ consoleWrite("playDecodeError()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_DECODE);
+ });
+
+ // The setMediaElementNetworkState() method requires metadata to be
+ // available.
+ waitForEvent('loadedmetadata', function() {
+ internals.setMediaElementNetworkState(mediaElement, 6 /* NetworkStateDecodeError */);
+ play();
+ });
+ },
+
+ // Test that play() returns a resolved promise if called after the
+ // element suffered from a network error.
+ // This test doesn't test a spec behaviour but tests that the Blink
+ // implementation properly changed after the spec changed
+ function playNetworkError()
+ {
+ consoleWrite("playNetworkError()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_NETWORK);
+ });
+
+ // The setMediaElementNetworkState() method requires metadata to be
+ // available.
+ waitForEvent('loadedmetadata', function() {
+ internals.setMediaElementNetworkState(mediaElement, 5 /* NetworkStateNetworkError */);
+ play();
+ });
+ },
+
+ // Test that play() returns a rejected promise if the element is
+ // suferring from a not supported error.
+ function playWithErrorAlreadySet()
+ {
+ consoleWrite("playWithErrorAlreadySet()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "data:,");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+ play();
+ });
+ },
+
+ // Test that play() returns a resolved promise if the element had its
+ // source changed after suffering from an error.
+ function playSrcChangedAfterError()
+ {
+ consoleWrite("playSrcChangedAfterError()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "data:,");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+ mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('playing');
+ waitForEvent('loadedmetadata', function() {
+ play();
+ });
+ });
+ },
+
+ // Test that play() returns a rejected promise if the element had an
+ // error and just changed its source.
+ function playRaceWithSrcChangeError()
+ {
+ consoleWrite("playRaceWithSrcChangeError()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "data:,");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('error', function() {
+ testExpected("mediaElement.error", "[object MediaError]");
+ testExpected("mediaElement.error.code", MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED);
+
+ mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ testExpected("mediaElement.error", null);
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_NOTHING);
+
+ waitForEvent('playing');
+ play();
+ });
+ },
+
+ // Test that play() returns a resolved promise when calling play() then
+ // pause() on an element that already has enough data to play. In other
+ // words, pause() doesn't cancel play() because it was resolved
+ // immediately.
+ function playAndPauseWhenCanPlay()
+ {
+ consoleWrite("playAndPauseWhenCanPlay()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+
+ waitForEvent('canplaythrough', function() {
+ waitForEvent('playing');
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_ENOUGH_DATA);
+ play();
+ testExpected("mediaElement.paused", false);
+ mediaElement.pause();
+ testExpected("mediaElement.paused", true);
+ });
+ },
+
+ // Test that play() returns a rejected promise when calling play() then
+ // pause() on an element that doesn't have enough data to play. In other
+ // words, pause() cancels play() before it can be resolved.
+ function playAndPauseBeforeCanPlay()
+ {
+ consoleWrite("playAndPauseBeforeCanPlay()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+
+ waitForEvent('playing');
+ testExpected("mediaElement.readyState", HTMLMediaElement.HAVE_NOTHING);
+ play();
+ testExpected("mediaElement.paused", false);
+ mediaElement.pause();
+ testExpected("mediaElement.paused", true);
+ },
+
+ // Test that load() rejects all the pending play() promises.
+ // This might be tested by other tests in this file but it is present in
+ // order to be explicit.
+ function loadRejectsPendingPromises()
+ {
+ consoleWrite("loadRejectsPendingPromises()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+
+ play(); // the promise will be left pending.
+
+ waitForEvent('playing');
+ run("mediaElement.load()");
+ },
+
+ // Test that changing the src rejects the pending play() promises.
+ function newSrcRejectPendingPromises()
+ {
+ consoleWrite("newSrcRejectPendingPromises()");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+
+ play(); // the promise will be left pending.
+
+ var mediaFile = findMediaFile("audio", "content/test");
+ run("mediaElement.src = '" + mediaFile + "'");
+ },
+
+ // Test ordering of events and promises.
+ // This is testing a bug in Blink, see https://crbug.com/587871
+ function testEventAndPromiseOrdering()
+ {
+ consoleWrite("testEventAndPromiseOrdering");
+ internals.settings.setMediaPlaybackRequiresUserGesture(false);
+
+ run("mediaElement = document.createElement('audio')");
+ run("mediaElement.src = 'data:,'");
+
+ waitForEvent('error', function() {
+ // Until https://crbug.com/587871 is fixed, the events will be
+ // [ volumechange, volumechange, promise ], it should be
+ // [ volumechange, promise, volumechange ].
+ waitForEvent('volumechange');
+ run("mediaElement.volume = 0.1");
+ play();
+ run("mediaElement.volume = 0.2");
+ });
+
+ }
+ ];
+
+ function start()
+ {
+ if (!('eventSender' in window) || !('internals' in window)) {
+ failTest("Not running in LayoutTests.");
+ return;
+ }
+ runNextTestOrFinish();
+ }
+
+</script>
+</head>
+
+<body onload="start()">
+
+<p>Test the play() behaviour with regards to the returned promise for media elements.</p>
+
+</body>
+</html>

Powered by Google App Engine
This is Rietveld 408576698