--- a/.eslintignore
+++ b/.eslintignore
@@ -210,16 +210,17 @@ dom/imptests/**
dom/interfaces/**
dom/ipc/**
dom/json/**
dom/jsurl/**
dom/locales/**
dom/manifest/**
dom/mathml/**
dom/media/**
+!dom/media/*.js*
dom/messagechannel/**
dom/network/**
dom/notification/**
dom/offline/**
dom/payments/**
dom/performance/**
dom/permission/**
dom/plugins/**
@@ -246,16 +247,19 @@ dom/webidl/**
dom/workers/**
dom/worklet/**
dom/xbl/**
dom/xhr/**
dom/xml/**
dom/xslt/**
dom/xul/**
+# Third-party
+dom/media/webvtt/**
+
# Exclude everything but self-hosted JS
js/ductwork/**
js/examples/**
js/ipc/**
js/public/**
js/xpconnect/**
js/src/devtools/**
js/src/octane/**
--- a/dom/media/IdpSandbox.jsm
+++ b/dom/media/IdpSandbox.jsm
@@ -1,113 +1,113 @@
/* 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/. */
-'use strict';
+"use strict";
const {
classes: Cc,
interfaces: Ci,
utils: Cu,
results: Cr
} = Components;
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import('resource://gre/modules/NetUtil.jsm');
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/** This little class ensures that redirects maintain an https:// origin */
function RedirectHttpsOnly() {}
RedirectHttpsOnly.prototype = {
- asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
- if (newChannel.URI.scheme !== 'https') {
+ asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
+ if (newChannel.URI.scheme !== "https") {
callback.onRedirectVerifyCallback(Cr.NS_ERROR_ABORT);
} else {
callback.onRedirectVerifyCallback(Cr.NS_OK);
}
},
- getInterface: function(iid) {
+ getInterface(iid) {
return this.QueryInterface(iid);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink])
};
/** This class loads a resource into a single string. ResourceLoader.load() is
* the entry point. */
function ResourceLoader(res, rej) {
this.resolve = res;
this.reject = rej;
- this.data = '';
+ this.data = "";
}
/** Loads the identified https:// URL. */
ResourceLoader.load = function(uri, doc) {
return new Promise((resolve, reject) => {
let listener = new ResourceLoader(resolve, reject);
let ioChannel = NetUtil.newChannel({
- uri: uri,
+ uri,
loadingNode: doc,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_SCRIPT
});
ioChannel.loadGroup = doc.documentLoadGroup.QueryInterface(Ci.nsILoadGroup);
ioChannel.notificationCallbacks = new RedirectHttpsOnly();
ioChannel.asyncOpen2(listener);
});
};
ResourceLoader.prototype = {
- onDataAvailable: function(request, context, input, offset, count) {
- let stream = Cc['@mozilla.org/scriptableinputstream;1']
+ onDataAvailable(request, context, input, offset, count) {
+ let stream = Cc["@mozilla.org/scriptableinputstream;1"]
.createInstance(Ci.nsIScriptableInputStream);
stream.init(input);
this.data += stream.read(count);
},
- onStartRequest: function (request, context) {},
+ onStartRequest(request, context) {},
- onStopRequest: function(request, context, status) {
+ onStopRequest(request, context, status) {
if (Components.isSuccessCode(status)) {
var statusCode = request.QueryInterface(Ci.nsIHttpChannel).responseStatus;
if (statusCode === 200) {
- this.resolve({ request: request, data: this.data });
+ this.resolve({ request, data: this.data });
} else {
- this.reject(new Error('Non-200 response from server: ' + statusCode));
+ this.reject(new Error("Non-200 response from server: " + statusCode));
}
} else {
- this.reject(new Error('Load failed: ' + status));
+ this.reject(new Error("Load failed: " + status));
}
},
- getInterface: function(iid) {
+ getInterface(iid) {
return this.QueryInterface(iid);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener])
};
/**
* A simple implementation of the WorkerLocation interface.
*/
function createLocationFromURI(uri) {
return {
href: uri.spec,
- protocol: uri.scheme + ':',
+ protocol: uri.scheme + ":",
host: uri.host + ((uri.port >= 0) ?
- (':' + uri.port) : ''),
+ (":" + uri.port) : ""),
port: uri.port,
hostname: uri.host,
- pathname: uri.path.replace(/[#\?].*/, ''),
- search: uri.path.replace(/^[^\?]*/, '').replace(/#.*/, ''),
- hash: uri.hasRef ? ('#' + uri.ref) : '',
+ pathname: uri.path.replace(/[#\?].*/, ""),
+ search: uri.path.replace(/^[^\?]*/, "").replace(/#.*/, ""),
+ hash: uri.hasRef ? ("#" + uri.ref) : "",
origin: uri.prePath,
- toString: function() {
+ toString() {
return uri.spec;
}
};
}
/**
* A javascript sandbox for running an IdP.
*
@@ -119,155 +119,155 @@ function createLocationFromURI(uri) {
function IdpSandbox(domain, protocol, win) {
this.source = IdpSandbox.createIdpUri(domain, protocol || "default");
this.active = null;
this.sandbox = null;
this.window = win;
}
IdpSandbox.checkDomain = function(domain) {
- if (!domain || typeof domain !== 'string') {
- throw new Error('Invalid domain for identity provider: ' +
- 'must be a non-zero length string');
+ if (!domain || typeof domain !== "string") {
+ throw new Error("Invalid domain for identity provider: " +
+ "must be a non-zero length string");
}
};
/**
* Checks that the IdP protocol is superficially sane. In particular, we don't
* want someone adding relative paths (e.g., '../../myuri'), which could be used
* to move outside of /.well-known/ and into space that they control.
*/
IdpSandbox.checkProtocol = function(protocol) {
- let message = 'Invalid protocol for identity provider: ';
- if (!protocol || typeof protocol !== 'string') {
- throw new Error(message + 'must be a non-zero length string');
+ let message = "Invalid protocol for identity provider: ";
+ if (!protocol || typeof protocol !== "string") {
+ throw new Error(message + "must be a non-zero length string");
}
if (decodeURIComponent(protocol).match(/[\/\\]/)) {
throw new Error(message + "must not include '/' or '\\'");
}
};
/**
* Turns a domain and protocol into a URI. This does some aggressive checking
* to make sure that we aren't being fooled somehow. Throws on fooling.
*/
IdpSandbox.createIdpUri = function(domain, protocol) {
IdpSandbox.checkDomain(domain);
IdpSandbox.checkProtocol(protocol);
- let message = 'Invalid IdP parameters: ';
+ let message = "Invalid IdP parameters: ";
try {
- let wkIdp = 'https://' + domain + '/.well-known/idp-proxy/' + protocol;
- let ioService = Components.classes['@mozilla.org/network/io-service;1']
+ let wkIdp = "https://" + domain + "/.well-known/idp-proxy/" + protocol;
+ let ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
let uri = ioService.newURI(wkIdp);
if (uri.hostPort !== domain) {
- throw new Error(message + 'domain is invalid');
+ throw new Error(message + "domain is invalid");
}
- if (uri.path.indexOf('/.well-known/idp-proxy/') !== 0) {
- throw new Error(message + 'must produce a /.well-known/idp-proxy/ URI');
+ if (uri.path.indexOf("/.well-known/idp-proxy/") !== 0) {
+ throw new Error(message + "must produce a /.well-known/idp-proxy/ URI");
}
return uri;
} catch (e) {
- if (typeof e.result !== 'undefined' &&
+ if (typeof e.result !== "undefined" &&
e.result === Cr.NS_ERROR_MALFORMED_URI) {
- throw new Error(message + 'must produce a valid URI');
+ throw new Error(message + "must produce a valid URI");
}
}
};
IdpSandbox.prototype = {
- isSame: function(domain, protocol) {
+ isSame(domain, protocol) {
return this.source.spec === IdpSandbox.createIdpUri(domain, protocol).spec;
},
- start: function() {
+ start() {
if (!this.active) {
this.active = ResourceLoader.load(this.source, this.window.document)
.then(result => this._createSandbox(result));
}
return this.active;
},
// Provides the sandbox with some useful facilities. Initially, this is only
// a minimal set; it is far easier to add more as the need arises, than to
// take them back if we discover a mistake.
- _populateSandbox: function(uri) {
+ _populateSandbox(uri) {
this.sandbox.location = Cu.cloneInto(createLocationFromURI(uri),
this.sandbox,
{ cloneFunctions: true });
},
- _createSandbox: function(result) {
+ _createSandbox(result) {
let principal = Services.scriptSecurityManager
.getChannelResultPrincipal(result.request);
this.sandbox = Cu.Sandbox(principal, {
- sandboxName: 'IdP-' + this.source.host,
+ sandboxName: "IdP-" + this.source.host,
wantComponents: false,
wantExportHelpers: false,
wantGlobalProperties: [
- 'indexedDB', 'XMLHttpRequest', 'TextEncoder', 'TextDecoder',
- 'URL', 'URLSearchParams', 'atob', 'btoa', 'Blob', 'crypto',
- 'rtcIdentityProvider', 'fetch'
+ "indexedDB", "XMLHttpRequest", "TextEncoder", "TextDecoder",
+ "URL", "URLSearchParams", "atob", "btoa", "Blob", "crypto",
+ "rtcIdentityProvider", "fetch"
]
});
let registrar = this.sandbox.rtcIdentityProvider;
if (!Cu.isXrayWrapper(registrar)) {
- throw new Error('IdP setup failed');
+ throw new Error("IdP setup failed");
}
// have to use the ultimate URI, not the starting one to avoid
// that origin stealing from the one that redirected to it
this._populateSandbox(result.request.URI);
try {
Cu.evalInSandbox(result.data, this.sandbox,
- 'latest', result.request.URI.spec, 1);
+ "latest", result.request.URI.spec, 1);
} catch (e) {
// These can be passed straight on, because they are explicitly labelled
// as being IdP errors by the IdP and we drop line numbers as a result.
- if (e.name === 'IdpError' || e.name === 'IdpLoginError') {
+ if (e.name === "IdpError" || e.name === "IdpLoginError") {
throw e;
}
this._logError(e);
- throw new Error('Error in IdP, check console for details');
+ throw new Error("Error in IdP, check console for details");
}
if (!registrar.hasIdp) {
- throw new Error('IdP failed to call rtcIdentityProvider.register()');
+ throw new Error("IdP failed to call rtcIdentityProvider.register()");
}
return registrar;
},
// Capture all the details from the error and log them to the console. This
// can't rethrow anything else because that could leak information about the
// internal workings of the IdP across origins.
- _logError: function(e) {
+ _logError(e) {
let winID = this.window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
let scriptError = Cc["@mozilla.org/scripterror;1"]
.createInstance(Ci.nsIScriptError);
scriptError.initWithWindowID(e.message, e.fileName, null,
e.lineNumber, e.columnNumber,
Ci.nsIScriptError.errorFlag,
"content javascript", winID);
- let consoleService = Cc['@mozilla.org/consoleservice;1']
+ let consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
consoleService.logMessage(scriptError);
},
- stop: function() {
+ stop() {
if (this.sandbox) {
Cu.nukeSandbox(this.sandbox);
}
this.sandbox = null;
this.active = null;
},
- toString: function() {
+ toString() {
return this.source.spec;
}
};
-this.EXPORTED_SYMBOLS = ['IdpSandbox'];
+this.EXPORTED_SYMBOLS = ["IdpSandbox"];
this.IdpSandbox = IdpSandbox;
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -43,17 +43,17 @@ const PC_DTMF_SENDER_CID = Components.ID
function logMsg(msg, file, line, flag, winID) {
let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
scriptError.initWithWindowID(msg, file, null, line, 0, flag,
"content javascript", winID);
let console = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);
console.logMessage(scriptError);
-};
+}
let setupPrototype = (_class, dict) => {
_class.prototype.classDescription = _class.name;
Object.assign(_class.prototype, dict);
}
// Global list of PeerConnection objects, so they can be cleaned up when
// a page is torn down. (Maps inner window ID to an array of PC objects).
@@ -106,26 +106,26 @@ class GlobalPCList {
}
}
removeNullRefs(winID) {
if (this._list[winID] === undefined) {
return;
}
this._list[winID] = this._list[winID].filter(
- function (e,i,a) { return e.get() !== null; });
+ function(e, i, a) { return e.get() !== null; });
if (this._list[winID].length === 0) {
delete this._list[winID];
}
}
hasActivePeerConnection(winID) {
this.removeNullRefs(winID);
- return this._list[winID] ? true : false;
+ return !!this._list[winID];
}
handleGMPCrash(data) {
let broadcastPluginCrash = function(list, winID, pluginID, pluginName) {
if (list.hasOwnProperty(winID)) {
list[winID].forEach(function(pcref) {
let pc = pcref.get();
if (pc) {
@@ -212,17 +212,17 @@ class GlobalPCList {
}
setupPrototype(GlobalPCList, {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsIMessageListener,
Ci.nsISupportsWeakReference,
Ci.IPeerConnectionManager]),
classID: PC_MANAGER_CID,
_xpcom_factory: {
- createInstance: function(outer, iid) {
+ createInstance(outer, iid) {
if (outer) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return _globalPCList.QueryInterface(iid);
}
}
});
@@ -333,21 +333,21 @@ class RTCStatsReport {
get mozPcid() { return this._pcid; }
}
setupPrototype(RTCStatsReport, {
classID: PC_STATS_CID,
contractID: PC_STATS_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
_specToLegacyFieldMapping: {
- 'inbound-rtp' : 'inboundrtp',
- 'outbound-rtp':'outboundrtp',
- 'candidate-pair':'candidatepair',
- 'local-candidate':'localcandidate',
- 'remote-candidate':'remotecandidate'
+ "inbound-rtp": "inboundrtp",
+ "outbound-rtp": "outboundrtp",
+ "candidate-pair": "candidatepair",
+ "local-candidate": "localcandidate",
+ "remote-candidate": "remotecandidate"
}
});
class RTCPeerConnection {
constructor() {
this._senders = [];
this._receivers = [];
@@ -612,17 +612,17 @@ class RTCPeerConnection {
server.urls = [server.urls];
} else if (!server.urls && server.url) {
// TODO: Remove support for legacy iceServer.url eventually (Bug 1116766)
server.urls = [server.url];
this.logWarning("RTCIceServer.url is deprecated! Use urls instead.");
}
});
- let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
+ let ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
let nicerNewURI = uriStr => {
try {
return ios.newURI(uriStr);
} catch (e) {
if (e.result == Cr.NS_ERROR_MALFORMED_URI) {
throw new this._win.DOMException(msg + " - malformed URI: " + uriStr,
"SyntaxError");
@@ -632,41 +632,41 @@ class RTCPeerConnection {
var stunServers = 0;
iceServers.forEach(({ urls, username, credential, credentialType }) => {
if (!urls) {
throw new this._win.DOMException(msg + " - missing urls", "InvalidAccessError");
}
urls.map(url => nicerNewURI(url)).forEach(({ scheme, spec }) => {
- if (scheme in { turn:1, turns:1 }) {
+ if (scheme in { turn: 1, turns: 1 }) {
if (username == undefined) {
throw new this._win.DOMException(msg + " - missing username: " + spec,
"InvalidAccessError");
}
if (credential == undefined) {
throw new this._win.DOMException(msg + " - missing credential: " + spec,
"InvalidAccessError");
}
if (credentialType != "password") {
- this.logWarning("RTCConfiguration TURN credentialType \""+
+ this.logWarning("RTCConfiguration TURN credentialType \"" +
credentialType +
- "\" is not yet implemented. Treating as password."+
+ "\" is not yet implemented. Treating as password." +
" https://bugzil.la/1247616");
}
this._hasTurnServer = true;
stunServers += 1;
- } else if (scheme in { stun:1, stuns:1 }) {
+ } else if (scheme in { stun: 1, stuns: 1 }) {
this._hasStunServer = true;
stunServers += 1;
} else {
throw new this._win.DOMException(msg + " - improper scheme: " + scheme,
"SyntaxError");
}
- if (scheme in { stuns:1, turns:1 }) {
+ if (scheme in { stuns: 1, turns: 1 }) {
this.logWarning(scheme.toUpperCase() + " is not yet supported.");
}
if (stunServers >= 5) {
this.logError("Using five or more STUN/TURN servers causes problems");
} else if (stunServers > 2) {
this.logWarning("Using more than two STUN/TURN servers slows down discovery");
}
});
@@ -696,21 +696,21 @@ class RTCPeerConnection {
logErrorAndCallOnError(e) {
this.logMsg(e.message, e.fileName, e.lineNumber, Ci.nsIScriptError.exceptionFlag);
// Safely call onerror directly if present (necessary for testing)
try {
if (typeof this._win.onerror === "function") {
this._win.onerror(e.message, e.fileName, e.lineNumber);
}
- } catch(e) {
+ } catch (e) {
// If onerror itself throws, service it.
try {
this.logMsg(e.message, e.fileName, e.lineNumber, Ci.nsIScriptError.errorFlag);
- } catch(e) {}
+ } catch (e) {}
}
}
logError(msg) {
this.logStackMsg(msg, Ci.nsIScriptError.errorFlag);
}
logWarning(msg) {
@@ -732,26 +732,26 @@ class RTCPeerConnection {
setEH(type, handler) {
this.__DOM_IMPL__.setEventHandler(type, handler);
}
makeGetterSetterEH(name) {
Object.defineProperty(this, name,
{
- get:function() { return this.getEH(name); },
- set:function(h) { return this.setEH(name, h); }
+ get() { return this.getEH(name); },
+ set(h) { return this.setEH(name, h); }
});
}
makeLegacyGetterSetterEH(name, msg) {
Object.defineProperty(this, name,
{
- get:function() { return this.getEH(name); },
- set:function(h) {
+ get() { return this.getEH(name); },
+ set(h) {
this.logWarning(name + " is deprecated! " + msg);
return this.setEH(name, h);
}
});
}
createOffer(optionsOrOnSucc, onErr, options) {
// This entry-point handles both new and legacy call sig. Decipher which one
@@ -906,17 +906,17 @@ class RTCPeerConnection {
if (msg) {
// Set new identity and generate an event.
this._impl.peerIdentity = msg.identity;
this._resolvePeerIdentity(Cu.cloneInto({
idp: this._remoteIdp.provider,
name: msg.identity
}, this._win));
}
- } catch(e) {
+ } catch (e) {
this._rejectPeerIdentity(e);
// If we don't expect a specific peer identity, failure to get a valid
// peer identity is not a terminal state, so replace the promise to
// allow another attempt.
if (!this._impl.peerIdentity) {
this._resetPeerIdentityPromise();
}
throw e;
@@ -1191,17 +1191,17 @@ class RTCPeerConnection {
}
return new this._win.RTCSessionDescription({ type: this._remoteType, sdp });
}
get peerIdentity() { return this._peerIdentity; }
get idpLoginUrl() { return this._localIdp.idpLoginUrl; }
get id() { return this._impl.id; }
set id(s) { this._impl.id = s; }
- get iceGatheringState() { return this._iceGatheringState; }
+ get iceGatheringState() { return this._iceGatheringState; }
get iceConnectionState() { return this._iceConnectionState; }
get signalingState() {
// checking for our local pc closed indication
// before invoking the pc methods.
if (this._closed) {
return "closed";
}
@@ -1413,44 +1413,41 @@ class PeerConnectionObserver {
//
// closed All of the RTCIceTransports are in the closed state.
handleIceConnectionStateChange(iceConnectionState) {
let pc = this._dompc;
if (pc.iceConnectionState === iceConnectionState) {
return;
}
- if (pc.iceConnectionState === 'new') {
+ if (pc.iceConnectionState === "new") {
var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
- if (iceConnectionState === 'checking') {
+ if (iceConnectionState === "checking") {
checking_histogram.add(true);
- } else if (iceConnectionState === 'failed') {
+ } else if (iceConnectionState === "failed") {
checking_histogram.add(false);
}
- } else if (pc.iceConnectionState === 'checking') {
+ } else if (pc.iceConnectionState === "checking") {
var success_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
- if (iceConnectionState === 'completed' ||
- iceConnectionState === 'connected') {
+ if (iceConnectionState === "completed" ||
+ iceConnectionState === "connected") {
success_histogram.add(true);
- } else if (iceConnectionState === 'failed') {
+ } else if (iceConnectionState === "failed") {
success_histogram.add(false);
}
}
- if (iceConnectionState === 'failed') {
+ if (iceConnectionState === "failed") {
if (!pc._hasStunServer) {
pc.logError("ICE failed, add a STUN server and see about:webrtc for more details");
- }
- else if (!pc._hasTurnServer) {
+ } else if (!pc._hasTurnServer) {
pc.logError("ICE failed, add a TURN server and see about:webrtc for more details");
- }
- else if (pc._hasTurnServer && !pc._iceGatheredRelayCandidates) {
+ } else if (pc._hasTurnServer && !pc._iceGatheredRelayCandidates) {
pc.logError("ICE failed, your TURN server appears to be broken, see about:webrtc for more details");
- }
- else {
+ } else {
pc.logError("ICE failed, see about:webrtc for more details");
}
}
pc.changeIceConnectionState(iceConnectionState);
}
// This method is responsible for updating iceGatheringState. This
--- a/dom/media/PeerConnectionIdp.jsm
+++ b/dom/media/PeerConnectionIdp.jsm
@@ -1,341 +1,341 @@
/* jshint moz:true, browser:true */
/* 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/. */
-this.EXPORTED_SYMBOLS = ['PeerConnectionIdp'];
+this.EXPORTED_SYMBOLS = ["PeerConnectionIdp"];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-XPCOMUtils.defineLazyModuleGetter(this, 'IdpSandbox',
- 'resource://gre/modules/media/IdpSandbox.jsm');
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "IdpSandbox",
+ "resource://gre/modules/media/IdpSandbox.jsm");
/**
* Creates an IdP helper.
*
* @param win (object) the window we are working for
* @param timeout (int) the timeout in milliseconds
*/
function PeerConnectionIdp(win, timeout) {
this._win = win;
this._timeout = timeout || 5000;
this.provider = null;
this._resetAssertion();
}
(function() {
- PeerConnectionIdp._mLinePattern = new RegExp('^m=', 'm');
+ PeerConnectionIdp._mLinePattern = new RegExp("^m=", "m");
// attributes are funny, the 'a' is case sensitive, the name isn't
- let pattern = '^a=[iI][dD][eE][nN][tT][iI][tT][yY]:(\\S+)';
- PeerConnectionIdp._identityPattern = new RegExp(pattern, 'm');
- pattern = '^a=[fF][iI][nN][gG][eE][rR][pP][rR][iI][nN][tT]:(\\S+) (\\S+)';
- PeerConnectionIdp._fingerprintPattern = new RegExp(pattern, 'm');
+ let pattern = "^a=[iI][dD][eE][nN][tT][iI][tT][yY]:(\\S+)";
+ PeerConnectionIdp._identityPattern = new RegExp(pattern, "m");
+ pattern = "^a=[fF][iI][nN][gG][eE][rR][pP][rR][iI][nN][tT]:(\\S+) (\\S+)";
+ PeerConnectionIdp._fingerprintPattern = new RegExp(pattern, "m");
})();
PeerConnectionIdp.prototype = {
get enabled() {
return !!this._idp;
},
- _resetAssertion: function() {
+ _resetAssertion() {
this.assertion = null;
this.idpLoginUrl = null;
},
- setIdentityProvider: function(provider, protocol, username) {
+ setIdentityProvider(provider, protocol, username) {
this._resetAssertion();
this.provider = provider;
- this.protocol = protocol || 'default';
+ this.protocol = protocol || "default";
this.username = username;
if (this._idp) {
if (this._idp.isSame(provider, protocol)) {
return; // noop
}
this._idp.stop();
}
this._idp = new IdpSandbox(provider, protocol, this._win);
},
// start the IdP and do some error fixup
- start: function() {
+ start() {
return this._idp.start()
.catch(e => {
- throw new this._win.DOMException(e.message, 'IdpError');
+ throw new this._win.DOMException(e.message, "IdpError");
});
},
- close: function() {
+ close() {
this._resetAssertion();
this.provider = null;
this.protocol = null;
if (this._idp) {
this._idp.stop();
this._idp = null;
}
},
- _getFingerprintsFromSdp: function(sdp) {
+ _getFingerprintsFromSdp(sdp) {
let fingerprints = {};
let m = sdp.match(PeerConnectionIdp._fingerprintPattern);
while (m) {
fingerprints[m[0]] = { algorithm: m[1], digest: m[2] };
sdp = sdp.substring(m.index + m[0].length);
m = sdp.match(PeerConnectionIdp._fingerprintPattern);
}
return Object.keys(fingerprints).map(k => fingerprints[k]);
},
- _isValidAssertion: function(assertion) {
+ _isValidAssertion(assertion) {
return assertion && assertion.idp &&
- typeof assertion.idp.domain === 'string' &&
+ typeof assertion.idp.domain === "string" &&
(!assertion.idp.protocol ||
- typeof assertion.idp.protocol === 'string') &&
- typeof assertion.assertion === 'string';
+ typeof assertion.idp.protocol === "string") &&
+ typeof assertion.assertion === "string";
},
- _getIdentityFromSdp: function(sdp) {
+ _getIdentityFromSdp(sdp) {
// a=identity is session level
let idMatch;
let mLineMatch = sdp.match(PeerConnectionIdp._mLinePattern);
if (mLineMatch) {
let sessionLevel = sdp.substring(0, mLineMatch.index);
idMatch = sessionLevel.match(PeerConnectionIdp._identityPattern);
}
if (!idMatch) {
return; // undefined === no identity
}
let assertion;
try {
assertion = JSON.parse(atob(idMatch[1]));
} catch (e) {
- throw new this._win.DOMException('invalid identity assertion: ' + e,
- 'InvalidSessionDescriptionError');
+ throw new this._win.DOMException("invalid identity assertion: " + e,
+ "InvalidSessionDescriptionError");
}
if (!this._isValidAssertion(assertion)) {
- throw new this._win.DOMException('assertion missing idp/idp.domain/assertion',
- 'InvalidSessionDescriptionError');
+ throw new this._win.DOMException("assertion missing idp/idp.domain/assertion",
+ "InvalidSessionDescriptionError");
}
return assertion;
},
/**
* Verifies the a=identity line the given SDP contains, if any.
* If the verification succeeds callback is called with the message from the
* IdP proxy as parameter, else (verification failed OR no a=identity line in
* SDP at all) null is passed to callback.
*
* Note that this only verifies that the SDP is coherent. We still rely on
* the fact that the RTCPeerConnection won't connect to a peer if the
* fingerprint of the certificate they offer doesn't appear in the SDP.
*/
- verifyIdentityFromSDP: function(sdp, origin) {
+ verifyIdentityFromSDP(sdp, origin) {
let identity = this._getIdentityFromSdp(sdp);
let fingerprints = this._getFingerprintsFromSdp(sdp);
if (!identity || fingerprints.length <= 0) {
return this._win.Promise.resolve(); // undefined result = no identity
}
this.setIdentityProvider(identity.idp.domain, identity.idp.protocol);
return this._verifyIdentity(identity.assertion, fingerprints, origin);
},
/**
* Checks that the name in the identity provided by the IdP is OK.
*
* @param name (string) the name to validate
* @throws if the name isn't valid
*/
- _validateName: function(name) {
+ _validateName(name) {
let error = msg => {
- throw new this._win.DOMException('assertion name error: ' + msg,
- 'IdpError');
+ throw new this._win.DOMException("assertion name error: " + msg,
+ "IdpError");
};
- if (typeof name !== 'string') {
- error('name not a string');
+ if (typeof name !== "string") {
+ error("name not a string");
}
- let atIdx = name.indexOf('@');
+ let atIdx = name.indexOf("@");
if (atIdx <= 0) {
- error('missing authority in name from IdP');
+ error("missing authority in name from IdP");
}
// no third party assertions... for now
let tail = name.substring(atIdx + 1);
// strip the port number, if present
let provider = this.provider;
- let providerPortIdx = provider.indexOf(':');
+ let providerPortIdx = provider.indexOf(":");
if (providerPortIdx > 0) {
provider = provider.substring(0, providerPortIdx);
}
- let idnService = Components.classes['@mozilla.org/network/idn-service;1']
+ let idnService = Components.classes["@mozilla.org/network/idn-service;1"]
.getService(Components.interfaces.nsIIDNService);
if (idnService.convertUTF8toACE(tail) !==
idnService.convertUTF8toACE(provider)) {
error('name "' + name +
'" doesn\'t match IdP: "' + this.provider + '"');
}
},
/**
* Check the validation response. We are very defensive here when handling
* the message from the IdP proxy. That way, broken IdPs aren't likely to
* cause catastrophic damage.
*/
- _checkValidation: function(validation, sdpFingerprints) {
+ _checkValidation(validation, sdpFingerprints) {
let error = msg => {
- throw new this._win.DOMException('IdP validation error: ' + msg,
- 'IdpError');
+ throw new this._win.DOMException("IdP validation error: " + msg,
+ "IdpError");
};
if (!this.provider) {
- error('IdP closed');
+ error("IdP closed");
}
- if (typeof validation !== 'object' ||
- typeof validation.contents !== 'string' ||
- typeof validation.identity !== 'string') {
- error('no payload in validation response');
+ if (typeof validation !== "object" ||
+ typeof validation.contents !== "string" ||
+ typeof validation.identity !== "string") {
+ error("no payload in validation response");
}
let fingerprints;
try {
fingerprints = JSON.parse(validation.contents).fingerprint;
} catch (e) {
- error('invalid JSON');
+ error("invalid JSON");
}
let isFingerprint = f =>
- (typeof f.digest === 'string') &&
- (typeof f.algorithm === 'string');
+ (typeof f.digest === "string") &&
+ (typeof f.algorithm === "string");
if (!Array.isArray(fingerprints) || !fingerprints.every(isFingerprint)) {
- error('fingerprints must be an array of objects' +
- ' with digest and algorithm attributes');
+ error("fingerprints must be an array of objects" +
+ " with digest and algorithm attributes");
}
// everything in `innerSet` is found in `outerSet`
let isSubsetOf = (outerSet, innerSet, comparator) => {
return innerSet.every(i => {
return outerSet.some(o => comparator(i, o));
});
};
let compareFingerprints = (a, b) => {
return (a.digest === b.digest) && (a.algorithm === b.algorithm);
};
if (!isSubsetOf(fingerprints, sdpFingerprints, compareFingerprints)) {
- error('the fingerprints must be covered by the assertion');
+ error("the fingerprints must be covered by the assertion");
}
this._validateName(validation.identity);
return validation;
},
/**
* Asks the IdP proxy to verify an identity assertion.
*/
- _verifyIdentity: function(assertion, fingerprints, origin) {
+ _verifyIdentity(assertion, fingerprints, origin) {
let p = this.start()
.then(idp => this._wrapCrossCompartmentPromise(
idp.validateAssertion(assertion, origin)))
.then(validation => this._checkValidation(validation, fingerprints));
return this._applyTimeout(p);
},
/**
* Enriches the given SDP with an `a=identity` line. getIdentityAssertion()
* must have already run successfully, otherwise this does nothing to the sdp.
*/
- addIdentityAttribute: function(sdp) {
+ addIdentityAttribute(sdp) {
if (!this.assertion) {
return sdp;
}
// yes, we assume that this matches; if it doesn't something is *wrong*
let match = sdp.match(PeerConnectionIdp._mLinePattern);
return sdp.substring(0, match.index) +
- 'a=identity:' + this.assertion + '\r\n' +
+ "a=identity:" + this.assertion + "\r\n" +
sdp.substring(match.index);
},
/**
* Asks the IdP proxy for an identity assertion. Don't call this unless you
* have checked .enabled, or you really like exceptions. Also, don't call
* this when another call is still running, because it's not certain which
* call will finish first and the final state will be similarly uncertain.
*/
- getIdentityAssertion: function(fingerprint, origin) {
+ getIdentityAssertion(fingerprint, origin) {
if (!this.enabled) {
throw new this._win.DOMException(
- 'no IdP set, call setIdentityProvider() to set one', 'InvalidStateError');
+ "no IdP set, call setIdentityProvider() to set one", "InvalidStateError");
}
- let [algorithm, digest] = fingerprint.split(' ', 2);
+ let [algorithm, digest] = fingerprint.split(" ", 2);
let content = {
fingerprint: [{
- algorithm: algorithm,
- digest: digest
+ algorithm,
+ digest
}]
};
this._resetAssertion();
let p = this.start()
.then(idp => this._wrapCrossCompartmentPromise(
idp.generateAssertion(JSON.stringify(content),
origin, this.username)))
.then(assertion => {
if (!this._isValidAssertion(assertion)) {
- throw new this._win.DOMException('IdP generated invalid assertion',
- 'IdpError');
+ throw new this._win.DOMException("IdP generated invalid assertion",
+ "IdpError");
}
// save the base64+JSON assertion, since that is all that is used
this.assertion = btoa(JSON.stringify(assertion));
return this.assertion;
});
return this._applyTimeout(p);
},
/**
* Promises generated by the sandbox need to be very carefully treated so that
* they can chain into promises in the `this._win` compartment. Results need
* to be cloned across; errors need to be converted.
*/
- _wrapCrossCompartmentPromise: function(sandboxPromise) {
+ _wrapCrossCompartmentPromise(sandboxPromise) {
return new this._win.Promise((resolve, reject) => {
sandboxPromise.then(
result => resolve(Cu.cloneInto(result, this._win)),
e => {
- let message = '' + (e.message || JSON.stringify(e) || 'IdP error');
- if (e.name === 'IdpLoginError') {
- if (typeof e.loginUrl === 'string') {
+ let message = "" + (e.message || JSON.stringify(e) || "IdP error");
+ if (e.name === "IdpLoginError") {
+ if (typeof e.loginUrl === "string") {
this.idpLoginUrl = e.loginUrl;
}
- reject(new this._win.DOMException(message, 'IdpLoginError'));
+ reject(new this._win.DOMException(message, "IdpLoginError"));
} else {
- reject(new this._win.DOMException(message, 'IdpError'));
+ reject(new this._win.DOMException(message, "IdpError"));
}
});
});
},
/**
* Wraps a promise, adding a timeout guard on it so that it can't take longer
* than the specified time. Returns a promise that rejects if the timeout
* elapses before `p` resolves.
*/
- _applyTimeout: function(p) {
+ _applyTimeout(p) {
let timeout = new this._win.Promise(
r => this._win.setTimeout(r, this._timeout))
.then(() => {
- throw new this._win.DOMException('IdP timed out', 'IdpError');
+ throw new this._win.DOMException("IdP timed out", "IdpError");
});
return this._win.Promise.race([ timeout, p ]);
}
};
this.PeerConnectionIdp = PeerConnectionIdp;