1
0
mirror of https://github.com/videojs/video.js.git synced 2025-02-02 11:34:50 +02:00

refactor: Make registerComponent only work with Components (#3802)

Prevent techs (and others) from being registered via registerComponent.
* `registerComponent` now throws if given an object that is not a subclass of Component (or Component itself).
* `registerComponent` now throws if given a non-empty string as its name argument.

BREAKING CHANGE: registerComponent now throws if no name or not a component is passed in.
This commit is contained in:
Pat O'Neill 2017-01-18 00:46:43 -05:00 committed by Gary Katsevman
parent ce6acc832a
commit 57af15ce8b
6 changed files with 89 additions and 31 deletions

View File

@ -1520,15 +1520,34 @@ class Component {
* @param {string} name
* The name of the `Component` to register.
*
* @param {Component} comp
* @param {Component} ComponentToRegister
* The `Component` class to register.
*
* @return {Component}
* The `Component` that was registered.
*/
static registerComponent(name, comp) {
if (!name) {
return;
static registerComponent(name, ComponentToRegister) {
if (typeof name !== 'string' || !name) {
throw new Error(`Illegal component name, "${name}"; must be a non-empty string.`);
}
const Tech = Component.getComponent('Tech');
// We need to make sure this check is only done if Tech has been registered.
const isTech = Tech && Tech.isTech(ComponentToRegister);
const isComp = Component === ComponentToRegister ||
Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
if (isTech || !isComp) {
let reason;
if (isTech) {
reason = 'techs must be registered using Tech.registerTech()';
} else {
reason = 'must be a Component subclass';
}
throw new Error(`Illegal component, "${name}"; ${reason}.`);
}
name = toTitleCase(name);
@ -1537,23 +1556,26 @@ class Component {
Component.components_ = {};
}
if (name === 'Player' && Component.components_[name]) {
const Player = Component.components_[name];
const Player = Component.getComponent('Player');
if (name === 'Player' && Player) {
const players = Player.players;
const playerNames = Object.keys(players);
// If we have players that were disposed, then their name will still be
// in Players.players. So, we must loop through and verify that the value
// for each item is not null. This allows registration of the Player component
// after all players have been disposed or before any were created.
if (Player.players &&
Object.keys(Player.players).length > 0 &&
Object.keys(Player.players).map((playerName) => Player.players[playerName]).every(Boolean)) {
throw new Error('Can not register Player component after player has been created');
if (players &&
playerNames.length > 0 &&
playerNames.map((pname) => players[pname]).every(Boolean)) {
throw new Error('Can not register Player component after player has been created.');
}
}
Component.components_[name] = comp;
Component.components_[name] = ComponentToRegister;
return comp;
return ComponentToRegister;
}
/**
@ -1580,14 +1602,9 @@ class Component {
if (Component.components_ && Component.components_[name]) {
return Component.components_[name];
}
if (window && window.videojs && window.videojs[name]) {
log.warn(`The ${name} component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)`);
return window.videojs[name];
}
}
}
Component.registerComponent('Component', Component);
export default Component;

View File

@ -10,7 +10,6 @@ import * as Dom from '../utils/dom.js';
import * as Url from '../utils/url.js';
import { createTimeRange } from '../utils/time-ranges.js';
import FlashRtmpDecorator from './flash-rtmp';
import Component from '../component';
import window from 'global/window';
import {assign} from '../utils/obj';
@ -1075,6 +1074,5 @@ Flash.getEmbedCode = function(swf, flashVars, params, attributes) {
// Run Flash through the RTMP decorator
FlashRtmpDecorator(Flash);
Component.registerComponent('Flash', Flash);
Tech.registerTech('Flash', Flash);
export default Flash;

View File

@ -2,7 +2,6 @@
* @file html5.js
*/
import Tech from './tech.js';
import Component from '../component';
import * as Dom from '../utils/dom.js';
import * as Url from '../utils/url.js';
import * as Fn from '../utils/fn.js';
@ -1662,6 +1661,5 @@ Html5.nativeSourceHandler.dispose = function() {};
// Register the native source handler
Html5.registerSourceHandler(Html5.nativeSourceHandler);
Component.registerComponent('Html5', Html5);
Tech.registerTech('Html5', Html5);
export default Html5;

View File

@ -1178,9 +1178,9 @@ Tech.withSourceHandlers = function(_Tech) {
};
// The base Tech class needs to be registered as a Component. It is the only
// Tech that can be registered as a Component.
Component.registerComponent('Tech', Tech);
// Old name for Tech
// @deprecated
Component.registerComponent('MediaTechController', Tech);
Tech.registerTech('Tech', Tech);
export default Tech;

View File

@ -41,6 +41,44 @@ const getFakePlayer = function() {
};
};
QUnit.test('registerComponent() throws with bad arguments', function(assert) {
assert.throws(
function() {
Component.registerComponent(null);
},
new Error('Illegal component name, "null"; must be a non-empty string.'),
'component names must be non-empty strings'
);
assert.throws(
function() {
Component.registerComponent('');
},
new Error('Illegal component name, ""; must be a non-empty string.'),
'component names must be non-empty strings'
);
assert.throws(
function() {
Component.registerComponent('TestComponent5', function() {});
},
new Error('Illegal component, "TestComponent5"; must be a Component subclass.'),
'components must be subclasses of Component'
);
assert.throws(
function() {
const Tech = Component.getComponent('Tech');
class DummyTech extends Tech {}
Component.registerComponent('TestComponent5', DummyTech);
},
new Error('Illegal component, "TestComponent5"; techs must be registered using Tech.registerTech().'),
'components must be subclasses of Component'
);
});
QUnit.test('should create an element', function(assert) {
const comp = new Component(getFakePlayer(), {});
@ -635,7 +673,7 @@ QUnit.test('should use a defined content el for appending children', function(as
class CompWithContent extends Component {}
CompWithContent.prototype.createEl = function() {
// Create the main componenent element
// Create the main component element
const el = Dom.createEl('div');
// Create the element where children will be appended

View File

@ -213,7 +213,6 @@ QUnit.test('if native text tracks are not supported, create a texttrackdisplay',
const oldTestVid = Html5.TEST_VID;
const oldIsFirefox = browser.IS_FIREFOX;
const oldTextTrackDisplay = Component.getComponent('TextTrackDisplay');
let called = false;
const tag = document.createElement('video');
const track1 = document.createElement('track');
const track2 = document.createElement('track');
@ -235,13 +234,21 @@ QUnit.test('if native text tracks are not supported, create a texttrackdisplay',
};
browser.IS_FIREFOX = true;
Component.registerComponent('TextTrackDisplay', function() {
called = true;
});
const fakeTTDSpy = sinon.spy();
class FakeTTD extends Component {
constructor() {
super();
fakeTTDSpy();
}
}
Component.registerComponent('TextTrackDisplay', FakeTTD);
const player = TestHelpers.makePlayer({}, tag);
assert.ok(called, 'text track display was created');
assert.strictEqual(fakeTTDSpy.callCount, 1, 'text track display was created');
Html5.TEST_VID = oldTestVid;
browser.IS_FIREFOX = oldIsFirefox;