diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js
index d860d4a24..587b62f9e 100644
--- a/src/js/tech/html5.js
+++ b/src/js/tech/html5.js
@@ -919,13 +919,15 @@ Html5.canControlPlaybackRate = function() {
* - False otherwise
*/
Html5.canOverrideAttributes = function() {
- // if we cannot overwrite the src property, there is no support
+ // if we cannot overwrite the src/innerHTML property, there is no support
// iOS 7 safari for instance cannot do this.
try {
const noop = () => {};
Object.defineProperty(document.createElement('video'), 'src', {get: noop, set: noop});
Object.defineProperty(document.createElement('audio'), 'src', {get: noop, set: noop});
+ Object.defineProperty(document.createElement('video'), 'innerHTML', {get: noop, set: noop});
+ Object.defineProperty(document.createElement('audio'), 'innerHTML', {get: noop, set: noop});
} catch (e) {
return false;
}
diff --git a/src/js/tech/setup-sourceset.js b/src/js/tech/setup-sourceset.js
index 5bbd54c0a..73ad56488 100644
--- a/src/js/tech/setup-sourceset.js
+++ b/src/js/tech/setup-sourceset.js
@@ -1,21 +1,152 @@
import window from 'global/window';
+import document from 'global/document';
import mergeOptions from '../utils/merge-options';
-const setupSourceset = function(tech) {
+/**
+ * This function is used to fire a sourceset when there is something
+ * similar to `mediaEl.load()` being called. It will try to find the source via
+ * the `src` attribute and then the `` elements. It will then fire `sourceset`
+ * with the source that was found or empty string if we cannot know. If it cannot
+ * find a source then `sourceset` will not be fired.
+ *
+ * @param {Html5} tech
+ * The tech object that sourceset was setup on
+ *
+ * @return {boolean}
+ * returns false if the sourceset was not fired and true otherwise.
+ */
+const sourcesetLoad = (tech) => {
+ const el = tech.el();
- if (!tech.featuresSourceset) {
+ // if `el.src` is set, that source will be loaded.
+ if (el.src) {
+ tech.triggerSourceset(el.src);
+ return true;
+ }
+
+ /**
+ * Since there isn't a src property on the media element, source elements will be used for
+ * implementing the source selection algorithm. This happens asynchronously and
+ * for most cases were there is more than one source we cannot tell what source will
+ * be loaded, without re-implementing the source selection algorithm. At this time we are not
+ * going to do that. There are three special cases that we do handle here though:
+ *
+ * 1. If there are no sources, do not fire `sourceset`.
+ * 2. If there is only one `` with a `src` property/attribute that is our `src`
+ * 3. If there is more than one `` but all of them have the same `src` url.
+ * That will be our src.
+ */
+ const sources = tech.$$('source');
+ const srcUrls = [];
+ let src = '';
+
+ // if there are no sources, do not fire sourceset
+ if (!sources.length) {
+ return false;
+ }
+
+ // only count valid/non-duplicate source elements
+ for (let i = 0; i < sources.length; i++) {
+ const url = sources[i].src;
+
+ if (url && srcUrls.indexOf(url) === -1) {
+ srcUrls.push(url);
+ }
+ }
+
+ // there were no valid sources
+ if (!srcUrls.length) {
return;
}
- const el = tech.el();
-
- // we need to fire sourceset when the player is ready
- // if we find that the media element had a src when it was
- // given to us and that tech element is not in a stalled state
- if (el.src || el.currentSrc && tech.el().initNetworkState_ !== 3) {
- tech.triggerSourceset(el.src || el.currentSrc);
+ // there is only one valid source element url
+ // use that
+ if (srcUrls.length === 1) {
+ src = srcUrls[0];
}
+ tech.triggerSourceset(src);
+ return true;
+};
+
+/**
+ * Get the browsers property descriptor for the `innerHTML`
+ * property. This will allow us to overwrite it without
+ * destroying native functionality.
+ *
+ * @param {HTMLMediaElement} el
+ * The tech element that should be used to get the descriptor
+ *
+ * @return {Object}
+ * The property descriptor for innerHTML.
+ */
+const getInnerHTMLDescriptor = (el) => {
+ const proto = window.Element.prototype;
+ let innerDescriptor = {};
+
+ // preserve getters/setters already on `el.innerHTML` if they exist
+ if (Object.getOwnPropertyDescriptor(el, 'innerHTML')) {
+ innerDescriptor = Object.getOwnPropertyDescriptor(el, 'innerHTML');
+ } else if (Object.getOwnPropertyDescriptor(proto, 'innerHTML')) {
+ innerDescriptor = Object.getOwnPropertyDescriptor(proto, 'innerHTML');
+ }
+
+ if (!innerDescriptor.get) {
+ innerDescriptor.get = function() {
+ return el.cloneNode().innerHTML;
+ };
+ }
+
+ if (!innerDescriptor.set) {
+ innerDescriptor.set = function(v) {
+ // remove all current content from inside
+ el.innerText = '';
+
+ // make a dummy node to use innerHTML on
+ const dummy = document.createElement(el.nodeName.toLowerCase());
+
+ // set innerHTML to the value provided
+ dummy.innerHTML = v;
+
+ // make a document fragment to hold the nodes from dummy
+ const docFrag = document.createDocumentFragment();
+
+ // copy all of the nodes created by the innerHTML on dummy
+ // to the document fragment
+ while (dummy.childNodes.length) {
+ docFrag.appendChild(dummy.childNodes[0]);
+ }
+
+ // now we add all of that html in one by appending the
+ // document fragment. This is how innerHTML does it.
+ window.Element.prototype.appendChild.call(el, docFrag);
+
+ // then return the result that innerHTML's setter would
+ return el.innerHTML;
+ };
+ }
+
+ if (typeof innerDescriptor.enumerable === 'undefined') {
+ innerDescriptor.enumerable = true;
+ }
+
+ innerDescriptor.configurable = true;
+
+ return innerDescriptor;
+};
+
+/**
+ * Get the browsers property descriptor for the `src`
+ * property. This will allow us to overwrite it without
+ * destroying native functionality.
+ *
+ * @param {HTMLMediaElement} el
+ * The tech element that should be used to get the descriptor
+ *
+ * @return {Object}
+ * The property descriptor for `src`.
+ */
+const getSrcDescriptor = (el) => {
const proto = window.HTMLMediaElement.prototype;
let srcDescriptor = {};
@@ -42,12 +173,158 @@ const setupSourceset = function(tech) {
srcDescriptor.enumerable = true;
}
+ srcDescriptor.configurable = true;
+
+ return srcDescriptor;
+};
+
+/**
+ * Patches browser internal functions so that we can tell syncronously
+ * if a `` was appended to the media element. For some reason this
+ * causes a `sourceset` if the the media element is ready and has no source.
+ * This happens when:
+ * - The page has just loaded and the media element does not have a source.
+ * - The media element was emptied of all sources, then `load()` was called.
+ *
+ * It does this by patching the following functions/properties when they are supported:
+ *
+ * - `append()` - can be used to add a `` element to the media element
+ * - `appendChild()` - can be used to add a `` element to the media element
+ * - `insertAdjacentHTML()` - can be used to add a `` element to the media element
+ * - `innerHTML` - can be used to add a `` element to the media element
+ *
+ * @param {Html5} tech
+ * The tech object that sourceset is being setup on.
+ */
+const firstSourceWatch = function(tech) {
+ const el = tech.el();
+
+ // make sure firstSourceWatch isn't setup twice.
+ if (el.firstSourceWatch_) {
+ return;
+ }
+
+ el.firstSourceWatch_ = true;
+ const oldAppend = el.append;
+ const oldAppendChild = el.appendChild;
+ const oldInsertAdjacentHTML = el.insertAdjacentHTML;
+ const innerDescriptor = getInnerHTMLDescriptor(el);
+
+ el.appendChild = function() {
+ const retval = oldAppendChild.apply(el, arguments);
+
+ sourcesetLoad(tech);
+
+ return retval;
+ };
+
+ if (oldAppend) {
+ el.append = function() {
+ const retval = oldAppend.apply(el, arguments);
+
+ sourcesetLoad(tech);
+
+ return retval;
+ };
+ }
+
+ if (oldInsertAdjacentHTML) {
+ el.insertAdjacentHTML = function() {
+ const retval = oldInsertAdjacentHTML.apply(el, arguments);
+
+ sourcesetLoad(tech);
+
+ return retval;
+ };
+ }
+
+ Object.defineProperty(el, 'innerHTML', {
+ get: innerDescriptor.get.bind(el),
+ set(v) {
+ const retval = innerDescriptor.set.call(el, v);
+
+ sourcesetLoad(tech);
+
+ return retval;
+ },
+ configurable: true,
+ enumerable: innerDescriptor.enumerable
+ });
+
+ // on the first sourceset, we need to revert
+ // our changes
+ tech.one('sourceset', (e) => {
+ el.firstSourceWatch_ = false;
+ el.appendChild = oldAppendChild;
+
+ if (oldAppend) {
+ el.append = oldAppend;
+ }
+ if (oldInsertAdjacentHTML) {
+ el.insertAdjacentHTML = oldInsertAdjacentHTML;
+ }
+
+ Object.defineProperty(el, 'innerHTML', innerDescriptor);
+ });
+};
+
+/**
+ * setup `sourceset` handling on the `Html5` tech. This function
+ * patches the following element properties/functions:
+ *
+ * - `src` - to determine when `src` is set
+ * - `setAttribute()` - to determine when `src` is set
+ * - `load()` - this re-triggers the source selection algorithm, and can
+ * cause a sourceset.
+ *
+ * If there is no source when we are adding `sourceset` support or during a `load()`
+ * we also patch the functions listed in `firstSourceWatch`.
+ *
+ * @param {Html5} tech
+ * The tech to patch
+ */
+const setupSourceset = function(tech) {
+ if (!tech.featuresSourceset) {
+ return;
+ }
+
+ const el = tech.el();
+
+ // make sure sourceset isn't setup twice.
+ if (el.setupSourceset_) {
+ return;
+ }
+
+ el.setupSourceset_ = true;
+
+ const srcDescriptor = getSrcDescriptor(el);
+ const oldSetAttribute = el.setAttribute;
+ const oldLoad = el.load;
+
+ // we need to fire sourceset when the player is ready
+ // if we find that the media element had a src when it was
+ // given to us and that tech element is not in a stalled state
+ if (el.src || el.currentSrc && el.initNetworkState_ !== 3) {
+ if (el.currentSrc) {
+ tech.triggerSourceset(el.currentSrc);
+ } else {
+ sourcesetLoad(tech);
+ }
+ }
+
+ // for some reason adding a source element when a mediaElement has no source
+ // calls `load` internally right away. We need to handle that.
+ if (!el.src && !el.currentSrc && !tech.$$('source').length) {
+ firstSourceWatch(tech);
+ }
+
Object.defineProperty(el, 'src', {
get: srcDescriptor.get.bind(el),
set: (v) => {
const retval = srcDescriptor.set.call(el, v);
- tech.triggerSourceset(v);
+ // we use the getter here to get the actual value set on src
+ tech.triggerSourceset(el.src);
return retval;
},
@@ -55,29 +332,26 @@ const setupSourceset = function(tech) {
enumerable: srcDescriptor.enumerable
});
- const oldSetAttribute = el.setAttribute;
-
el.setAttribute = (n, v) => {
const retval = oldSetAttribute.call(el, n, v);
if (n === 'src') {
- tech.triggerSourceset(v);
+ tech.triggerSourceset(el.getAttribute('src'));
}
return retval;
};
- const oldLoad = el.load;
-
el.load = () => {
const retval = oldLoad.call(el);
- // if `el.src` is set, that source will be loaded
- // otherwise, we can't know for sure what source will be set because
- // source elements will be used but implementing the source selection algorithm
- // is laborious and asynchronous, so,
- // instead return an empty string to basically indicate source may change
- tech.triggerSourceset(el.src || '');
+ // if load was called, but there was no source to fire
+ // sourceset on. We have to watch for a source append
+ // as that can trigger a `sourceset` when the media element
+ // has no source
+ if (!sourcesetLoad(tech)) {
+ firstSourceWatch(tech);
+ }
return retval;
};
diff --git a/test/unit/sourceset.test.js b/test/unit/sourceset.test.js
index d2ed1d2ed..ad2d7385f 100644
--- a/test/unit/sourceset.test.js
+++ b/test/unit/sourceset.test.js
@@ -161,51 +161,6 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
});
- QUnit.test('player.src({...}) one source', function(assert) {
- const done = assert.async();
-
- this.player = videojs(this.mediaEl, {
- enableSourceset: true
- });
- this.player.one('sourceset', () => {
- validateSource(assert, this.player, [this.testSrc]);
- done();
- });
-
- this.player.src(this.testSrc);
- });
-
- QUnit.test('player.src({...}) preload auto', function(assert) {
- const done = assert.async();
-
- this.mediaEl.setAttribute('preload', 'auto');
- this.player = videojs(this.mediaEl, {
- enableSourceset: true
- });
-
- this.player.one('sourceset', () => {
- validateSource(assert, this.player, [this.testSrc]);
- done();
- });
-
- this.player.src(this.testSrc);
- });
-
- QUnit.test('player.src({...}) two sources', function(assert) {
- const done = assert.async();
-
- this.player = videojs(this.mediaEl, {
- enableSourceset: true
- });
-
- this.player.one('sourceset', () => {
- validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
- done();
- });
-
- this.player.src([this.sourceOne, this.sourceTwo]);
- });
-
QUnit.test('mediaEl.src = ...;', function(assert) {
const done = assert.async();
@@ -292,6 +247,320 @@ QUnit[qunitFn]('sourceset', function(hooks) {
});
}));
+ QUnit.module('source after player', (subhooks) => testTypes.forEach((testName) => {
+ QUnit.module(testName, {
+ beforeEach() {
+ sinon.stub(log, 'error');
+
+ setupEnv(this, testName);
+ },
+ afterEach: setupAfterEach(1)
+ });
+
+ QUnit.test('player.src({...}) one source', function(assert) {
+ const done = assert.async();
+
+ this.player = videojs(this.mediaEl, {
+ enableSourceset: true
+ });
+ this.player.one('sourceset', () => {
+ validateSource(assert, this.player, [this.testSrc]);
+ done();
+ });
+
+ this.player.src(this.testSrc);
+ });
+
+ QUnit.test('player.src({...}) preload auto', function(assert) {
+ const done = assert.async();
+
+ this.mediaEl.setAttribute('preload', 'auto');
+ this.player = videojs(this.mediaEl, {
+ enableSourceset: true
+ });
+
+ this.player.one('sourceset', () => {
+ validateSource(assert, this.player, [this.testSrc]);
+ done();
+ });
+
+ this.player.src(this.testSrc);
+ });
+
+ QUnit.test('player.src({...}) two sources', function(assert) {
+ const done = assert.async();
+
+ this.player = videojs(this.mediaEl, {
+ enableSourceset: true
+ });
+
+ this.player.one('sourceset', () => {
+ validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
+ done();
+ });
+
+ this.player.src([this.sourceOne, this.sourceTwo]);
+ });
+
+ QUnit.test('mediaEl.src = ...;', function(assert) {
+ const done = assert.async();
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ validateSource(assert, this.player, [this.testSrc]);
+ done();
+ });
+
+ this.player.tech_.el_.src = this.testSrc.src;
+ });
+
+ QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) {
+ const done = assert.async();
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ validateSource(assert, this.player, [this.testSrc]);
+ done();
+ });
+
+ this.player.tech_.el_.setAttribute('src', this.testSrc.src);
+ });
+
+ const appendTypes = [
+ {name: 'appendChild', fn: (el, obj) => el.appendChild(obj)},
+ {name: 'innerHTML', fn: (el, obj) => {el.innerHTML = obj.outerHTML;}}, // eslint-disable-line
+ ];
+
+ // ie does not support this and safari < 10 does not either
+ if (window.Element.prototype.append) {
+ appendTypes.push({name: 'append', fn: (el, obj) => el.append(obj)});
+ }
+
+ if (window.Element.prototype.insertAdjacentHTML) {
+ appendTypes.push({name: 'insertAdjacentHTML', fn: (el, obj) => el.insertAdjacentHTML('afterbegin', obj.outerHTML)});
+ }
+
+ appendTypes.forEach((appendObj) => {
+
+ QUnit.test(` one source through ${appendObj.name}`, function(assert) {
+ const done = assert.async();
+
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ assert.equal(e.src, this.testSrc.src, 'source is as expected');
+ done();
+ });
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+ });
+
+ QUnit.test(` one source through ${appendObj.name} and load`, function(assert) {
+ const done = assert.async();
+
+ this.totalSourcesets = 2;
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e1) => {
+ assert.equal(e1.src, this.testSrc.src, 'event has expected source');
+
+ this.player.one('sourceset', (e2) => {
+ assert.equal(e2.src, this.testSrc.src, 'second event has expected source');
+ done();
+ });
+ });
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+
+ // should fire an additional sourceset
+ this.player.tech_.el_.load();
+ });
+
+ QUnit.test(`one through ${appendObj.name} and then mediaEl.src`, function(assert) {
+ const done = assert.async();
+
+ this.totalSourcesets = 2;
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ assert.equal(e.src, this.testSrc.src, 'source is as expected');
+
+ this.player.one('sourceset', (e2) => {
+ validateSource(assert, this.player, [this.sourceOne]);
+
+ done();
+ });
+ });
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+
+ // should fire an additional sourceset
+ this.player.tech_.el_.src = this.sourceOne.src;
+ });
+
+ QUnit.test(`one through ${appendObj.name} and then mediaEl.setAttribute`, function(assert) {
+ const done = assert.async();
+
+ this.totalSourcesets = 2;
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ assert.equal(e.src, this.testSrc.src, 'source is as expected');
+
+ this.player.one('sourceset', (e2) => {
+ validateSource(assert, this.player, [this.sourceOne]);
+
+ done();
+ });
+ });
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+
+ // should fire an additional sourceset
+ this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+ });
+
+ QUnit.test(`mediaEl.src and then through ${appendObj.name}`, function(assert) {
+ const done = assert.async();
+
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ validateSource(assert, this.player, [this.sourceOne]);
+
+ done();
+ });
+
+ this.player.tech_.el_.src = this.sourceOne.src;
+
+ // should not fire sourceset
+ appendObj.fn(this.player.tech_.el_, this.source);
+ });
+
+ QUnit.test(`mediaEl.setAttribute and then through ${appendObj.name}`, function(assert) {
+ const done = assert.async();
+
+ this.source = document.createElement('source');
+ this.source.src = this.testSrc.src;
+ this.source.type = this.testSrc.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ validateSource(assert, this.player, [this.sourceOne]);
+
+ done();
+ });
+
+ this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+
+ // should not fire sourceset
+ appendObj.fn(this.player.tech_.el_, this.source);
+ });
+
+ QUnit.test(` two sources through ${appendObj.name}`, function(assert) {
+ const done = assert.async();
+
+ this.source = document.createElement('source');
+ this.source.src = this.sourceOne.src;
+ this.source.type = this.sourceOne.type;
+
+ this.source2 = document.createElement('source');
+ this.source2.src = this.sourceTwo.src;
+ this.source2.type = this.sourceTwo.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e) => {
+ assert.equal(e.src, this.sourceOne.src, 'source is as expected');
+ done();
+ });
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+
+ // this should not be in the source list or fire a sourceset
+ appendObj.fn(this.player.tech_.el_, this.source2);
+ });
+
+ QUnit.test(`set, remove, load, and set again through ${appendObj.name}`, function(assert) {
+ const done = assert.async();
+
+ this.totalSourcesets = 2;
+ this.source = document.createElement('source');
+ this.source.src = this.sourceTwo.src;
+ this.source.type = this.sourceTwo.type;
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+
+ this.player.one('sourceset', (e1) => {
+ validateSource(assert, this.player, [this.sourceOne]);
+
+ this.player.one('sourceset', (e2) => {
+ validateSource(assert, this.player, [this.sourceTwo], false);
+ done();
+ });
+
+ // reset to no source
+ this.player.tech_.el_.removeAttribute('src');
+ this.player.tech_.el_.load();
+
+ // since the media el has no source, just appending will
+ // change the source without calling load
+ appendObj.fn(this.player.tech_.el_, this.source);
+
+ });
+
+ this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
+ });
+ });
+
+ QUnit.test('no source and load', function(assert) {
+ const done = assert.async();
+
+ this.player = videojs(this.mediaEl, {enableSourceset: true});
+ this.player.tech_.el_.load();
+
+ this.totalSourcesets = 0;
+
+ window.setTimeout(() => {
+ assert.equal(this.sourcesets, 0, 'no sourceset');
+ done();
+ }, wait);
+ });
+ }));
+
QUnit.module('source change', (subhooks) => testTypes.forEach((testName) => {
QUnit.module(testName, {
beforeEach(assert) {
@@ -451,10 +720,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
this.mediaEl.removeAttribute('src');
this.player.one('sourceset', (e1) => {
- assert.equal(e1.src, '', 'we got a sourceset with an empty src');
+ assert.equal(e1.src, this.testSrc.src, 'we got a sourceset with the expected src');
this.player.one('sourceset', (e2) => {
- assert.equal(e2.src, '', 'we got a sourceset with an empty src');
+ assert.equal(e2.src, this.sourceOne.src, 'we got a sourceset with the expected src');
});
source.src = this.sourceOne.src;
@@ -474,10 +743,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
source.type = this.sourceOne.type;
this.player.one('sourceset', (e1) => {
- assert.equal(e1.src, '', 'we got a sourceset with an empty src');
+ assert.equal(e1.src, this.sourceOne.src, 'we got a sourceset with the expected src');
this.player.one('sourceset', (e2) => {
- assert.equal(e2.src, '', 'we got a sourceset with an empty src');
+ assert.equal(e2.src, this.sourceTwo.src, 'we got a sourceset with the expected src');
});
});
@@ -538,7 +807,7 @@ QUnit[qunitFn]('sourceset', function(hooks) {
src: 'http://example.com/oceans.flv',
type: 'video/flv'
};
- let sourcesets = 0;
+ const sourcesets = [];
class FakeFlash extends Html5 {
static isSupported() {
@@ -563,38 +832,27 @@ QUnit[qunitFn]('sourceset', function(hooks) {
techOrder: ['fakeFlash', 'html5']
});
- player.src(flashSrc);
-
player.ready(function() {
- // the first sourceset comes from our FakeFlash because it extends Html5 tech
- // which calls load() on dispose for various reasons
- player.one('sourceset', function(e1) {
- // ignore the first sourceset that gets called when disposing the original tech
+ // the first sourceset ends up being the second source because when the first source is set
+ // the tech isn't ready so we delay it, then the second source comes and the tech is ready
+ // so it ends up being triggered immediately.
+ player.on('sourceset', (e) => {
+ sourcesets.push(e.src);
- // the second sourceset ends up being the second source because when the first source is set
- // the tech isn't ready so we delay it, then the second source comes and the tech is ready
- // so it ends up being triggered immediately.
- player.one('sourceset', function(e2) {
- assert.equal(e2.src, sourceTwo.src, 'the second sourceset ends up being the second source');
- sourcesets++;
+ if (sourcesets.length === 3) {
+ assert.deepEqual([flashSrc.src, sourceTwo.src, sourceOne.src], sourcesets, 'sourceset as expected');
- // now that the tech is ready, we will re-trigger the original sourceset event
- // and get the first source
- player.one('sourceset', function(e3) {
- assert.equal(e3.src, sourceOne.src, 'the third sourceset is the first source');
- sourcesets++;
-
- assert.equal(sourcesets, 2, 'two sourcesets');
- player.dispose();
- delete Tech.techs_.FakeFlash;
- done();
- });
- });
+ player.dispose();
+ delete Tech.techs_.FakeFlash;
+ done();
+ }
});
player.src(sourceOne);
player.src(sourceTwo);
});
+ player.src(flashSrc);
+
});
});