Skip to content

Commit f9fb1d3

Browse files
authoredJun 30, 2021
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.
1 parent 0f70787 commit f9fb1d3

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed
 

‎src/js/player.js

+12-7
Original file line numberDiff line numberDiff line change
@@ -1430,7 +1430,9 @@ class Player extends Component {
14301430
return;
14311431
}
14321432

1433-
const muted = () => {
1433+
// Save original muted() value, set muted to true, and attempt to play().
1434+
// On promise rejection, restore muted from saved value
1435+
const resolveMuted = () => {
14341436
const previouslyMuted = this.muted();
14351437

14361438
this.muted(true);
@@ -1448,21 +1450,24 @@ class Player extends Component {
14481450
return;
14491451
}
14501452

1451-
return mutedPromise.catch(restoreMuted);
1453+
return mutedPromise.catch(err => {
1454+
restoreMuted();
1455+
throw new Error(`Rejection at manualAutoplay. Restoring muted value. ${err ? err : ''}`);
1456+
});
14521457
};
14531458

14541459
let promise;
14551460

14561461
// if muted defaults to true
14571462
// the only thing we can do is call play
1458-
if (type === 'any' && this.muted() !== true) {
1463+
if (type === 'any' && !this.muted()) {
14591464
promise = this.play();
14601465

14611466
if (isPromise(promise)) {
1462-
promise = promise.catch(muted);
1467+
promise = promise.catch(resolveMuted);
14631468
}
1464-
} else if (type === 'muted' && this.muted() !== true) {
1465-
promise = muted();
1469+
} else if (type === 'muted' && !this.muted()) {
1470+
promise = resolveMuted();
14661471
} else {
14671472
promise = this.play();
14681473
}
@@ -1473,7 +1478,7 @@ class Player extends Component {
14731478

14741479
return promise.then(() => {
14751480
this.trigger({type: 'autoplay-success', autoplay: type});
1476-
}).catch((e) => {
1481+
}).catch(() => {
14771482
this.trigger({type: 'autoplay-failure', autoplay: type});
14781483
});
14791484
}

‎test/unit/autoplay.test.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,25 @@ QUnit.module('autoplay', {
2929

3030
fixture.appendChild(videoTag);
3131

32-
// this promise fake will act right away
33-
// it will also only act on catch calls
32+
// These mock promises immediately execute,
33+
// effectively synchronising promise chains for testing
34+
35+
// This will only act on catch calls
3436
this.rejectPromise = {
3537
then(fn) {
3638
return this;
3739
},
3840
catch(fn) {
39-
fn();
41+
try {
42+
fn();
43+
} catch (err) {
44+
return this;
45+
}
4046
return this;
4147
}
4248
};
4349

50+
// This will only act on then calls
4451
this.resolvePromise = {
4552
then(fn) {
4653
fn();
@@ -274,7 +281,10 @@ QUnit.test('option = "any" play, no muted, rejection leads to muted then play',
274281
assert.equal(this.player.autoplay(), 'any', 'player.autoplay getter');
275282
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
276283

277-
// muted called twice here, as muted is value is restored on failure.
284+
// The workflow described here:
285+
// Call play() -> on rejection, attempt to set mute to true ->
286+
// call play() again -> on rejection, set original mute value ->
287+
// catch failure at the end of promise chain
278288
this.player.tech_.trigger('loadstart');
279289
assert.equal(this.counts.play, 2, 'play count');
280290
assert.equal(this.counts.muted, 2, 'muted count');

0 commit comments

Comments
 (0)
Please sign in to comment.