diff --git a/static/js/directives/screenshare.js b/static/js/directives/screenshare.js index 58e73dd7..e105b626 100644 --- a/static/js/directives/screenshare.js +++ b/static/js/directives/screenshare.js @@ -22,7 +22,7 @@ "use strict"; define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials/screensharepeer.html', 'bigscreen', 'webrtc.adapter'], function($, _, template, templatePeer, BigScreen) { - return ["$window", "mediaStream", "$compile", "safeApply", "videoWaiter", "$timeout", "alertify", "translation", "screensharing", function($window, mediaStream, $compile, safeApply, videoWaiter, $timeout, alertify, translation, screensharing) { + return ["$window", "mediaStream", "$compile", "safeApply", "videoWaiter", "$timeout", "alertify", "translation", "screensharing", "$location", function($window, mediaStream, $compile, safeApply, videoWaiter, $timeout, alertify, translation, screensharing, $location) { var peerTemplate = $compile(templatePeer); @@ -172,6 +172,7 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials } $scope.layout.screenshare = true; + screensharing.globalNotify.screensharingStart(); screensharing.getScreen().then(function(options) { if (options) { $scope.startScreenshare(options); @@ -278,9 +279,13 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials switch (error.name) { case "PermissionDeniedError": case "InvalidStateError": - if ($window.webrtcDetectedVersion >= 32 && + if ($window.webrtcDetectedBrowser === "chrome" && + $window.webrtcDetectedVersion >= 32 && $window.webrtcDetectedVersion < 37) { alertify.dialog.alert(translation._("Permission to start screen sharing was denied. Make sure to have enabled screen sharing access for your browser. Copy chrome://flags/#enable-usermedia-screen-capture and open it with your browser and enable the flag on top. Then restart the browser and you are ready to go.")); + } else if ($window.webrtcDetectedBrowser === "firefox" && + $window.webrtcDetectedVersion >= 36) { + alertify.dialog.alert(translation._("Permission to start screen sharing was denied. Make sure to have enabled screen sharing access for your browser. Copy about:config?filter=screensharing.allowed_domains and open it with your browser, double click the option, and add the domain %s to the list (include a comma). Then you are ready to go. Otherwise install the Firefox screensharing extension.", $location.host())); } else { alertify.dialog.alert(translation._("Permission to start screen sharing was denied.")); } @@ -307,6 +312,8 @@ define(['jquery', 'underscore', 'text!partials/screenshare.html', 'text!partials $scope.layout.screenshare = false; } + screensharing.globalNotify.screensharingStop(); + }; $scope.toggleFullscreen = function(elem) { diff --git a/static/js/services/firefoxextension.js b/static/js/services/firefoxextension.js new file mode 100644 index 00000000..1774f303 --- /dev/null +++ b/static/js/services/firefoxextension.js @@ -0,0 +1,102 @@ +/* + * 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 . + * + */ + +"use strict"; +define(["underscore", "jquery", "webrtc.adapter"], function(_, $) { + + // firefoxExtension + return ["$window", "$q", "alertify", "translation", "$interval", function($window, $q, alertify, translation, $interval) { + + var EXTENSION_DOM_ID = 'firefoxextension-available'; + + var intervalSecs = 50; + var intervalCount = 1; + var isAvailable = function() { + return $window.document.getElementById(EXTENSION_DOM_ID); + }; + + var FirefoxExtension = function() { + this.available = false; + this.e = $({}); + this.autoinstall = {}; + this.initialize(); + }; + + FirefoxExtension.prototype.initialize = function() { + if (isAvailable()) { + this.available = true; + console.log("Firefox extension is available."); + this.e.triggerHandler("available", true); + } else if (this.available) { + this.available = false; + console.log("Firefox extension is no longer available."); + this.e.triggerHandler("available", false); + } + }; + + FirefoxExtension.prototype.registerAutoInstall = function(installFunc, cancelInstallFunc, force) { + this.autoinstall.install = installFunc; + this.autoinstall.cancel = cancelInstallFunc; + this.autoinstall.force = !!force; + if (!this.available && installFunc) { + this.e.triggerHandler("available", true); + } + }; + + /** + * Checks for availability of the Firefox extension by looking for the id which the extension + * will append to the body of the document. Unfortunately there is no callback + * API implemented by Firefox which will allow other domains to see if an + * extension is installed using `InstallTrigger.install`. Only priviledged + * domains may use the callback. + * + * @param {int} How long of a timespan the function should check for the extension install at intervalSecs interval rate + * @return {promise} + */ + FirefoxExtension.prototype.detectInstalled = function(maxTimeout) { + var defer = $q.defer(); + var that = this; + + var intervalPromise = $interval(function() { + if (isAvailable()) { + console.log("Auto install success Firefox extension"); + $interval.cancel(intervalPromise); + that.initialize(); + defer.resolve("Auto install success Firefox extension"); + } else if (intervalCount * intervalSecs >= maxTimeout) { + $interval.cancel(intervalPromise); + defer.reject("Timeout while waiting for extension to become available"); + } + intervalCount++; + }, intervalSecs); + + return defer.promise; + }; + + // Create extension api and wait for messages. + var extension = new FirefoxExtension(); + + // Expose. + return extension; + + }]; + +}); diff --git a/static/js/services/screensharing.js b/static/js/services/screensharing.js index 2cc5c54f..b5f3cfc5 100644 --- a/static/js/services/screensharing.js +++ b/static/js/services/screensharing.js @@ -32,8 +32,11 @@ define(['underscore', 'text!partials/screensharedialogff.html', 'webrtc.adapter' }; }]; + var GLOBAL_SCREENSHARING_START_EVENT = new Event('webrtcStartScreensharing'); + var GLOBAL_SCREENSHARING_STOP_EVENT = new Event('webrtcStopScreensharing'); + // screensharing - return ["$window", "$q", "$timeout", "chromeExtension", "dialogs", "$templateCache", function($window, $q, $timeout, chromeExtension, dialogs, $templateCache) { + return ["$window", "$q", "$timeout", "chromeExtension", "firefoxExtension", "dialogs", "$templateCache", function($window, $q, $timeout, chromeExtension, firefoxExtension, dialogs, $templateCache) { $templateCache.put('/dialogs/screensharedialogff.html', screenshareDialogFF); @@ -43,6 +46,18 @@ define(['underscore', 'text!partials/screensharedialogff.html', 'webrtc.adapter' chromeExtension.e.on("available", _.bind(function() { this.initialize(); }, this)); + firefoxExtension.e.on("available", _.bind(function() { + this.initialize(); + }, this)); + }; + + Screensharing.prototype.globalNotify = { + screensharingStart: function() { + $window.dispatchEvent(GLOBAL_SCREENSHARING_START_EVENT); + }, + screensharingStop: function() { + $window.dispatchEvent(GLOBAL_SCREENSHARING_STOP_EVENT); + } }; Screensharing.prototype.initialize = function() { @@ -53,6 +68,29 @@ define(['underscore', 'text!partials/screensharedialogff.html', 'webrtc.adapter' // Define our helpers. this.prepare = null; this.cancel = null; + var that = this; + + var selectFirefoxScreenToShare = function(options) { + // To work, the current domain must be whitelisted in + // media.getusermedia.screensharing.allowed_domains (about:config). + // See https://wiki.mozilla.org/Screensharing for reference. + that.globalNotify.screensharingStart(); + var d = $q.defer(); + var dlg = dialogs.create('/dialogs/screensharedialogff.html', screenshareDialogFFController, {selection: "screen"}, {}); + dlg.result.then(function(source) { + if (source) { + var opts = _.extend({ + mediaSource: source + }, options); + d.resolve(opts); + } else { + d.resolve(null); + } + }, function(err) { + d.resolve(null); + }); + return d.promise; + }; // Chrome support. if ($window.webrtcDetectedBrowser === "chrome") { @@ -133,44 +171,29 @@ define(['underscore', 'text!partials/screensharedialogff.html', 'webrtc.adapter' } - } else if ($window.webrtcDetectedBrowser === "firefox") { + } else if ($window.webrtcDetectedBrowser === "firefox" && $window.webrtcDetectedVersion >= 36) { // Firefox 36 got screen sharing support. // See https://bugzilla.mozilla.org/show_bug.cgi?id=923225 - if ($window.webrtcDetectedVersion >= 36) { + this.prepare = function(options) { + return selectFirefoxScreenToShare(options); + }; + if (firefoxExtension.available) { + this.supported = true; + } else if (!firefoxExtension.autoinstall.force) { this.supported = true; - this.prepare = function(options) { - // To work, the current domain must be whitelisted in - // media.getusermedia.screensharing.allowed_domains (about:config). - // See https://wiki.mozilla.org/Screensharing for reference. - var d = $q.defer(); - var dlg = dialogs.create('/dialogs/screensharedialogff.html', screenshareDialogFFController, {selection: "screen"}, {}); - dlg.result.then(function(source) { - if (source) { - var opts = _.extend({ - mediaSource: source - }, options); - d.resolve(opts); - } else { - d.resolve(null); - } - }, function(err) { - d.resolve(null); - }); - return d.promise; - }; } } else { // No support for screen sharing. } + var waiting = false; + var prepareAlternative = this.prepare; + // Auto install support. if (!this.supported && chromeExtension.autoinstall.install) { this.supported = this.autoinstall = true; - var that = this; - var waiting = false; - var prepareAlternative = this.prepare; this.prepare = function(options) { var d = $q.defer(); var install = chromeExtension.autoinstall.install(); @@ -230,6 +253,46 @@ define(['underscore', 'text!partials/screensharedialogff.html', 'webrtc.adapter' } waiting = false; }; + } else if (!this.supported && firefoxExtension.autoinstall.install) { + this.supported = this.autoinstall = true; + this.prepare = function(options) { + var d = $q.defer(); + var install = firefoxExtension.autoinstall.install(); + install.then(function() { + var starter = function() { + var prepare = selectFirefoxScreenToShare(options); + prepare.then(function(id) { + d.resolve(id); + }, function(err) { + d.reject(err); + }); + }; + firefoxExtension.detectInstalled(30000).then(function() { + starter(); + }, function(reason) { + d.reject(reason); + }); + }, function(err) { + console.log("Auto install of extension failed.", err); + if (prepareAlternative) { + var alternative = prepareAlternative(options); + alternative.then(function(id) { + d.resolve(id); + }, function() { + d.reject(err); + }); + } else { + d.reject(err); + } + }); + return d.promise; + }; + this.cancel = function() { + if (firefoxExtension.autoinstall.cancel) { + firefoxExtension.autoinstall.cancel(); + } + waiting = false; + }; } else { this.autoinstall = false; } diff --git a/static/js/services/services.js b/static/js/services/services.js index 4fa9919a..a5de387c 100644 --- a/static/js/services/services.js +++ b/static/js/services/services.js @@ -70,7 +70,8 @@ define([ 'services/mediadevices', 'services/sandbox', 'services/dummystream', - 'services/usermedia'], function(_, + 'services/usermedia', + 'services/firefoxextension'], function(_, desktopNotify, playSound, safeApply, @@ -118,7 +119,8 @@ modules, mediaDevices, sandbox, dummyStream, -userMedia) { +userMedia, +firefoxExtension) { var services = { desktopNotify: desktopNotify, @@ -168,7 +170,8 @@ userMedia) { mediaDevices: mediaDevices, sandbox: sandbox, dummyStream: dummyStream, - userMedia: userMedia + userMedia: userMedia, + firefoxExtension: firefoxExtension }; var initialize = function(angModule) {