Browse Source

Merge pull request #195 from fancycode/youtube_sandbox

Run YouTube player in sandboxed iframe
pull/200/head
Simon Eisenmann 10 years ago
parent
commit
3da6580aac
  1. 2
      Makefile.am
  2. 7
      build/build.js
  3. 1
      server.conf.in
  4. 4
      src/styles/components/_youtubevideo.scss
  5. 251
      static/js/directives/youtubevideo.js
  6. 248
      static/js/sandboxes/youtube.js
  7. 5
      static/js/services/resturl.js
  8. 66
      static/js/services/sandbox.js
  9. 9
      static/js/services/services.js
  10. 2
      static/partials/youtubevideo.html
  11. 26
      static/partials/youtubevideo_sandbox.html

2
Makefile.am

@ -131,6 +131,7 @@ install: @@ -131,6 +131,7 @@ install:
$(INSTALL) -d $(SHARE)/www/static/translation
$(INSTALL) -d $(SHARE)/www/static/css
$(INSTALL) -d $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -d $(SHARE)/www/static/js/sandboxes
$(INSTALL) bin/$(EXENAME) $(BIN)
$(INSTALL) html/* $(SHARE)/www/html
$(INSTALL) static/img/* $(SHARE)/www/static/img
@ -142,6 +143,7 @@ install: @@ -142,6 +143,7 @@ install:
$(INSTALL) $(OUTPUT_JS)/*.js $(SHARE)/www/static/js
$(INSTALL) $(OUTPUT_JS)/libs/pdf/*.js $(SHARE)/www/static/js/libs/pdf
$(INSTALL) -D static/js/libs/webodf.js $(SHARE)/www/static/js/libs/webodf.js
$(INSTALL) $(OUTPUT_JS)/sandboxes/*.js $(SHARE)/www/static/js/sandboxes
clean:
$(GO) clean -i -r app/... 2>/dev/null || true

7
build/build.js

@ -76,6 +76,13 @@ @@ -76,6 +76,13 @@
override: {
skipModuleInsertion: true
}
},
{
name: 'sandboxes/youtube',
dir: './out/sandboxes',
override: {
skipModuleInsertion: true
}
}
]
})

1
server.conf.in

@ -109,6 +109,7 @@ serverRealm = local @@ -109,6 +109,7 @@ serverRealm = local
; data: URL for images.
; The currently recommended CSP is:
; default-src 'self';
; frame-src 'self' data:;
; style-src 'self' 'unsafe-inline';
; img-src 'self' data: blob:;
; connect-src 'self' wss://server:port/ws blob:;

4
src/styles/components/_youtubevideo.scss

@ -92,6 +92,10 @@ @@ -92,6 +92,10 @@
#youtubecontainer { // scss-lint:disable IdSelector
position: relative;
&.fullscreen {
width: 100%;
}
}
#youtubeplayerinfo { // scss-lint:disable IdSelector

251
static/js/directives/youtubevideo.js

@ -20,20 +20,81 @@ @@ -20,20 +20,81 @@
*/
"use strict";
define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], function($, _, template, BigScreen) {
define(['require', 'jquery', 'underscore', 'moment', 'text!partials/youtubevideo.html', 'text!partials/youtubevideo_sandbox.html', 'bigscreen'], function(require, $, _, moment, template, sandboxTemplate, BigScreen) {
return ["$window", "$document", "mediaStream", "alertify", "translation", "safeApply", "appData", "$q", function($window, $document, mediaStream, alertify, translation, safeApply, appData, $q) {
return ["$window", "$document", "mediaStream", "alertify", "translation", "safeApply", "appData", "$q", "restURL", "sandbox", function($window, $document, mediaStream, alertify, translation, safeApply, appData, $q, restURL, sandbox) {
var YOUTUBE_IFRAME_API_URL = "//www.youtube.com/iframe_api";
var isYouTubeIframeAPIReady = (function() {
var d = $q.defer();
$window.onYouTubeIframeAPIReady = function() {
console.log("YouTube IFrame ready");
d.resolve();
var isYouTubeIframeAPIReadyDefer = $q.defer();
var isYouTubeIframeAPIReady = isYouTubeIframeAPIReadyDefer.promise;
var SandboxPlayer = function(sandbox, params) {
this.sandbox = sandbox;
this.state = -1;
this.position = 0;
this.lastPositionUpdate = null;
this.sandbox.postMessage("loadPlayer", params);
};
SandboxPlayer.prototype.destroy = function() {
this.sandbox.postMessage("destroyPlayer", {"destroy": true});
};
SandboxPlayer.prototype.loadVideoById = function(id, position) {
var msg = {"id": id};
if (typeof(position) !== "undefined") {
msg.position = position;
}
this.sandbox.postMessage("loadVideo", msg);
};
SandboxPlayer.prototype.playVideo = function() {
this.sandbox.postMessage("playVideo", {"play": true});
};
SandboxPlayer.prototype.pauseVideo = function() {
this.sandbox.postMessage("pauseVideo", {"pause": true});
};
SandboxPlayer.prototype.stopVideo = function() {
this.sandbox.postMessage("stopVideo", {"stop": true});
};
SandboxPlayer.prototype.seekTo = function(position, allowSeekAhead) {
var msg = {"position": position};
if (typeof(allowSeekAhead) !== "undefined") {
msg.allowSeekAhead = allowSeekAhead;
}
this.sandbox.postMessage("seekTo", msg);
};
SandboxPlayer.prototype.setVolume = function(volume) {
this.sandbox.postMessage("setVolume", {"volume": volume});
};
SandboxPlayer.prototype.setCurrentTime = function(time) {
this.position = time;
this.lastPositionUpdate = moment();
};
SandboxPlayer.prototype.getCurrentTime = function() {
if (!this.lastPositionUpdate) {
return this.position;
}
var now = moment();
var deltaTime = now.diff(this.lastPositionUpdate, 'seconds', true);
return this.position + deltaTime;
};
SandboxPlayer.prototype.setPlayerState = function(state) {
this.state = state;
};
SandboxPlayer.prototype.getPlayerState = function() {
return this.state;
};
return d.promise;
})();
var controller = ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
@ -41,20 +102,63 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -41,20 +102,63 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
var player = null;
var playerReady = null;
var isPaused = null;
var seekDetector = null;
var playReceivedNow = null;
var prevTime = null;
var prevNow = null;
var initialState = null;
var sandboxFrame = $("#youtubeplayer", $element)[0];
var template = sandboxTemplate;
template = template.replace(/__PARENT_ORIGIN__/g, $window.location.protocol + "//" + $window.location.host);
template = template.replace(/__YOUTUBE_SANDBOX_JS_URL__/g, restURL.createAbsoluteUrl(require.toUrl('sandboxes/youtube') + ".js"));
var sandboxApi = sandbox.createSandbox(sandboxFrame, template);
sandboxApi.e.on("message", function(event, message) {
var msg = message.data;
var data = msg[msg.type] || {};
switch (msg.type) {
case "youtube.apiReady":
$scope.$apply(function() {
console.log("YouTube IFrame ready");
isYouTubeIframeAPIReadyDefer.resolve();
});
break;
case "youtube.playerReady":
$scope.$apply(function() {
playerReady.resolve();
});
break;
case "youtube.volume":
$scope.$apply(function(scope) {
scope.volume = data.volume;
});
break;
case "youtube.event":
$scope.$apply(function(scope) {
console.log("State change", data);
if (player) {
player.setPlayerState(data.state);
}
scope.$emit(data.event, data.position);
});
break;
case "youtube.position":
if (player) {
player.setCurrentTime(data.position);
}
break;
default:
console.log("Unknown message received", message);
break;
}
});
$scope.$on("$destroy", function() {
if (player) {
player.destroy();
}
sandboxApi.destroy();
sandboxApi = null;
});
var stateEvents = {
"-1": "youtube.unstarted",
"0": "youtube.ended",
"1": "youtube.playing",
"2": "youtube.paused",
"3": "youtube.buffering",
"5": "youtube.videocued"
};
var errorIds = {
"2": "invalidParameter",
"5": "htmlPlayerError",
@ -79,13 +183,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -79,13 +183,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
});
});
var onPlayerReady = function(event) {
$scope.$apply(function(scope) {
scope.volume = player.getVolume();
playerReady.resolve();
});
};
var onPlayerError = function(event) {
var error = errorIds[event.data] || "unknownError";
$scope.$apply(function(scope) {
@ -93,19 +190,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -93,19 +190,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
});
};
var onPlayerStateChange = function(event) {
var msg = stateEvents[event.data];
if (typeof msg === "undefined") {
console.warn("Unknown YouTube player state", event)
return;
}
$scope.$apply(function(scope) {
console.log("State change", msg, event.target);
scope.$emit(msg, event.target);
});
};
var getYouTubeId = function(url) {
/*
* Supported URLs:
@ -130,79 +214,34 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -130,79 +214,34 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
return null;
}
var startDetectSeek = function() {
var checkSeek = function() {
if (!player) {
return;
}
var now = new Date();
var time = player.getCurrentTime();
if (prevTime === null) {
prevTime = time;
}
if (prevNow === null) {
prevNow = now;
}
var deltaTime = Math.abs(time - prevTime);
var deltaNow = (now - prevNow) * 0.001;
if (deltaTime > deltaNow * 1.1) {
safeApply($scope, function(scope) {
scope.$emit("youtube.seeked", time);
});
}
prevNow = now;
prevTime = time;
};
if (!seekDetector) {
seekDetector = $window.setInterval(function() {
checkSeek();
}, 1000);
}
checkSeek();
};
var stopDetectSeek = function() {
if (seekDetector) {
$window.clearInterval(seekDetector);
seekDetector = null;
}
prevNow = null;
};
$scope.$on("youtube.playing", function() {
$scope.$on("youtube.playing", function(event, position) {
if (initialState === 2) {
initialState = null;
player.pauseVideo();
return;
}
prevTime = null;
startDetectSeek();
if (isPaused) {
isPaused = false;
mediaStream.webrtc.callForEachCall(function(peercall) {
mediaStreamSendYouTubeVideo(peercall, currentToken, {
Type: "Resume",
Resume: {
position: player.getCurrentTime()
position: position
}
});
});
}
});
$scope.$on("youtube.buffering", function() {
$scope.$on("youtube.buffering", function(event, position) {
if (initialState === 2) {
initialState = null;
player.pauseVideo();
}
startDetectSeek();
});
$scope.$on("youtube.paused", function() {
stopDetectSeek();
$scope.$on("youtube.paused", function(event, position) {
if (!$scope.isPublisher || !currentToken) {
return;
}
@ -213,7 +252,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -213,7 +252,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
mediaStreamSendYouTubeVideo(peercall, currentToken, {
Type: "Pause",
Pause: {
position: player.getCurrentTime()
position: position
}
});
});
@ -221,7 +260,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -221,7 +260,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
});
$scope.$on("youtube.ended", function() {
stopDetectSeek();
});
$scope.$on("youtube.seeked", function($event, position) {
@ -241,10 +279,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -241,10 +279,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
var playVideo = function(id, position, state) {
playerReady.done(function() {
$("#youtubeplayer").show();
$scope.playbackActive = true;
prevTime = null;
prevNow = null;
isPaused = null;
if (playReceivedNow) {
var delta = ((new Date()) - playReceivedNow) * 0.001;
@ -278,7 +313,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -278,7 +313,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
isYouTubeIframeAPIReady.then(function() {
if (!player) {
var origin = $window.location.protocol + "//" + $window.location.host;
player = new $window.YT.Player("youtubeplayer", {
player = new SandboxPlayer(sandboxApi, {
height: "390",
width: "640",
playerVars: {
@ -291,13 +326,8 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -291,13 +326,8 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
"controls": with_controls ? "2" : "0",
"disablekb": with_controls ? "0" : "1",
"origin": origin
},
events: {
"onReady": onPlayerReady,
"onStateChange": onPlayerStateChange
}
});
$("#youtubeplayer").show();
safeApply($scope, function(scope) {
// YT player events don't fire in Firefox if
// player is not visible, so show while loading
@ -475,19 +505,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -475,19 +505,7 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
};
$scope.loadYouTubeAPI = function() {
if (!addedIframeScript) {
var head = $document[0].getElementsByTagName('head')[0];
var script = $document[0].createElement('script');
script.type = "text/javascript";
script.src = YOUTUBE_IFRAME_API_URL;
script.onerror = function(event) {
alertify.dialog.alert(translation._("Could not load YouTube player API, please check your network / firewall settings."));
head.removeChild(script);
addedIframeScript = false;
};
head.appendChild(script);
addedIframeScript = true;
}
sandboxApi.postMessage("loadApi", {"url": $window.location.protocol + YOUTUBE_IFRAME_API_URL});
};
$scope.showYouTubeVideo = function() {
@ -538,7 +556,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -538,7 +556,6 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
$scope.currentVideoUrl = null;
$scope.currentVideoId = null;
peers = {};
stopDetectSeek();
playerReady = null;
initialState = null;
mediaStream.webrtc.e.off("statechange", updater);
@ -568,7 +585,11 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'], @@ -568,7 +585,11 @@ define(['jquery', 'underscore', 'text!partials/youtubevideo.html', 'bigscreen'],
$scope.toggleFullscreen = function(elem) {
if (BigScreen.enabled) {
BigScreen.toggle(elem);
BigScreen.toggle(elem, function() {
$(elem).addClass("fullscreen");
}, function() {
$(elem).removeClass("fullscreen");
});
}
};

248
static/js/sandboxes/youtube.js

@ -0,0 +1,248 @@ @@ -0,0 +1,248 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
"use strict";
(function () {
var script = document.getElementsByTagName("script")[0];
var PARENT_ORIGIN = script.getAttribute("data-parent-origin");
var YouTubeSandbox = function(window) {
this.head = document.getElementsByTagName('head')[0];
this.window = window;
this.addedIframeScript = false;
this.player = null;
this.seekDetector = null;
this.prevTime = null;
this.prevNow = null;
};
YouTubeSandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type};
msg[type] = message;
this.window.parent.postMessage(msg, PARENT_ORIGIN);
};
YouTubeSandbox.prototype.onYouTubeIframeAPIReady = function() {
this.postMessage("youtube.apiReady", {"apiReady": true});
};
YouTubeSandbox.prototype.loadApi = function(url) {
if (!this.addedIframeScript) {
var that = this;
var script = document.createElement('script');
script.type = "text/javascript";
script.src = url;
script.onerror = function(evt) {
that.postMessage("youtube.error", {"msgid": "loadScriptFailed"});
that.head.removeChild(script);
that.addedIframeScript = false;
};
this.head.appendChild(script);
this.addedIframeScript = true;
}
};
YouTubeSandbox.prototype.loadPlayer = function(params) {
if (!this.player) {
var that = this;
var stateEvents = {
"-1": "youtube.unstarted",
"0": "youtube.ended",
"1": "youtube.playing",
"2": "youtube.paused",
"3": "youtube.buffering",
"5": "youtube.videocued"
};
var playerVars = params.playerVars || {};
delete playerVars.origin;
this.player = new this.window.YT.Player("youtubeplayer", {
height: params.height || "390",
width: params.width || "640",
playerVars: playerVars,
events: {
"onReady": function(event) {
that.postMessage("youtube.volume", {"volume": that.player.getVolume()});
that.postMessage("youtube.playerReady", {"playerReady": true});
},
"onStateChange": function(event) {
var msg = stateEvents[event.data];
if (typeof msg === "undefined") {
console.warn("Unknown YouTube player state", event)
return;
}
switch (msg) {
case "youtube.playing":
that.prevTime = null;
that.startDetectSeek();
break;
case "youtube.buffering":
that.startDetectSeek();
break;
case "youtube.paused":
that.stopDetectSeek();
break;
case "youtube.ended":
that.stopDetectSeek();
break;
}
that.postMessage("youtube.event", {"event": msg, "state": event.data, "position": that.player.getCurrentTime()});
}
}
});
}
};
YouTubeSandbox.prototype.destroyPlayer = function() {
this.stopDetectSeek();
if (this.player) {
this.player.destroy();
this.player = null;
}
};
YouTubeSandbox.prototype.loadVideo = function(id, position) {
this.prevTime = null;
this.prevNow = null;
if (typeof(position) !== "undefined") {
this.player.loadVideoById(id, position);
} else {
this.player.loadVideoById(id);
}
};
YouTubeSandbox.prototype.playVideo = function() {
this.player.playVideo();
};
YouTubeSandbox.prototype.pauseVideo = function() {
this.player.pauseVideo();
};
YouTubeSandbox.prototype.stopVideo = function() {
this.player.stopVideo();
};
YouTubeSandbox.prototype.seekTo = function(position, allowSeekAhead) {
if (typeof(allowSeekAhead) !== "undefined") {
this.player.seekTo(position, allowSeekAhead);
} else {
this.player.seekTo(position);
}
};
YouTubeSandbox.prototype.setVolume = function(volume) {
this.player.setVolume(volume);
};
YouTubeSandbox.prototype.startDetectSeek = function() {
var that = this;
var checkSeek = function() {
if (!that.player) {
return;
}
var now = new Date();
var time = that.player.getCurrentTime();
that.postMessage("youtube.position", {"position": time});
if (that.prevTime === null) {
that.prevTime = time;
}
if (that.prevNow === null) {
that.prevNow = now;
}
var deltaTime = Math.abs(time - that.prevTime);
var deltaNow = (now - that.prevNow) * 0.001;
if (deltaTime > deltaNow * 1.1) {
that.postMessage("youtube.event", {"event": "youtube.seeked", "position": time});
}
that.prevNow = now;
that.prevTime = time;
};
if (!this.seekDetector) {
this.seekDetector = this.window.setInterval(function() {
checkSeek();
}, 1000);
}
checkSeek();
};
YouTubeSandbox.prototype.stopDetectSeek = function() {
if (this.seekDetector) {
this.window.clearInterval(this.seekDetector);
this.seekDetector = null;
}
this.prevNow = null;
};
var sandbox = new YouTubeSandbox(window);
window.onYouTubeIframeAPIReady = function() {
sandbox.onYouTubeIframeAPIReady();
};
window.addEventListener("message", function(event) {
if (event.origin !== PARENT_ORIGIN) {
// only accept messages from spreed-webrtc
return;
}
var msg = event.data;
var data = msg[msg.type] || {};
switch (msg.type) {
case "loadApi":
sandbox.loadApi(data.url);
break;
case "loadPlayer":
sandbox.loadPlayer(data);
break;
case "destroyPlayer":
sandbox.destroyPlayer();
break;
case "loadVideo":
sandbox.loadVideo(data.id, data.position);
break;
case "playVideo":
sandbox.playVideo();
break;
case "pauseVideo":
sandbox.pauseVideo();
break;
case "stopVideo":
sandbox.stopVideo();
break;
case "seekTo":
sandbox.seekTo(data.position, data.allowSeekAhead);
break;
case "setVolume":
sandbox.setVolume(data.volume);
break;
default:
console.log("Unknown message received", event);
break;
}
}, false);
console.log("YouTube sandbox ready.");
})();

5
static/js/services/resturl.js

@ -64,6 +64,11 @@ define(["underscore"], function(_) { @@ -64,6 +64,11 @@ define(["underscore"], function(_) {
}
return url.join("/");
};
RestURL.prototype.createAbsoluteUrl = function(url) {
var link = $window.document.createElement("a");
link.href = url;
return link.href;
};
return new RestURL();
}];
});

66
static/js/services/sandbox.js

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
/*
* Spreed WebRTC.
* Copyright (C) 2013-2015 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
"use strict";
define(["jquery", "underscore"], function($, _) {
return ["$window", function($window) {
var Sandbox = function(iframe, template) {
this.iframe = iframe;
this.iframe.src = "data:text/html;charset=utf-8," + $window.encodeURI(template);
this.target = this.iframe.contentWindow;
this.e = $({});
this.handler = _.bind(this.onPostMessageReceived, this);
$window.addEventListener("message", this.handler, false);
};
Sandbox.prototype.destroy = function() {
if (this.handler) {
$window.removeEventListener("message", this.handler, false);
this.handler = null;
}
};
Sandbox.prototype.onPostMessageReceived = function(event) {
if (event.origin !== "null" || event.source !== this.target) {
// the sandboxed data-url iframe has "null" as origin
return;
}
this.e.triggerHandler("message", [event]);
};
Sandbox.prototype.postMessage = function(type, message) {
var msg = {"type": type}
msg[type] = message;
this.target.postMessage(msg, "*");
};
return {
createSandbox: function(iframe, template) {
return new Sandbox(iframe, template);
}
};
}];
});

9
static/js/services/services.js

@ -67,7 +67,8 @@ define([ @@ -67,7 +67,8 @@ define([
'services/roompin',
'services/constraints',
'services/modules',
'services/mediadevices'], function(_,
'services/mediadevices',
'services/sandbox'], function(_,
desktopNotify,
playSound,
safeApply,
@ -112,7 +113,8 @@ restURL, @@ -112,7 +113,8 @@ restURL,
roompin,
constraints,
modules,
mediaDevices) {
mediaDevices,
sandbox) {
var services = {
desktopNotify: desktopNotify,
@ -159,7 +161,8 @@ mediaDevices) { @@ -159,7 +161,8 @@ mediaDevices) {
roompin: roompin,
constraints: constraints,
modules: modules,
mediaDevices: mediaDevices
mediaDevices: mediaDevices,
sandbox: sandbox
};
var initialize = function(angModule) {

2
static/partials/youtubevideo.html

@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
<div ng-show="playbackActive">
<div class="row" id="youtubecontainer">
<div class="embed-responsive embed-responsive-16by9">
<div id="youtubeplayer"></div>
<iframe sandbox="allow-scripts allow-same-origin" id="youtubeplayer"></iframe>
</div>
<div id="youtubeplayerinfo">
<div>{{_('Currently playing')}}<br><a href="{{ currentVideoUrl }}" rel="external" target="_blank">{{ currentVideoUrl }}</a></div>

26
static/partials/youtubevideo_sandbox.html

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>YouTube Player Sandbox</title>
<style type="text/css">
html, body {
height:100%;
}
body {
margin:0;
padding:0;
max-width:100%;
max-height:100%;
overflow:hidden;
}
#youtubeplayer {
width:100%;
height:100%;
}
</style>
</head>
<body>
<div id="youtubeplayer"></div>
<script src="__YOUTUBE_SANDBOX_JS_URL__" data-parent-origin="__PARENT_ORIGIN__"></script>
</body>
</html>
Loading…
Cancel
Save