Add PiP pollyfill for Safari (#1902)

This commit is contained in:
Álvaro Velad Galván
2019-05-03 19:50:37 +02:00
committed by Joey Parrish
parent 9a4b73df7c
commit caa34ca90d
4 changed files with 252 additions and 0 deletions
+1
View File
@@ -10,6 +10,7 @@
+../../lib/polyfill/patchedmediakeys_ms.js
+../../lib/polyfill/patchedmediakeys_nop.js
+../../lib/polyfill/patchedmediakeys_webkit.js
+../../lib/polyfill/pip.js
+../../lib/polyfill/video_play_promise.js
+../../lib/polyfill/videoplaybackquality.js
+../../lib/polyfill/vttcue.js
+18
View File
@@ -42,3 +42,21 @@ HTMLMediaElement.prototype.requestPictureInPicture = function() {};
/** @type {boolean} */
HTMLMediaElement.prototype.disablePictureInPicture;
/**
* @param {string} mode
* @return {boolean}
*/
HTMLMediaElement.prototype.webkitSetPresentationMode = function(mode) {};
/**
* @param {string} mode
* @return {boolean}
*/
HTMLMediaElement.prototype.webkitSupportsPresentationMode = function(mode) {};
/** @type {string} */
HTMLMediaElement.prototype.webkitPresentationMode;
+232
View File
@@ -0,0 +1,232 @@
/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
goog.provide('shaka.polyfill.PiP');
goog.require('shaka.log');
goog.require('shaka.polyfill.register');
/**
* @namespace shaka.polyfill.PiP
*
* @summary A polyfill to provide PiP support in Safari.
*/
/**
* Install the polyfill if needed.
*/
shaka.polyfill.PiP.install = function() {
if (!window.HTMLVideoElement) {
// Avoid errors on very old browsers.
return;
}
const proto = HTMLVideoElement.prototype;
if (proto.requestPictureInPicture &&
document.exitPictureInPicture) {
// No polyfill needed.
return;
}
if (!proto.webkitSupportsPresentationMode) {
// No polyfill available.
return;
}
shaka.log.debug('PiP.install');
/** @type {HTMLMediaElement} */
let polyfillPictureInPictureElement = null;
let polyfillEnterpictureinpicture = null;
let polyfillLeavepictureinpicture = null;
/**
* polyfill document.pictureInPictureElement
*/
Object.defineProperty(document, 'pictureInPictureElement', {
get() {
return polyfillPictureInPictureElement;
},
set(value) {
if (value === polyfillPictureInPictureElement) return;
if (polyfillPictureInPictureElement) {
polyfillPictureInPictureElement.removeEventListener(
'webkitpresentationmodechanged',
shaka.polyfill.PiP.updatePictureInPictureElementInDocument_,
);
}
polyfillPictureInPictureElement =
/** @type {HTMLMediaElement} */ (value);
if (polyfillPictureInPictureElement) {
polyfillPictureInPictureElement.addEventListener(
'webkitpresentationmodechanged',
shaka.polyfill.PiP.updatePictureInPictureElementInDocument_,
);
}
},
});
/**
* polyfill document.pictureInPictureEnabled
*/
document.pictureInPictureEnabled = true;
/**
* polyfill HTMLMediaElement.requestPictureInPicture
*/
proto.requestPictureInPicture = shaka.polyfill.PiP.requestPictureInPicture_;
/**
* polyfill document.exitPictureInPicture
*/
document.exitPictureInPicture = shaka.polyfill.PiP.exitPictureInPicture_;
/**
* polyfill enterpictureinpicture and leavepictureinpicture events
*/
const oldAddEventListener = proto.addEventListener;
/**
* @this {HTMLMediaElement}
*/
proto.addEventListener = function(type, listener, options) {
const callback = /** @type {Function} */ (listener);
if (type === 'enterpictureinpicture') {
// eslint-disable-next-line no-inner-declarations
function proxyEnterPiPEvent(event) {
const videoElement =
/** @type {HTMLVideoElement} */ (event.target);
if (videoElement.webkitPresentationMode === 'picture-in-picture') {
// keep track of the pipElement
document.pictureInPictureElement = videoElement;
callback();
}
}
this.addEventListener('webkitpresentationmodechanged',
proxyEnterPiPEvent);
// keep track of the listener to be able to remove them later
if (polyfillEnterpictureinpicture) {
polyfillEnterpictureinpicture[listener] = proxyEnterPiPEvent;
} else {
polyfillEnterpictureinpicture = {
[listener]: proxyEnterPiPEvent,
};
}
} else if (type === 'leavepictureinpicture') {
// eslint-disable-next-line no-inner-declarations
function proxyLeavePiPEvent(event) {
const videoElement =
/** @type {HTMLVideoElement} */ (event.target);
if (videoElement.webkitPresentationMode === 'inline') {
callback();
}
}
this.addEventListener('webkitpresentationmodechanged',
proxyLeavePiPEvent);
// keep track of the listener to be able to remove them later
if (polyfillLeavepictureinpicture) {
polyfillLeavepictureinpicture[listener] = proxyLeavePiPEvent;
} else {
polyfillLeavepictureinpicture = {
[listener]: proxyLeavePiPEvent,
};
}
} else {
// fallback for all the other events
oldAddEventListener.apply(this, [type, listener, options]);
}
};
const oldRemoveEventListener = proto.removeEventListener;
/**
* @this {HTMLMediaElement}
*/
proto.removeEventListener = function(type, listener, options) {
if (type === 'enterpictureinpicture') {
this.removeEventListener('webkitpresentationmodechanged',
polyfillEnterpictureinpicture[listener]);
} else if (type === 'leavepictureinpicture') {
this.removeEventListener('webkitpresentationmodechanged',
polyfillLeavepictureinpicture[listener]);
} else {
// fallback for all the other events
oldRemoveEventListener.apply(this, [type, listener, options]);
}
};
};
/**
* @param {!Event} event
* @private
*/
shaka.polyfill.PiP.updatePictureInPictureElementInDocument_ =
function(event) {
const videoElement = /** @type {HTMLVideoElement} */ (event.target);
if (videoElement.webkitPresentationMode &&
videoElement.webkitPresentationMode !== 'picture-in-picture') {
document.pictureInPictureElement = null;
}
};
/**
* @this {HTMLMediaElement}
* @return {!Promise}
* @private
*/
shaka.polyfill.PiP.requestPictureInPicture_ = function() {
// check if PIP is enabled
if (!this.webkitSupportsPresentationMode('picture-in-picture')) {
const error = new Error('PIP not allowed by videoElement',
'InvalidStateError');
return Promise.reject(error);
} else {
// enter PIP mode
this.webkitSetPresentationMode('picture-in-picture');
document.pictureInPictureElement = this;
return Promise.resolve();
}
};
/**
* @this {Document}
* @return {!Promise}
* @private
*/
shaka.polyfill.PiP.exitPictureInPicture_ = function() {
if (document.pictureInPictureElement) {
// exit PIP mode
const video =
/** @type {!HTMLMediaElement} */ (document.pictureInPictureElement);
video.webkitSetPresentationMode('inline');
document.pictureInPictureElement = null;
return Promise.resolve();
} else {
const error = new Error('No picture in picture element found',
'InvalidStateError');
return Promise.reject(error);
}
};
shaka.polyfill.register(shaka.polyfill.PiP.install);
+1
View File
@@ -51,6 +51,7 @@ goog.require('shaka.polyfill.PatchedMediaKeysApple');
goog.require('shaka.polyfill.PatchedMediaKeysMs');
goog.require('shaka.polyfill.PatchedMediaKeysNop');
goog.require('shaka.polyfill.PatchedMediaKeysWebkit');
goog.require('shaka.polyfill.PiP');
goog.require('shaka.polyfill.VTTCue');
goog.require('shaka.polyfill.VideoPlayPromise');
goog.require('shaka.polyfill.VideoPlaybackQuality');