From ac0b03f2f711bb96aad6f71ae2435c1e11ae18d2 Mon Sep 17 00:00:00 2001 From: Pat O'Neill Date: Fri, 23 Dec 2016 11:30:49 -0500 Subject: [PATCH] fix: Support require()-ing video.js (#3889) Introduce the Dom.isReal() function, which makes an educated assumption about the "realness" of the document object. Wrap code here and there with checks against Dom.isReal() as well as other defensive code. Fixes #3869. --- src/js/setup.js | 11 ++++++++-- src/js/tech/html5.js | 45 ++++++++++++++++++++++------------------- src/js/utils/browser.js | 12 ++++++++--- src/js/utils/dom.js | 9 +++++++++ src/js/video.js | 6 ++---- 5 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/js/setup.js b/src/js/setup.js index c63e794a7c..b9278e7f09 100644 --- a/src/js/setup.js +++ b/src/js/setup.js @@ -4,6 +4,7 @@ * * @module setup */ +import * as Dom from './utils/dom'; import * as Events from './utils/events.js'; import document from 'global/document'; import window from 'global/window'; @@ -15,6 +16,12 @@ let videojs; * Set up any tags that have a data-setup `attribute` when the player is started. */ const autoSetup = function() { + + // Protect against breakage in non-browser environments. + if (!Dom.isReal()) { + return; + } + // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack* // var vids = Array.prototype.slice.call(document.getElementsByTagName('video')); // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio')); @@ -89,10 +96,10 @@ function autoSetupTimeout(wait, vjs) { videojs = vjs; } - setTimeout(autoSetup, wait); + window.setTimeout(autoSetup, wait); } -if (document.readyState === 'complete') { +if (Dom.isReal() && document.readyState === 'complete') { _windowLoaded = true; } else { /** diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js index 060c0c5a86..d71615b14c 100644 --- a/src/js/tech/html5.js +++ b/src/js/tech/html5.js @@ -798,20 +798,23 @@ class Html5 extends Tech { /* HTML5 Support Testing ---------------------------------------------------- */ -/** - * Element for testing browser HTML5 media capabilities - * - * @type {Element} - * @constant - * @private - */ -Html5.TEST_VID = document.createElement('video'); -const track = document.createElement('track'); +if (Dom.isReal()) { -track.kind = 'captions'; -track.srclang = 'en'; -track.label = 'English'; -Html5.TEST_VID.appendChild(track); + /** + * Element for testing browser HTML5 media capabilities + * + * @type {Element} + * @constant + * @private + */ + Html5.TEST_VID = document.createElement('video'); + const track = document.createElement('track'); + + track.kind = 'captions'; + track.srclang = 'en'; + track.label = 'English'; + Html5.TEST_VID.appendChild(track); +} /** * Check if HTML5 media is supported by this browser/device. @@ -828,7 +831,7 @@ Html5.isSupported = function() { return false; } - return !!Html5.TEST_VID.canPlayType; + return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType); }; /** @@ -895,9 +898,7 @@ Html5.supportsNativeTextTracks = function() { * - False otherwise */ Html5.supportsNativeVideoTracks = function() { - const supportsVideoTracks = !!Html5.TEST_VID.videoTracks; - - return supportsVideoTracks; + return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks); }; /** @@ -908,9 +909,7 @@ Html5.supportsNativeVideoTracks = function() { * - False otherwise */ Html5.supportsNativeAudioTracks = function() { - const supportsAudioTracks = !!Html5.TEST_VID.audioTracks; - - return supportsAudioTracks; + return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks); }; /** @@ -1026,11 +1025,15 @@ Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks(); Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks(); // HTML5 Feature detection and Device Fixes --------------------------------- // -const canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType; +const canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType; const mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i; const mp4RE = /^video\/mp4/i; Html5.patchCanPlayType = function() { + if (!canPlayType) { + return; + } + // Android 4.0 and above can play HLS to some extent but it reports being unable to do so if (browser.ANDROID_VERSION >= 4.0 && !browser.IS_FIREFOX) { Html5.TEST_VID.constructor.prototype.canPlayType = function(type) { diff --git a/src/js/utils/browser.js b/src/js/utils/browser.js index c00c46471e..50f79a3935 100644 --- a/src/js/utils/browser.js +++ b/src/js/utils/browser.js @@ -2,7 +2,7 @@ * @file browser.js * @module browser */ -import document from 'global/document'; +import * as Dom from './dom'; import window from 'global/window'; const USER_AGENT = window.navigator && window.navigator.userAgent || ''; @@ -70,5 +70,11 @@ export const IE_VERSION = (function(result) { export const IS_SAFARI = (/Safari/i).test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE; export const IS_ANY_SAFARI = IS_SAFARI || IS_IOS; -export const TOUCH_ENABLED = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch); -export const BACKGROUND_SIZE_SUPPORTED = 'backgroundSize' in document.createElement('video').style; +export const TOUCH_ENABLED = Dom.isReal() && ( + 'ontouchstart' in window || + window.DocumentTouch && + window.document instanceof window.DocumentTouch); + +export const BACKGROUND_SIZE_SUPPORTED = ( + Dom.isReal() && + 'backgroundSize' in window.document.createElement('video').style); diff --git a/src/js/utils/dom.js b/src/js/utils/dom.js index 491910b243..f96b00932a 100644 --- a/src/js/utils/dom.js +++ b/src/js/utils/dom.js @@ -55,6 +55,15 @@ function classRegExp(className) { return new RegExp('(^|\\s)' + className + '($|\\s)'); } +/** + * Whether the current DOM interface appears to be real. + * + * @return {Boolean} + */ +export function isReal() { + return document === window.document && typeof document.createElement === 'function'; +} + /** * Determines, via duck typing, whether or not a value is a DOM element. * diff --git a/src/js/video.js b/src/js/video.js index 9b3e19e91c..21187ec92c 100644 --- a/src/js/video.js +++ b/src/js/video.js @@ -35,9 +35,7 @@ import xhr from 'xhr'; import Tech from './tech/tech.js'; // HTML5 Element Shim for IE8 -if (typeof HTMLVideoElement === 'undefined' && - window.document && - window.document.createElement) { +if (typeof HTMLVideoElement === 'undefined' && Dom.isReal()) { document.createElement('video'); document.createElement('audio'); document.createElement('track'); @@ -196,7 +194,7 @@ videojs.removeHook = function(type, fn) { }; // Add default styles -if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true) { +if (window.VIDEOJS_NO_DYNAMIC_STYLE !== true && Dom.isReal()) { let style = Dom.$('.vjs-styles-defaults'); if (!style) {