jiti-meet/modules/desktopsharing/ScreenObtainer.js

408 lines
13 KiB
JavaScript

/* global config, APP, chrome, $, alert */
/* jshint -W003 */
var RTCBrowserType = require("../RTC/RTCBrowserType");
var AdapterJS = require("../RTC/adapter.screenshare");
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
/**
* Indicates whether the Chrome desktop sharing extension is installed.
* @type {boolean}
*/
var chromeExtInstalled = false;
/**
* Indicates whether an update of the Chrome desktop sharing extension is
* required.
* @type {boolean}
*/
var chromeExtUpdateRequired = false;
/**
* Whether the jidesha extension for firefox is installed for the domain on
* which we are running. Null designates an unknown value.
* @type {null}
*/
var firefoxExtInstalled = null;
/**
* If set to true, detection of an installed firefox extension will be started
* again the next time obtainScreenOnFirefox is called (e.g. next time the
* user tries to enable screen sharing).
*/
var reDetectFirefoxExtension = false;
/**
* Handles obtaining a stream from a screen capture on different browsers.
*/
function ScreenObtainer(){
}
/**
* The EventEmitter to use to emit events.
* @type {null}
*/
ScreenObtainer.prototype.eventEmitter = null;
/**
* Initializes the function used to obtain a screen capture (this.obtainStream).
*
* If the browser is Chrome, it uses the value of
* 'config.desktopSharingChromeMethod' (or 'config.desktopSharing') to * decide
* whether to use the a Chrome extension (if the value is 'ext'), use the
* "screen" media source (if the value is 'webrtc'), or disable screen capture
* (if the value is other).
* Note that for the "screen" media source to work the
* 'chrome://flags/#enable-usermedia-screen-capture' flag must be set.
*/
ScreenObtainer.prototype.init = function(eventEmitter) {
this.eventEmitter = eventEmitter;
var obtainDesktopStream = null;
if (RTCBrowserType.isFirefox())
initFirefoxExtensionDetection();
// TODO remove this, config.desktopSharing is deprecated.
var chromeMethod =
(config.desktopSharingChromeMethod || config.desktopSharing);
if (RTCBrowserType.isTemasysPluginUsed()) {
if (!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature) {
console.info("Screensharing not supported by this plugin version");
} else if (!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
console.info(
"Screensharing not available with Temasys plugin on this site");
} else {
obtainDesktopStream = obtainWebRTCScreen;
console.info("Using Temasys plugin for desktop sharing");
}
} else if (RTCBrowserType.isChrome()) {
if (chromeMethod == "ext") {
if (RTCBrowserType.getChromeVersion() >= 34) {
obtainDesktopStream = obtainScreenFromExtension;
console.info("Using Chrome extension for desktop sharing");
initChromeExtension();
} else {
console.info("Chrome extension not supported until ver 34");
}
} else if (chromeMethod == "webrtc") {
obtainDesktopStream = obtainWebRTCScreen;
console.info("Using Chrome WebRTC for desktop sharing");
}
} else if (RTCBrowserType.isFirefox()) {
if (config.desktopSharingFirefoxDisabled) {
obtainDesktopStream = null;
} else if (window.location.protocol === "http:"){
console.log("Screen sharing is not supported over HTTP. Use of " +
"HTTPS is required.");
obtainDesktopStream = null;
} else {
obtainDesktopStream = this.obtainScreenOnFirefox;
}
}
if (!obtainDesktopStream) {
console.info("Desktop sharing disabled");
}
ScreenObtainer.prototype.obtainStream = obtainDesktopStream;
};
ScreenObtainer.prototype.obtainStream = null;
/**
* Checks whether obtaining a screen capture is supported in the current
* environment.
* @returns {boolean}
*/
ScreenObtainer.prototype.isSupported = function() {
return !!this.obtainStream;
};
/**
* Obtains a desktop stream using getUserMedia.
* For this to work on Chrome, the
* 'chrome://flags/#enable-usermedia-screen-capture' flag must be enabled.
*
* On firefox, the document's domain must be white-listed in the
* 'media.getusermedia.screensharing.allowed_domains' preference in
* 'about:config'.
*/
function obtainWebRTCScreen(streamCallback, failCallback) {
APP.RTC.getUserMediaWithConstraints(
['screen'],
streamCallback,
failCallback
);
}
/**
* Constructs inline install URL for Chrome desktop streaming extension.
* The 'chromeExtensionId' must be defined in config.js.
* @returns {string}
*/
function getWebStoreInstallUrl()
{
//TODO remove chromeExtensionId (deprecated)
return "https://chrome.google.com/webstore/detail/" +
(config.desktopSharingChromeExtId || config.chromeExtensionId);
}
/**
* Checks whether an update of the Chrome extension is required.
* @param minVersion minimal required version
* @param extVersion current extension version
* @returns {boolean}
*/
function isUpdateRequired(minVersion, extVersion) {
try {
var s1 = minVersion.split('.');
var s2 = extVersion.split('.');
var len = Math.max(s1.length, s2.length);
for (var i = 0; i < len; i++) {
var n1 = 0,
n2 = 0;
if (i < s1.length)
n1 = parseInt(s1[i]);
if (i < s2.length)
n2 = parseInt(s2[i]);
if (isNaN(n1) || isNaN(n2)) {
return true;
} else if (n1 !== n2) {
return n1 > n2;
}
}
// will happen if both versions have identical numbers in
// their components (even if one of them is longer, has more components)
return false;
}
catch (e) {
console.error("Failed to parse extension version", e);
APP.UI.messageHandler.showError("dialog.error",
"dialog.detectext");
return true;
}
}
function checkChromeExtInstalled(callback) {
if (!chrome || !chrome.runtime) {
// No API, so no extension for sure
callback(false, false);
return;
}
chrome.runtime.sendMessage(
//TODO: remove chromeExtensionId (deprecated)
(config.desktopSharingChromeExtId || config.chromeExtensionId),
{ getVersion: true },
function (response) {
if (!response || !response.version) {
// Communication failure - assume that no endpoint exists
console.warn(
"Extension not installed?: ", chrome.runtime.lastError);
callback(false, false);
return;
}
// Check installed extension version
var extVersion = response.version;
console.log('Extension version is: ' + extVersion);
//TODO: remove minChromeExtVersion (deprecated)
var updateRequired
= isUpdateRequired(
(config.desktopSharingChromeMinExtVersion ||
config.minChromeExtVersion),
extVersion);
callback(!updateRequired, updateRequired);
}
);
}
function doGetStreamFromExtension(streamCallback, failCallback) {
// Sends 'getStream' msg to the extension.
// Extension id must be defined in the config.
chrome.runtime.sendMessage(
//TODO: remove chromeExtensionId (deprecated)
(config.desktopSharingChromeExtId || config.chromeExtensionId),
{
getStream: true,
//TODO: remove desktopSharingSources (deprecated).
sources: (config.desktopSharingChromeSources ||
config.desktopSharingSources)
},
function (response) {
if (!response) {
failCallback(chrome.runtime.lastError);
return;
}
console.log("Response from extension: " + response);
if (response.streamId) {
APP.RTC.getUserMediaWithConstraints(
['desktop'],
function (stream) {
streamCallback(stream);
},
failCallback,
null, null, null,
response.streamId);
} else {
failCallback("Extension failed to get the stream");
}
}
);
}
/**
* Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop'
* stream for returned stream token.
*/
function obtainScreenFromExtension(streamCallback, failCallback) {
if (chromeExtInstalled) {
doGetStreamFromExtension(streamCallback, failCallback);
} else {
if (chromeExtUpdateRequired) {
alert(
'Jitsi Desktop Streamer requires update. ' +
'Changes will take effect after next Chrome restart.');
}
chrome.webstore.install(
getWebStoreInstallUrl(),
function (arg) {
console.log("Extension installed successfully", arg);
chromeExtInstalled = true;
// We need to give a moment for the endpoint to become available
window.setTimeout(function () {
doGetStreamFromExtension(streamCallback, failCallback);
}, 500);
},
function (arg) {
console.log("Failed to install the extension", arg);
failCallback(arg);
APP.UI.messageHandler.showError("dialog.error",
"dialog.failtoinstall");
}
);
}
}
/**
* Initializes <link rel=chrome-webstore-item /> with extension id set in
* config.js to support inline installs. Host site must be selected as main
* website of published extension.
*/
function initInlineInstalls()
{
$("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
}
function initChromeExtension() {
// Initialize Chrome extension inline installs
initInlineInstalls();
// Check if extension is installed
checkChromeExtInstalled(function (installed, updateRequired) {
chromeExtInstalled = installed;
chromeExtUpdateRequired = updateRequired;
console.info(
"Chrome extension installed: " + chromeExtInstalled +
" updateRequired: " + chromeExtUpdateRequired);
});
}
/**
* Obtains a screen capture stream on Firefox.
* @param callback
* @param errorCallback
*/
ScreenObtainer.prototype.obtainScreenOnFirefox =
function (callback, errorCallback) {
var self = this;
var extensionRequired = false;
if (config.desktopSharingFirefoxMaxVersionExtRequired === -1 ||
(config.desktopSharingFirefoxMaxVersionExtRequired >= 0 &&
RTCBrowserType.getFirefoxVersion() <=
config.desktopSharingFirefoxMaxVersionExtRequired)) {
extensionRequired = true;
console.log("Jidesha extension required on firefox version " +
RTCBrowserType.getFirefoxVersion());
}
if (!extensionRequired || firefoxExtInstalled === true) {
obtainWebRTCScreen(callback, errorCallback);
return;
}
if (reDetectFirefoxExtension) {
reDetectFirefoxExtension = false;
initFirefoxExtensionDetection();
}
// Give it some (more) time to initialize, and assume lack of extension if
// it hasn't.
if (firefoxExtInstalled === null) {
window.setTimeout(
function() {
if (firefoxExtInstalled === null)
firefoxExtInstalled = false;
self.obtainScreenOnFirefox(callback, errorCallback);
},
300
);
console.log("Waiting for detection of jidesha on firefox to finish.");
return;
}
// We need an extension and it isn't installed.
// Make sure we check for the extension when the user clicks again.
firefoxExtInstalled = null;
reDetectFirefoxExtension = true;
// Prompt the user to install the extension
this.eventEmitter.emit(DesktopSharingEventTypes.FIREFOX_EXTENSION_NEEDED,
config.desktopSharingFirefoxExtensionURL);
// Make sure desktopsharing knows that we failed, so that it doesn't get
// stuck in 'switching' mode.
errorCallback('Firefox extension required.');
};
/**
* Starts the detection of an installed jidesha extension for firefox.
*/
function initFirefoxExtensionDetection() {
if (config.desktopSharingFirefoxDisabled) {
return;
}
if (firefoxExtInstalled === false || firefoxExtInstalled === true)
return;
if (!config.desktopSharingFirefoxExtId) {
firefoxExtInstalled = false;
return;
}
var img = document.createElement('img');
img.onload = function(){
console.log("Detected firefox screen sharing extension.");
firefoxExtInstalled = true;
};
img.onerror = function(){
console.log("Detected lack of firefox screen sharing extension.");
firefoxExtInstalled = false;
};
// The jidesha extension exposes an empty image file under the url:
// "chrome://EXT_ID/content/DOMAIN.png"
// Where EXT_ID is the ID of the extension with "@" replaced by ".", and
// DOMAIN is a domain whitelisted by the extension.
var src = "chrome://" +
(config.desktopSharingFirefoxExtId.replace('@', '.')) +
"/content/" + document.location.hostname + ".png";
img.setAttribute('src', src);
}
module.exports = ScreenObtainer;