1
0
mirror of https://github.com/videojs/video.js.git synced 2025-03-29 22:07:10 +02:00

fix: throw error on muted resolution rejection during autoplay (#7293)

Previously, when autoplay was set to any or muted, we would accidentally swallow the autoplay rejection when we reset the muted state back to what it was. Instead, we want to re-throw the error.
To get it working, we also had to update our tests to try/catch in our fake promise.
This commit is contained in:
Roman Pougatchev 2021-06-30 12:28:41 -04:00 committed by GitHub
parent 0f707876ef
commit f9fb1d3f51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 26 additions and 11 deletions

View File

@ -1430,7 +1430,9 @@ class Player extends Component {
return;
}
const muted = () => {
// Save original muted() value, set muted to true, and attempt to play().
// On promise rejection, restore muted from saved value
const resolveMuted = () => {
const previouslyMuted = this.muted();
this.muted(true);
@ -1448,21 +1450,24 @@ class Player extends Component {
return;
}
return mutedPromise.catch(restoreMuted);
return mutedPromise.catch(err => {
restoreMuted();
throw new Error(`Rejection at manualAutoplay. Restoring muted value. ${err ? err : ''}`);
});
};
let promise;
// if muted defaults to true
// the only thing we can do is call play
if (type === 'any' && this.muted() !== true) {
if (type === 'any' && !this.muted()) {
promise = this.play();
if (isPromise(promise)) {
promise = promise.catch(muted);
promise = promise.catch(resolveMuted);
}
} else if (type === 'muted' && this.muted() !== true) {
promise = muted();
} else if (type === 'muted' && !this.muted()) {
promise = resolveMuted();
} else {
promise = this.play();
}
@ -1473,7 +1478,7 @@ class Player extends Component {
return promise.then(() => {
this.trigger({type: 'autoplay-success', autoplay: type});
}).catch((e) => {
}).catch(() => {
this.trigger({type: 'autoplay-failure', autoplay: type});
});
}

View File

@ -29,18 +29,25 @@ QUnit.module('autoplay', {
fixture.appendChild(videoTag);
// this promise fake will act right away
// it will also only act on catch calls
// These mock promises immediately execute,
// effectively synchronising promise chains for testing
// This will only act on catch calls
this.rejectPromise = {
then(fn) {
return this;
},
catch(fn) {
fn();
try {
fn();
} catch (err) {
return this;
}
return this;
}
};
// This will only act on then calls
this.resolvePromise = {
then(fn) {
fn();
@ -274,7 +281,10 @@ QUnit.test('option = "any" play, no muted, rejection leads to muted then play',
assert.equal(this.player.autoplay(), 'any', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
// muted called twice here, as muted is value is restored on failure.
// The workflow described here:
// Call play() -> on rejection, attempt to set mute to true ->
// call play() again -> on rejection, set original mute value ->
// catch failure at the end of promise chain
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');