gecko-dev/browser/components/loop/standalone/content/js/multiplexGum.js

150 lines
5.1 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var loop = loop || {};
/**
* Monkeypatch getUserMedia in a way that prevents additional camera and
* microphone prompts, at the cost of ignoring all constraints other than
* the first set passed in.
*
* The first call to navigator.getUserMedia (also now aliased to
* multiplexGum.getPermsAndCacheMedia to allow for explicit calling code)
* will cause the underlying gUM implementation to be called.
*
* While permission is pending, subsequent calls will result in the callbacks
* being queued. Once the call succeeds or fails, all queued success or
* failure callbacks will be invoked. Subsequent calls to either function will
* cause the success or failure callback to be invoked immediately.
*/
loop.standaloneMedia = (function() {
"use strict";
function patchSymbolIfExtant(objectName, propertyName, replacement) {
var object;
if (window[objectName]) {
object = window[objectName];
}
if (object && object[propertyName]) {
object[propertyName] = replacement;
}
}
// originalGum _must_ be on navigator; otherwise things blow up.
// For TBPlugin users, navigator.originalGum is set after the TB SDK is loaded.
navigator.originalGum = navigator.getUserMedia ||
navigator.mozGetUserMedia ||
navigator.webkitGetUserMedia;
function _MultiplexGum() {
this.reset();
}
_MultiplexGum.prototype = {
/**
* @see The docs at the top of this file for overall semantics,
* & http://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia
* for params, since this is intended to be purely a passthrough to gUM.
*/
getPermsAndCacheMedia: function(constraints, onSuccess, onError) {
function handleResult(callbacks, param) {
// Operate on a copy of the array in case any of the callbacks
// calls reset, which would cause an infinite-recursion.
this.userMedia.successCallbacks = [];
this.userMedia.errorCallbacks = [];
callbacks.forEach(function(cb) {
if (typeof cb == "function") {
cb(param);
}
})
}
function handleSuccess(localStream) {
this.userMedia.pending = false;
this.userMedia.localStream = localStream;
this.userMedia.error = null;
handleResult.call(this, this.userMedia.successCallbacks.slice(0), localStream);
}
function handleError(error) {
this.userMedia.pending = false;
this.userMedia.error = error;
handleResult.call(this, this.userMedia.errorCallbacks.slice(0), error);
this.error = null;
}
if (this.userMedia.localStream &&
this.userMedia.localStream.ended) {
this.userMedia.localStream = null;
}
this.userMedia.errorCallbacks.push(onError);
this.userMedia.successCallbacks.push(onSuccess);
if (this.userMedia.localStream) {
handleSuccess.call(this, this.userMedia.localStream);
return;
} else if (this.userMedia.error) {
handleError.call(this, this.userMedia.error);
return;
}
if (this.userMedia.pending) {
return;
}
this.userMedia.pending = true;
navigator.originalGum(constraints, handleSuccess.bind(this),
handleError.bind(this));
},
/**
* Reset the cached permissions, callbacks, and media to their default
* state and call any error callbacks to let any waiting callers know
* not to ever expect any more callbacks. We use "PERMISSION_DENIED",
* for lack of a better, more specific gUM code that callers are likely
* to be prepared to handle.
*/
reset: function() {
// When called from the ctor, userMedia is not created yet.
if (this.userMedia) {
this.userMedia.errorCallbacks.forEach(function(cb) {
if (typeof cb == "function") {
cb("PERMISSION_DENIED");
}
});
if (this.userMedia.localStream &&
typeof this.userMedia.localStream.stop == "function") {
this.userMedia.localStream.stop();
}
}
this.userMedia = {
error: null,
localStream: null,
pending: false,
errorCallbacks: [],
successCallbacks: [],
};
}
};
var singletonMultiplexGum = new _MultiplexGum();
function myGetUserMedia() {
// This function is needed to pull in the instance
// of the singleton for tests to overwrite the used instance.
singletonMultiplexGum.getPermsAndCacheMedia.apply(singletonMultiplexGum, arguments);
};
patchSymbolIfExtant("navigator", "mozGetUserMedia", myGetUserMedia);
patchSymbolIfExtant("navigator", "webkitGetUserMedia", myGetUserMedia);
patchSymbolIfExtant("navigator", "getUserMedia", myGetUserMedia);
patchSymbolIfExtant("TBPlugin", "getUserMedia", myGetUserMedia);
return {
multiplexGum: singletonMultiplexGum,
_MultiplexGum: _MultiplexGum,
setSingleton: function(singleton) {
singletonMultiplexGum = singleton;
},
};
})();