Skip to content

Commit

Permalink
Fixed bug in Safari 9 where video wouldn't play sometimes:
Browse files Browse the repository at this point in the history
- deferred resolution of youtube player load returned promise
  • Loading branch information
Jonathan Raoult committed Oct 22, 2015
1 parent cea4465 commit a947667
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 40 deletions.
8 changes: 7 additions & 1 deletion src/main/playerYoutube.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var animationGroup = require('./animationGroup'),
animationFade = require('./animationFade'),
isNumber = require('lodash/lang/isNumber'),
has = require('lodash/object/has'),
defer = require('lodash/function/defer'),
EventEmitter = require('events').EventEmitter;

var _ytApiPromise = new Promise(function ytApiPromiseExecutor(resolve, reject) {
Expand Down Expand Up @@ -175,7 +176,12 @@ function playerYoutube(config) {
if (evt.data === YT.PlayerState.PLAYING) {
unbindLoadListeners();
ytPlayer.pauseVideo();
resolve();

// sometimes videos are not playing in Safari 9 until a re-layout is triggered
// seems like resolving the load promise in the next macro task fixes it
defer(function() {
resolve();
});
}
}

Expand Down
10 changes: 3 additions & 7 deletions src/test/unit/defer.js → src/test/unit/enqueueMicrotask.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,21 @@ var isFunction = require('lodash/lang/isFunction'),
slice = require('lodash/array/slice');

/**
* Defers executing the `func` function until the current call stack has cleared.
* Defers executing the `func` function until the current microtasks stack has cleared.
* Additional arguments will be provided to `func` when it is invoked.
*
* It is a version inspired from the lodash one but based on Promise instead of timeout which
* is convenient for test when the clock is mocked and manipulated.
*
* @param {Function} func The function to defer.
* @param {...*} [arg] Arguments to invoke the function with.
* @example
*
* _.defer(function(text) { console.log(text); }, 'deferred');
* // logs 'deferred' after one or more milliseconds
*/
function defer(func) {
function enqueueMicrotask(func) {
if (!isFunction(func)) {
throw new TypeError();
}
var args = slice(arguments, 1);
Promise.resolve().then(function() { func.apply(undefined, args); });
}

module.exports = defer;
module.exports = enqueueMicrotask;
10 changes: 5 additions & 5 deletions src/test/unit/playbackSlotSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

var playbackSlot = require('../../main/playbackSlot'),
playersPoolMock = require('./playersPoolMock'),
defer = require('./defer'),
enqueueMicrotask = require('./enqueueMicrotask'),
defaults = require('lodash/object/defaults'),
constant = require('lodash/utility/constant'),
identity = require('lodash/utility/identity'),
Expand Down Expand Up @@ -147,7 +147,7 @@ describe('A player slot', function() {
var config = {audioGain: 0};
slot.start(config);

defer(function() {
enqueueMicrotask(function() {
expect(playSpy).not.toHaveBeenCalled();

done();
Expand Down Expand Up @@ -176,7 +176,7 @@ describe('A player slot', function() {

slot.end();

defer(function() {
enqueueMicrotask(function() {
expect(fadeOutSpy).not.toHaveBeenCalled();

done();
Expand Down Expand Up @@ -298,7 +298,7 @@ describe('A player slot', function() {

slot.load().then(function() {
slot.start();
defer(function() {
enqueueMicrotask(function() {
slot.end().then(function() {

expect(fadeOutSpy).toHaveBeenCalled();
Expand Down Expand Up @@ -355,7 +355,7 @@ describe('A player slot', function() {

slot.start();

defer(function() {
enqueueMicrotask(function() {
// we are going to execute 3 "cues handler" cycles each time with a different currentTime value

playerProps.currentTime = playerProps.duration * 1 / 4;
Expand Down
54 changes: 27 additions & 27 deletions src/test/unit/sequencerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

var sequencer = require('../../main/sequencer'),
playbackSlotMock = require('./playbackSlotMock'),
defer = require('./defer'),
enqueueMicrotask = require('./enqueueMicrotask'),
describe = jasmine.getEnv().describe,
beforeEach = jasmine.getEnv().beforeEach,
it = jasmine.getEnv().it,
Expand Down Expand Up @@ -86,7 +86,7 @@ describe('A sequencer', function() {

seq.play();

defer(function() {
enqueueMicrotask(function() {
expect(nextEntryProducerSpy).not.toHaveBeenCalledWith(null);

done();
Expand Down Expand Up @@ -137,13 +137,13 @@ describe('A sequencer', function() {
_entries[4]
]);

defer(done);
enqueueMicrotask(done);
});

seq.skip(_entries[0]);
seq.play();

defer(function() {
enqueueMicrotask(function() {
seq.skip(_entries[3]);
});
});
Expand All @@ -169,9 +169,9 @@ describe('A sequencer', function() {

// fake auto ending
slot.start.and.callFake(function() {
defer(function() {
enqueueMicrotask(function() {
producerCfg.endingSoon();
defer(function() {
enqueueMicrotask(function() {
producerCfg.ending();
});
});
Expand Down Expand Up @@ -211,7 +211,7 @@ describe('A sequencer', function() {
new Promise(function(resolve) {
(function deferredWhile(idx) {
if (idx < _entries.length) {
defer(function() {
enqueueMicrotask(function() {
seq.skip(_entries[idx]);
deferredWhile(idx + 1);
});
Expand Down Expand Up @@ -250,8 +250,8 @@ describe('A sequencer', function() {
var slot = seqDefaultCfg.playbackSlotProducer(producerCfg);
slot.end.and.callFake(function() {
// doubled defer to simulate how actual implementation behaves
defer(function() {
defer(function() {
enqueueMicrotask(function() {
enqueueMicrotask(function() {
producerCfg.ending();
});
});
Expand All @@ -266,7 +266,7 @@ describe('A sequencer', function() {
seq.skip(_entries[0]);
seq.play();

defer(function() {
enqueueMicrotask(function() {
seq.skip(_entries[1]);
});

Expand All @@ -286,7 +286,7 @@ describe('A sequencer', function() {
slot = seqDefaultCfg.playbackSlotProducer(producerCfg);

slot.load.and.callFake(function() {
defer(runChecks);
enqueueMicrotask(runChecks);
return Promise.resolve();
});

Expand Down Expand Up @@ -323,7 +323,7 @@ describe('A sequencer', function() {
var step = steps[slotsIdx];

slot.load.and.callFake(function() {
defer(step);
enqueueMicrotask(step);
return Promise.resolve();
});

Expand All @@ -345,7 +345,7 @@ describe('A sequencer', function() {
function step2() {
seq.play();

defer(function() {
enqueueMicrotask(function() {

expect(slots[0].proceed).not.toHaveBeenCalled();
expect(slots[1].proceed).toHaveBeenCalled();
Expand Down Expand Up @@ -381,10 +381,10 @@ describe('A sequencer', function() {
seq.play();
seq.skip(_entries[0]);

defer(function() {
enqueueMicrotask(function() {
seq.skip(_entries[1]);

defer(function() {
enqueueMicrotask(function() {
seq.stop();
});
});
Expand All @@ -411,7 +411,7 @@ describe('A sequencer', function() {
slots.push(slot);

if (slot.entry === _entries[2]) {
defer(function() {
enqueueMicrotask(function() {
runChecks();
done();
});
Expand All @@ -425,7 +425,7 @@ describe('A sequencer', function() {
seq.play();
seq.skip(entries[0]);

defer(function() {
enqueueMicrotask(function() {
// remove the next
pullAt(entries, 1);
seq.checkNextEntry();
Expand Down Expand Up @@ -455,7 +455,7 @@ describe('A sequencer', function() {
slots.push(slot);

if (slot.entry === _entries[1]) {
defer(function() {
enqueueMicrotask(function() {
runChecks();
done();
});
Expand All @@ -469,7 +469,7 @@ describe('A sequencer', function() {
seq.play();
seq.skip(entries[0]);

defer(function() {
enqueueMicrotask(function() {
// add the next
entries.push(_entries[1]);
seq.checkNextEntry();
Expand Down Expand Up @@ -498,7 +498,7 @@ describe('A sequencer', function() {
slots.push(slot);

if (slot.entry === _entries[1]) {
defer(function() {
enqueueMicrotask(function() {
runChecks();
done();
});
Expand All @@ -512,7 +512,7 @@ describe('A sequencer', function() {
seq.play();
seq.skip(entries[0]);

defer(function() {
enqueueMicrotask(function() {
seq.checkNextEntry();
});

Expand Down Expand Up @@ -574,7 +574,7 @@ describe('A sequencer', function() {
} else {
slot.start.and.callFake(function() {
startedSlot = slot;
defer(runChecks);
enqueueMicrotask(runChecks);
});
}
return slot;
Expand All @@ -601,15 +601,15 @@ describe('A sequencer', function() {
var slot = seqDefaultCfg.playbackSlotProducer(producerCfg);
if (slot.entry === _entries[0]) {
slot.start.and.callFake(function() {
defer(producerCfg.ending);
enqueueMicrotask(producerCfg.ending);
});
} else if (contains(_entries.slice(1, lastFailingEntryIdx + 1), slot.entry)) {
// make the all slots for the entries from 1 to 3 failing on load
slot.load.and.returnValue(Promise.reject());
} else {
slot.start.and.callFake(function() {
startedSlot = slot;
defer(runChecks);
enqueueMicrotask(runChecks);
});
}
return slot;
Expand Down Expand Up @@ -664,7 +664,7 @@ describe('A sequencer', function() {
var slot = seqDefaultCfg.playbackSlotProducer(producerCfg);
if (slot.entry === _entries[0]) {
slot.start.and.callFake(function() {
defer(producerCfg.ending);
enqueueMicrotask(producerCfg.ending);
});
} else {
// make all the slots for the other entries failing on load
Expand Down Expand Up @@ -697,7 +697,7 @@ describe('A sequencer', function() {

seq.play();
seq.skip(_entries[0]);
defer(function() {
enqueueMicrotask(function() {
seq.skip(_entries[1]);
});

Expand Down Expand Up @@ -736,7 +736,7 @@ describe('A sequencer', function() {

seq.play();
seq.skip(_entries[0]);
defer(function() {
enqueueMicrotask(function() {
// entry 1 will fail to load and the sequencer will try entry 2
seq.skip(_entries[1]);
});
Expand Down

0 comments on commit a947667

Please sign in to comment.