diff --git a/demo/main.js b/demo/main.js index 8e0a5c48c8..659bd485f8 100644 --- a/demo/main.js +++ b/demo/main.js @@ -1124,6 +1124,9 @@ shakaDemo.Main = class { if (document.fullscreenElement) { document.exitFullscreen(); } + if (this.video_.webkitDisplayingFullscreen) { + this.video_.webkitExitFullscreen(); + } if (document.pictureInPictureElement) { document.exitPictureInPicture(); } diff --git a/lib/ads/client_side_ad.js b/lib/ads/client_side_ad.js index 79e6e90692..f9bf9ccdf0 100644 --- a/lib/ads/client_side_ad.js +++ b/lib/ads/client_side_ad.js @@ -18,14 +18,18 @@ shaka.ads.ClientSideAd = class { /** * @param {!google.ima.Ad} imaAd * @param {!google.ima.AdsManager} imaAdManager + * @param {HTMLMediaElement} video */ - constructor(imaAd, imaAdManager) { + constructor(imaAd, imaAdManager, video) { /** @private {google.ima.Ad} */ this.ad_ = imaAd; /** @private {google.ima.AdsManager} */ this.manager_ = imaAdManager; + /** @private {HTMLMediaElement} */ + this.video_ = video; + /** @private {boolean} */ this.isPaused_ = false; @@ -176,7 +180,14 @@ shaka.ads.ClientSideAd = class { * @export */ resize(width, height) { - const viewMode = document.fullscreenElement ? + let isInFullscreen = false; + const video = /** @type {HTMLVideoElement} */(this.video_); + if (document.fullscreenEnabled) { + isInFullscreen = !!document.fullscreenElement; + } else if (video.webkitSupportsFullscreen) { + isInFullscreen = video.webkitDisplayingFullscreen; + } + const viewMode = isInFullscreen ? google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL; this.manager_.resize(width, height, viewMode); } diff --git a/lib/ads/client_side_ad_manager.js b/lib/ads/client_side_ad_manager.js index c12742b871..c24e528101 100644 --- a/lib/ads/client_side_ad_manager.js +++ b/lib/ads/client_side_ad_manager.js @@ -181,7 +181,7 @@ shaka.ads.ClientSideAdManager = class { this.addImaEventListeners_(); try { - const viewMode = document.fullscreenElement ? + const viewMode = this.isFullScreenEnabled_() ? google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL; this.imaAdsManager_.init(this.video_.offsetWidth, @@ -191,7 +191,7 @@ shaka.ads.ClientSideAdManager = class { // because 'loadedmetadata' is sometimes called before the video resizes // on some platforms (e.g. Safari). this.eventManager_.listen(this.video_, 'loadeddata', () => { - const viewMode = document.fullscreenElement ? + const viewMode = this.isFullScreenEnabled_() ? google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL; this.imaAdsManager_.resize(this.video_.offsetWidth, this.video_.offsetHeight, viewMode); @@ -199,7 +199,7 @@ shaka.ads.ClientSideAdManager = class { if ('ResizeObserver' in window) { this.resizeObserver_ = new ResizeObserver(() => { - const viewMode = document.fullscreenElement ? + const viewMode = this.isFullScreenEnabled_() ? google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL; this.imaAdsManager_.resize(this.video_.offsetWidth, this.video_.offsetHeight, viewMode); @@ -221,6 +221,23 @@ shaka.ads.ClientSideAdManager = class { } + /** + * @return {boolean} + * @private + */ + isFullScreenEnabled_() { + if (document.fullscreenEnabled) { + return !!document.fullscreenElement; + } else { + const video = /** @type {HTMLVideoElement} */(this.video_); + if (video.webkitSupportsFullscreen) { + return video.webkitDisplayingFullscreen; + } + } + return false; + } + + /** * @private */ @@ -389,7 +406,8 @@ shaka.ads.ClientSideAdManager = class { 'Should have an ads manager at this point!'); const imaAd = e.getAd(); - this.ad_ = new shaka.ads.ClientSideAd(imaAd, this.imaAdsManager_); + this.ad_ = new shaka.ads.ClientSideAd(imaAd, + this.imaAdsManager_, this.video_); const data = new Map() .set('ad', this.ad_) .set('sdkAdObject', imaAd) diff --git a/ui/controls.js b/ui/controls.js index c5d454f10e..1b27040cfb 100644 --- a/ui/controls.js +++ b/ui/controls.js @@ -553,33 +553,74 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { this.hideSettingsMenusTimer_.tickNow(); } + /** + * @return {boolean} + * @export + */ + isFullScreenSupported() { + if (document.fullscreenEnabled) { + return true; + } + const video = /** @type {HTMLVideoElement} */(this.localVideo_); + if (video.webkitSupportsFullscreen) { + return true; + } + return false; + } + + /** + * @return {boolean} + * @export + */ + isFullScreenEnabled() { + if (document.fullscreenEnabled) { + return !!document.fullscreenElement; + } + const video = /** @type {HTMLVideoElement} */(this.localVideo_); + if (video.webkitSupportsFullscreen) { + return video.webkitDisplayingFullscreen; + } + return false; + } + /** @export */ async toggleFullScreen() { - if (document.fullscreenElement) { - if (screen.orientation) { - screen.orientation.unlock(); - } - await document.exitFullscreen(); - } else { - // If we are in PiP mode, leave PiP mode first. - try { - if (document.pictureInPictureElement) { - await document.exitPictureInPicture(); + if (document.fullscreenEnabled) { + if (document.fullscreenElement) { + if (screen.orientation) { + screen.orientation.unlock(); } - await this.videoContainer_.requestFullscreen({navigationUI: 'hide'}); - if (this.config_.forceLandscapeOnFullscreen && screen.orientation) { - try { - // Locking to 'landscape' should let it be either - // 'landscape-primary' or 'landscape-secondary' as appropriate. - await screen.orientation.lock('landscape'); - } catch (error) { - // If screen.orientation.lock does not work on a device, it will - // be rejected with an error. Suppress that error. + await document.exitFullscreen(); + } else { + // If we are in PiP mode, leave PiP mode first. + try { + if (document.pictureInPictureElement) { + await document.exitPictureInPicture(); + } + await this.videoContainer_.requestFullscreen({navigationUI: 'hide'}); + if (this.config_.forceLandscapeOnFullscreen && screen.orientation) { + try { + // Locking to 'landscape' should let it be either + // 'landscape-primary' or 'landscape-secondary' as appropriate. + await screen.orientation.lock('landscape'); + } catch (error) { + // If screen.orientation.lock does not work on a device, it will + // be rejected with an error. Suppress that error. + } } + } catch (error) { + this.dispatchEvent(new shaka.util.FakeEvent( + 'error', (new Map()).set('detail', error))); + } + } + } else { + const video = /** @type {HTMLVideoElement} */(this.localVideo_); + if (video.webkitSupportsFullscreen) { + if (video.webkitDisplayingFullscreen) { + video.webkitExitFullscreen(); + } else { + video.webkitEnterFullscreen(); } - } catch (error) { - this.dispatchEvent(new shaka.util.FakeEvent( - 'error', (new Map()).set('detail', error))); } } } @@ -699,7 +740,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { }); this.eventManager_.listen(this.controlsContainer_, 'dblclick', () => { - if (this.config_.doubleClickForFullscreen && document.fullscreenEnabled) { + if (this.config_.doubleClickForFullscreen && + this.isFullScreenSupported()) { this.toggleFullScreen(); } }); diff --git a/ui/fullscreen_button.js b/ui/fullscreen_button.js index 38adfca80c..8ed450fef0 100644 --- a/ui/fullscreen_button.js +++ b/ui/fullscreen_button.js @@ -28,16 +28,16 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element { constructor(parent, controls) { super(parent, controls); + /** @private {HTMLMediaElement} */ + this.localVideo_ = this.controls.getLocalVideo(); + /** @private {!HTMLButtonElement} */ this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-fullscreen-button'); this.button_.classList.add('material-icons-round'); this.button_.classList.add('shaka-tooltip'); - // Don't show the button if fullscreen is not supported - if (!document.fullscreenEnabled) { - this.button_.classList.add('shaka-hidden'); - } + this.checkSupport_(); this.button_.textContent = shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN; this.parent.appendChild(this.button_); @@ -61,6 +61,26 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element { this.updateIcon_(); this.updateAriaLabel_(); }); + + this.eventManager.listen(this.localVideo_, 'loadedmetadata', () => { + this.checkSupport_(); + }); + + this.eventManager.listen(this.localVideo_, 'loadeddata', () => { + this.checkSupport_(); + }); + } + + /** + * @private + */ + checkSupport_() { + // Don't show the button if fullscreen is not supported + if (!this.controls.isFullScreenSupported()) { + this.button_.classList.add('shaka-hidden'); + } else { + this.button_.classList.remove('shaka-hidden'); + } } /** @@ -68,7 +88,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element { */ updateAriaLabel_() { const LocIds = shaka.ui.Locales.Ids; - const label = document.fullscreenElement ? + const label = this.controls.isFullScreenEnabled() ? LocIds.EXIT_FULL_SCREEN : LocIds.FULL_SCREEN; this.button_.ariaLabel = this.localization.resolve(label); @@ -79,7 +99,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element { */ updateIcon_() { this.button_.textContent = - document.fullscreenElement ? + this.controls.isFullScreenEnabled() ? shaka.ui.Enums.MaterialDesignIcons.EXIT_FULLSCREEN : shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN; }