Skip to content

Commit

Permalink
Fix exception thrown for Chrome & Firefox on iOS
Browse files Browse the repository at this point in the history
Because we have some polyfills and blacklists that are specific to
Safari and iOS, we need to detect specific versions of Safari and iOS.
The existing detection of Safari/iOS versions was in multiple places,
and did not account for non-Safari browsers on iOS.  This refactor
allows us to correctly detect iOS Chrome, and fixes a fatal exception
thrown in the MediaSource polyfill for all non-Safari browsers on iOS.

Change-Id: Ic88dc1a4c82087054cd4791dbf295b7ea2aeab09
  • Loading branch information
joeyparrish committed Feb 4, 2020
1 parent 65ad9ad commit 917e788
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 35 deletions.
12 changes: 5 additions & 7 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -708,13 +708,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
return false;
}

// We do not support iOS 9, 10, or 11.
if (shaka.util.Platform.isApple() && shaka.util.Platform.isMobile()) {
const match = navigator.appVersion.match(/Version\/(\d+)/);
const version = parseInt(match[1], /* base= */ 10);
if (version < 12) {
return false;
}
// We do not support iOS 9, 10, or 11, nor those same versions of desktop
// Safari.
const safariVersion = shaka.util.Platform.safariVersion();
if (safariVersion && safariVersion < 12) {
return false;
}

// DRM support is not strictly necessary, but the APIs at least need to be
Expand Down
30 changes: 3 additions & 27 deletions lib/polyfill/mediasource.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ shaka.polyfill.MediaSource = class {
// example, and instances are only accessible after setting up MediaSource
// on a video element. Because of this, we use UA detection and other
// platform detection tricks to decide which patches to install.
const safariVersion = shaka.util.Platform.safariVersion();

if (!window.MediaSource) {
shaka.log.info('No MSE implementation available.');
Expand All @@ -33,27 +34,13 @@ shaka.polyfill.MediaSource = class {
shaka.log.info('Patching Chromecast MSE bugs.');
// Chromecast cannot make accurate determinations via isTypeSupported.
shaka.polyfill.MediaSource.patchCastIsTypeSupported_();
} else if (shaka.util.Platform.isApple()) {
const match = navigator.appVersion.match(/Version\/(\d+)/);
const version = parseInt(match[1], /* base= */ 10);

} else if (safariVersion) {
// TS content is broken on Safari in general.
// See https://github.com/google/shaka-player/issues/743
// and https://bugs.webkit.org/show_bug.cgi?id=165342
shaka.polyfill.MediaSource.rejectTsContent_();

if (version <= 10) {
// Safari 8 does not implement appendWindowEnd.
// Safari 9 & 10 do not correctly implement abort() on SourceBuffer.
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=160316
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
// Safari 10 fires spurious 'updateend' events after endOfStream().
// Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165336

// Blacklist these very outdated versions.
shaka.log.info('Blacklisting MSE on Safari <= 10.');
shaka.polyfill.MediaSource.blacklist_();
} else if (version <= 12) {
if (safariVersion <= 12) {
shaka.log.info('Patching Safari 11 & 12 MSE bugs.');
// Safari 11 & 12 do not correctly implement abort() on SourceBuffer.
// Calling abort() before appending a segment causes that segment to be
Expand Down Expand Up @@ -85,17 +72,6 @@ shaka.polyfill.MediaSource = class {
}
}

/**
* Blacklist the current browser by removing media source. A side-effect of
* this will be to make |shaka.util.Platform.supportsMediaSource| return
* |false|.
*
* @private
*/
static blacklist_() {
window['MediaSource'] = null;
}

/**
* Stub out abort(). On some buggy MSE implementations, calling abort()
* causes various problems.
Expand Down
44 changes: 43 additions & 1 deletion lib/util/platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,56 @@ shaka.util.Platform = class {


/**
* Check if the current platform is an Apple device (iOS, desktop Safari, etc)
* Check if the current platform is from Apple.
*
* Returns true on all iOS browsers and on desktop Safari.
*
* Returns false for non-Safari browsers on macOS, which are independent of
* Apple.
*
* @return {boolean}
*/
static isApple() {
return !!navigator.vendor && navigator.vendor.includes('Apple');
}

/**
* Returns a major version number for Safari, or Safari-based iOS browsers.
*
* For example:
* - Safari 13.0.4 on macOS returns 13.
* - Safari on iOS 13.3.1 returns 13.
* - Chrome on iOS 13.3.1 returns 13 (since this is based on Safari/WebKit).
* - Chrome on macOS returns null (since this is independent of Apple).
*
* Returns null on Firefox on iOS, where this version information is not
* available.
*
* @return {?number} A major version number or null if not iOS.
*/
static safariVersion() {
// All iOS browsers and desktop Safari will return true for isApple().
if (!shaka.util.Platform.isApple()) {
return null;
}

// This works for iOS Safari and desktop Safari, which contain something
// like "Version/13.0" indicating the major Safari or iOS version.
let match = navigator.userAgent.match(/Version\/(\d+)/);
if (match) {
return parseInt(match[1], /* base= */ 10);
}

// This works for all other browsers on iOS, which contain something like
// "OS 13_3" indicating the major & minor iOS version.
match = navigator.userAgent.match(/OS (\d+)(?:_\d+)?/);
if (match) {
return parseInt(match[1], /* base= */ 10);
}

return null;
}

/**
* Guesses if the platform is a mobile one (iOS or Android).
*
Expand Down

0 comments on commit 917e788

Please sign in to comment.