2014-02-21 04:26:16 +04:00
|
|
|
/* jshint moz:true, browser:true */
|
2012-10-07 09:34:30 +04:00
|
|
|
/* 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";
|
|
|
|
|
2013-01-23 23:21:25 +04:00
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
2012-10-07 09:34:30 +04:00
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
2014-02-11 02:41:46 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp",
|
|
|
|
"resource://gre/modules/media/PeerConnectionIdp.jsm");
|
2014-06-10 02:14:14 +04:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
|
|
|
|
"resource://gre/modules/media/RTCStatsReport.jsm");
|
2015-08-07 22:22:30 +03:00
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
|
|
|
"resource://gre/modules/AppConstants.jsm");
|
2012-10-07 09:34:30 +04:00
|
|
|
|
|
|
|
const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
|
2013-10-14 20:53:56 +04:00
|
|
|
const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
|
2012-10-07 09:34:30 +04:00
|
|
|
const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
|
|
|
|
const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
|
2012-11-10 09:04:27 +04:00
|
|
|
const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
|
2013-10-18 02:00:05 +04:00
|
|
|
const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1";
|
2014-05-23 01:14:56 +04:00
|
|
|
const PC_STATIC_CONTRACT = "@mozilla.org/dom/peerconnectionstatic;1";
|
2014-08-14 05:40:41 +04:00
|
|
|
const PC_SENDER_CONTRACT = "@mozilla.org/dom/rtpsender;1";
|
|
|
|
const PC_RECEIVER_CONTRACT = "@mozilla.org/dom/rtpreceiver;1";
|
2015-08-11 20:48:55 +03:00
|
|
|
const PC_COREQUEST_CONTRACT = "@mozilla.org/dom/createofferrequest;1";
|
2016-09-29 20:26:04 +03:00
|
|
|
const PC_DTMF_SENDER_CONTRACT = "@mozilla.org/dom/rtcdtmfsender;1";
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2014-11-20 03:16:29 +03:00
|
|
|
const PC_CID = Components.ID("{bdc2e533-b308-4708-ac8e-a8bfade6d851}");
|
2013-11-25 23:01:03 +04:00
|
|
|
const PC_OBS_CID = Components.ID("{d1748d4c-7f6a-4dc5-add6-d55b7678537e}");
|
2013-05-08 04:55:21 +04:00
|
|
|
const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
|
|
|
|
const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
|
2012-11-10 09:04:27 +04:00
|
|
|
const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
|
2013-10-18 02:00:05 +04:00
|
|
|
const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}");
|
2014-05-23 01:14:56 +04:00
|
|
|
const PC_STATIC_CID = Components.ID("{0fb47c47-a205-4583-a9fc-cbadf8c95880}");
|
2014-08-14 05:40:41 +04:00
|
|
|
const PC_SENDER_CID = Components.ID("{4fff5d46-d827-4cd4-a970-8fd53977440e}");
|
|
|
|
const PC_RECEIVER_CID = Components.ID("{d974b814-8fde-411c-8c45-b86791b81030}");
|
2015-08-11 20:48:55 +03:00
|
|
|
const PC_COREQUEST_CID = Components.ID("{74b2122d-65a8-4824-aa9e-3d664cb75dc2}");
|
2016-09-29 20:26:04 +03:00
|
|
|
const PC_DTMF_SENDER_CID = Components.ID("{3610C242-654E-11E6-8EC0-6D1BE389A607}");
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-11-09 01:50:24 +03:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2012-10-07 09:34:30 +04:00
|
|
|
// 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).
|
|
|
|
function GlobalPCList() {
|
2013-10-31 22:50:59 +04:00
|
|
|
this._list = {};
|
2012-12-11 16:58:12 +04:00
|
|
|
this._networkdown = false; // XXX Need to query current state somehow
|
2014-05-23 01:14:56 +04:00
|
|
|
this._lifecycleobservers = {};
|
2015-08-07 22:22:30 +03:00
|
|
|
this._nextId = 1;
|
2012-10-07 09:34:30 +04:00
|
|
|
Services.obs.addObserver(this, "inner-window-destroyed", true);
|
2012-11-19 08:53:14 +04:00
|
|
|
Services.obs.addObserver(this, "profile-change-net-teardown", true);
|
|
|
|
Services.obs.addObserver(this, "network:offline-about-to-go-offline", true);
|
2012-12-11 16:58:12 +04:00
|
|
|
Services.obs.addObserver(this, "network:offline-status-changed", true);
|
2014-07-21 11:50:11 +04:00
|
|
|
Services.obs.addObserver(this, "gmp-plugin-crash", true);
|
2015-08-07 22:22:30 +03:00
|
|
|
Services.obs.addObserver(this, "PeerConnection:response:allow", true);
|
|
|
|
Services.obs.addObserver(this, "PeerConnection:response:deny", true);
|
2015-04-27 22:44:36 +03:00
|
|
|
if (Cc["@mozilla.org/childprocessmessagemanager;1"]) {
|
|
|
|
let mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
|
|
|
|
mm.addMessageListener("gmp-plugin-crash", this);
|
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
GlobalPCList.prototype = {
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
2015-04-27 22:44:36 +03:00
|
|
|
Ci.nsIMessageListener,
|
2012-11-10 09:04:27 +04:00
|
|
|
Ci.nsISupportsWeakReference,
|
|
|
|
Ci.IPeerConnectionManager]),
|
|
|
|
classID: PC_MANAGER_CID,
|
|
|
|
_xpcom_factory: {
|
|
|
|
createInstance: function(outer, iid) {
|
|
|
|
if (outer) {
|
2013-01-23 23:21:25 +04:00
|
|
|
throw Cr.NS_ERROR_NO_AGGREGATION;
|
2012-11-10 09:04:27 +04:00
|
|
|
}
|
|
|
|
return _globalPCList.QueryInterface(iid);
|
|
|
|
}
|
|
|
|
},
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2014-05-23 01:14:56 +04:00
|
|
|
notifyLifecycleObservers: function(pc, type) {
|
|
|
|
for (var key of Object.keys(this._lifecycleobservers)) {
|
|
|
|
this._lifecycleobservers[key](pc, pc._winID, type);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-10-07 09:34:30 +04:00
|
|
|
addPC: function(pc) {
|
|
|
|
let winID = pc._winID;
|
|
|
|
if (this._list[winID]) {
|
2013-05-08 04:55:21 +04:00
|
|
|
this._list[winID].push(Cu.getWeakReference(pc));
|
2012-10-07 09:34:30 +04:00
|
|
|
} else {
|
2013-05-08 04:55:21 +04:00
|
|
|
this._list[winID] = [Cu.getWeakReference(pc)];
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
2015-08-07 22:22:30 +03:00
|
|
|
pc._globalPCListId = this._nextId++;
|
2013-01-18 03:11:14 +04:00
|
|
|
this.removeNullRefs(winID);
|
|
|
|
},
|
|
|
|
|
2015-08-07 22:22:30 +03:00
|
|
|
findPC: function(globalPCListId) {
|
|
|
|
for (let winId in this._list) {
|
|
|
|
if (this._list.hasOwnProperty(winId)) {
|
|
|
|
for (let pcref of this._list[winId]) {
|
|
|
|
let pc = pcref.get();
|
|
|
|
if (pc && pc._globalPCListId == globalPCListId) {
|
|
|
|
return pc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-01-18 03:11:14 +04:00
|
|
|
removeNullRefs: function(winID) {
|
2013-10-31 22:50:59 +04:00
|
|
|
if (this._list[winID] === undefined) {
|
2013-01-18 03:11:14 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._list[winID] = this._list[winID].filter(
|
|
|
|
function (e,i,a) { return e.get() !== null; });
|
2013-11-25 23:01:03 +04:00
|
|
|
|
|
|
|
if (this._list[winID].length === 0) {
|
|
|
|
delete this._list[winID];
|
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2012-11-10 09:04:27 +04:00
|
|
|
hasActivePeerConnection: function(winID) {
|
2013-01-18 03:11:14 +04:00
|
|
|
this.removeNullRefs(winID);
|
2012-11-10 09:04:27 +04:00
|
|
|
return this._list[winID] ? true : false;
|
|
|
|
},
|
|
|
|
|
2015-04-27 22:44:36 +03:00
|
|
|
handleGMPCrash: function(data) {
|
|
|
|
let broadcastPluginCrash = function(list, winID, pluginID, pluginName) {
|
|
|
|
if (list.hasOwnProperty(winID)) {
|
|
|
|
list[winID].forEach(function(pcref) {
|
|
|
|
let pc = pcref.get();
|
|
|
|
if (pc) {
|
|
|
|
pc._pc.pluginCrash(pluginID, pluginName);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// a plugin crashed; if it's associated with any of our PCs, fire an
|
|
|
|
// event to the DOM window
|
|
|
|
for (let winId in this._list) {
|
|
|
|
broadcastPluginCrash(this._list, winId, data.pluginID, data.pluginName);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
receiveMessage: function({ name, data }) {
|
|
|
|
if (name == "gmp-plugin-crash") {
|
|
|
|
this.handleGMPCrash(data);
|
2015-04-27 22:44:36 +03:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-10-07 09:34:30 +04:00
|
|
|
observe: function(subject, topic, data) {
|
2013-10-31 22:50:59 +04:00
|
|
|
let cleanupPcRef = function(pcref) {
|
|
|
|
let pc = pcref.get();
|
|
|
|
if (pc) {
|
|
|
|
pc._pc.close();
|
|
|
|
delete pc._observer;
|
|
|
|
pc._pc = null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let cleanupWinId = function(list, winID) {
|
|
|
|
if (list.hasOwnProperty(winID)) {
|
|
|
|
list[winID].forEach(cleanupPcRef);
|
|
|
|
delete list[winID];
|
2012-11-19 08:53:14 +04:00
|
|
|
}
|
2013-10-31 22:50:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
if (topic == "inner-window-destroyed") {
|
2014-05-23 01:14:56 +04:00
|
|
|
let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
|
|
|
cleanupWinId(this._list, winID);
|
|
|
|
|
|
|
|
if (this._lifecycleobservers.hasOwnProperty(winID)) {
|
|
|
|
delete this._lifecycleobservers[winID];
|
|
|
|
}
|
2012-11-19 08:53:14 +04:00
|
|
|
} else if (topic == "profile-change-net-teardown" ||
|
|
|
|
topic == "network:offline-about-to-go-offline") {
|
2016-11-18 10:16:33 +03:00
|
|
|
// As Necko doesn't prevent us from accessing the network we still need to
|
|
|
|
// monitor the network offline/online state here. See bug 1326483
|
2012-12-11 16:58:12 +04:00
|
|
|
this._networkdown = true;
|
2016-11-20 00:47:10 +03:00
|
|
|
} else if (topic == "network:offline-status-changed") {
|
2012-12-11 16:58:12 +04:00
|
|
|
if (data == "offline") {
|
|
|
|
this._networkdown = true;
|
|
|
|
} else if (data == "online") {
|
|
|
|
this._networkdown = false;
|
|
|
|
}
|
2014-07-21 11:50:11 +04:00
|
|
|
} else if (topic == "gmp-plugin-crash") {
|
2015-04-27 22:44:36 +03:00
|
|
|
if (subject instanceof Ci.nsIWritablePropertyBag2) {
|
|
|
|
let pluginID = subject.getPropertyAsUint32("pluginID");
|
|
|
|
let pluginName = subject.getPropertyAsAString("pluginName");
|
|
|
|
let data = { pluginID, pluginName };
|
|
|
|
this.handleGMPCrash(data);
|
2014-07-28 19:41:52 +04:00
|
|
|
}
|
2015-08-07 22:22:30 +03:00
|
|
|
} else if (topic == "PeerConnection:response:allow" ||
|
|
|
|
topic == "PeerConnection:response:deny") {
|
|
|
|
var pc = this.findPC(data);
|
|
|
|
if (pc) {
|
|
|
|
if (topic == "PeerConnection:response:allow") {
|
|
|
|
pc._settlePermission.allow();
|
|
|
|
} else {
|
2016-05-09 05:29:46 +03:00
|
|
|
let err = new pc._win.DOMException("The request is not allowed by " +
|
|
|
|
"the user agent or the platform in the current context.",
|
|
|
|
"NotAllowedError");
|
2015-08-07 22:22:30 +03:00
|
|
|
pc._settlePermission.deny(err);
|
|
|
|
}
|
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
2012-11-19 08:53:14 +04:00
|
|
|
},
|
2013-11-25 23:01:03 +04:00
|
|
|
|
2014-05-23 01:14:56 +04:00
|
|
|
_registerPeerConnectionLifecycleCallback: function(winID, cb) {
|
|
|
|
this._lifecycleobservers[winID] = cb;
|
|
|
|
},
|
2012-10-07 09:34:30 +04:00
|
|
|
};
|
2015-09-15 21:19:45 +03:00
|
|
|
var _globalPCList = new GlobalPCList();
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-11-09 04:22:40 +03:00
|
|
|
function RTCIceCandidate() {}
|
2013-05-08 04:55:21 +04:00
|
|
|
RTCIceCandidate.prototype = {
|
2015-09-28 21:25:04 +03:00
|
|
|
classDescription: "RTCIceCandidate",
|
2012-10-07 09:34:30 +04:00
|
|
|
classID: PC_ICE_CID,
|
2013-05-08 04:55:21 +04:00
|
|
|
contractID: PC_ICE_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
|
|
|
Ci.nsIDOMGlobalPropertyInitializer]),
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
init: function(win) { this._win = win; },
|
|
|
|
|
|
|
|
__init: function(dict) {
|
2016-11-09 04:22:40 +03:00
|
|
|
Object.assign(this, dict);
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-09 01:50:24 +03:00
|
|
|
function RTCSessionDescription() {}
|
2013-05-08 04:55:21 +04:00
|
|
|
RTCSessionDescription.prototype = {
|
2015-09-28 21:25:04 +03:00
|
|
|
classDescription: "RTCSessionDescription",
|
2012-10-07 09:34:30 +04:00
|
|
|
classID: PC_SESSION_CID,
|
2013-05-08 04:55:21 +04:00
|
|
|
contractID: PC_SESSION_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
|
|
|
Ci.nsIDOMGlobalPropertyInitializer]),
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-11-09 01:50:24 +03:00
|
|
|
init: function(win) {
|
|
|
|
this._win = win;
|
|
|
|
this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
|
|
|
},
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-11-09 01:50:24 +03:00
|
|
|
__init: function({ type, sdp }) {
|
|
|
|
Object.assign(this, { _type: type, _sdp: sdp });
|
|
|
|
},
|
|
|
|
|
|
|
|
get type() { return this._type; },
|
|
|
|
set type(type) {
|
|
|
|
this.warn();
|
|
|
|
this._type = type;
|
|
|
|
},
|
|
|
|
|
|
|
|
get sdp() { return this._sdp; },
|
|
|
|
set sdp(sdp) {
|
|
|
|
this.warn();
|
|
|
|
this._sdp = sdp;
|
|
|
|
},
|
|
|
|
|
|
|
|
warn: function() {
|
|
|
|
if (!this._warned) {
|
|
|
|
// Warn once per RTCSessionDescription about deprecated writable usage.
|
|
|
|
this.logWarning("RTCSessionDescription's members are readonly! " +
|
|
|
|
"Writing to them is deprecated and will break soon!");
|
|
|
|
this._warned = true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
logWarning: function(msg) {
|
|
|
|
let err = this._win.Error();
|
|
|
|
logMsg(msg, err.fileName, err.lineNumber, Ci.nsIScriptError.warningFlag,
|
|
|
|
this._winID);
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-12-19 19:34:39 +04:00
|
|
|
function RTCStatsReport(win, dict) {
|
2013-10-18 02:00:05 +04:00
|
|
|
this._win = win;
|
2013-12-19 19:34:39 +04:00
|
|
|
this._pcid = dict.pcid;
|
2014-06-10 02:14:14 +04:00
|
|
|
this._report = convertToRTCStatsReport(dict);
|
2013-10-18 02:00:05 +04:00
|
|
|
}
|
|
|
|
RTCStatsReport.prototype = {
|
|
|
|
classDescription: "RTCStatsReport",
|
|
|
|
classID: PC_STATS_CID,
|
|
|
|
contractID: PC_STATS_CONTRACT,
|
2013-12-19 19:34:39 +04:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
|
|
|
|
2016-04-07 17:40:37 +03:00
|
|
|
setInternal: function(aKey, aObj) {
|
|
|
|
return this.__DOM_IMPL__.__set(aKey, aObj);
|
|
|
|
},
|
|
|
|
|
|
|
|
// TODO: Remove legacy API eventually
|
2013-12-19 19:34:39 +04:00
|
|
|
//
|
2016-04-07 17:40:37 +03:00
|
|
|
// Since maplike is recent, we still also make the stats available as legacy
|
2013-12-19 19:34:39 +04:00
|
|
|
// enumerable read-only properties directly on our content-facing object.
|
|
|
|
// Must be called after our webidl sandwich is made.
|
|
|
|
|
2016-04-07 17:40:37 +03:00
|
|
|
makeStatsPublic: function(warnNullable) {
|
|
|
|
let legacyProps = {};
|
|
|
|
for (let key in this._report) {
|
|
|
|
let value = Cu.cloneInto(this._report[key], this._win);
|
|
|
|
this.setInternal(key, value);
|
|
|
|
|
|
|
|
legacyProps[key] = {
|
|
|
|
enumerable: true, configurable: false,
|
|
|
|
get: Cu.exportFunction(function() {
|
|
|
|
if (warnNullable.warn) {
|
|
|
|
warnNullable.warn();
|
|
|
|
warnNullable.warn = null;
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}, this.__DOM_IMPL__.wrappedJSObject)
|
|
|
|
};
|
2013-10-18 02:00:05 +04:00
|
|
|
}
|
2016-04-07 17:40:37 +03:00
|
|
|
Object.defineProperties(this.__DOM_IMPL__.wrappedJSObject, legacyProps);
|
2013-12-19 19:34:39 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
get mozPcid() { return this._pcid; }
|
2013-10-18 02:00:05 +04:00
|
|
|
};
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
function RTCPeerConnection() {
|
2014-08-14 05:40:54 +04:00
|
|
|
this._senders = [];
|
|
|
|
this._receivers = [];
|
2012-10-07 09:34:30 +04:00
|
|
|
|
|
|
|
this._pc = null;
|
|
|
|
this._observer = null;
|
2012-10-20 20:43:57 +04:00
|
|
|
this._closed = false;
|
2012-10-07 09:34:30 +04:00
|
|
|
|
|
|
|
this._onCreateOfferSuccess = null;
|
|
|
|
this._onCreateOfferFailure = null;
|
|
|
|
this._onCreateAnswerSuccess = null;
|
|
|
|
this._onCreateAnswerFailure = null;
|
2013-10-18 02:00:05 +04:00
|
|
|
this._onGetStatsSuccess = null;
|
|
|
|
this._onGetStatsFailure = null;
|
2014-08-15 09:33:09 +04:00
|
|
|
this._onReplaceTrackSender = null;
|
2014-08-27 03:31:51 +04:00
|
|
|
this._onReplaceTrackWithTrack = null;
|
2014-08-15 09:33:09 +04:00
|
|
|
this._onReplaceTrackSuccess = null;
|
|
|
|
this._onReplaceTrackFailure = null;
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2013-01-15 02:25:54 +04:00
|
|
|
this._localType = null;
|
|
|
|
this._remoteType = null;
|
2016-02-21 03:48:20 +03:00
|
|
|
// http://rtcweb-wg.github.io/jsep/#rfc.section.4.1.9
|
|
|
|
// canTrickle == null means unknown; when a remote description is received it
|
|
|
|
// is set to true or false based on the presence of the "trickle" ice-option
|
|
|
|
this._canTrickle = null;
|
2013-01-15 02:25:54 +04:00
|
|
|
|
2013-05-17 03:40:23 +04:00
|
|
|
// States
|
|
|
|
this._iceGatheringState = this._iceConnectionState = "new";
|
2016-12-07 12:49:50 +03:00
|
|
|
|
|
|
|
this._hasStunServer = this._hasTurnServer = false;
|
2016-12-08 03:59:32 +03:00
|
|
|
this._iceGatheredRelayCandidates = false;
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
2013-05-08 04:55:21 +04:00
|
|
|
RTCPeerConnection.prototype = {
|
2015-09-28 21:25:04 +03:00
|
|
|
classDescription: "RTCPeerConnection",
|
2012-10-07 09:34:30 +04:00
|
|
|
classID: PC_CID,
|
2013-05-08 04:55:21 +04:00
|
|
|
contractID: PC_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
2013-10-14 20:53:56 +04:00
|
|
|
Ci.nsIDOMGlobalPropertyInitializer]),
|
2013-05-08 04:55:21 +04:00
|
|
|
init: function(win) { this._win = win; },
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
__init: function(rtcConfig) {
|
2015-01-20 18:08:00 +03:00
|
|
|
this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
2015-07-30 17:48:27 +03:00
|
|
|
// TODO: Update this code once we support pc.setConfiguration, to track
|
|
|
|
// setting from content independently from pref (Bug 1181768).
|
|
|
|
if (rtcConfig.iceTransportPolicy == "all" &&
|
|
|
|
Services.prefs.getBoolPref("media.peerconnection.ice.relay_only")) {
|
|
|
|
rtcConfig.iceTransportPolicy = "relay";
|
|
|
|
}
|
2016-03-04 23:05:20 +03:00
|
|
|
this._config = Object.assign({}, rtcConfig);
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
if (!rtcConfig.iceServers ||
|
2013-02-18 17:49:14 +04:00
|
|
|
!Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
|
2015-06-24 02:52:50 +03:00
|
|
|
try {
|
|
|
|
rtcConfig.iceServers =
|
|
|
|
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers") || "[]");
|
|
|
|
} catch (e) {
|
|
|
|
this.logWarning(
|
2016-03-09 06:44:06 +03:00
|
|
|
"Ignoring invalid media.peerconnection.default_iceservers in about:config");
|
2015-06-24 02:52:50 +03:00
|
|
|
rtcConfig.iceServers = [];
|
2015-01-20 18:08:00 +03:00
|
|
|
}
|
2015-06-24 02:52:50 +03:00
|
|
|
try {
|
|
|
|
this._mustValidateRTCConfiguration(rtcConfig,
|
|
|
|
"Ignoring invalid media.peerconnection.default_iceservers in about:config");
|
|
|
|
} catch (e) {
|
2016-03-09 06:44:06 +03:00
|
|
|
this.logWarning(e.message);
|
2015-06-24 02:52:50 +03:00
|
|
|
rtcConfig.iceServers = [];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This gets executed in the typical case when iceServers
|
|
|
|
// are passed in through the web page.
|
|
|
|
this._mustValidateRTCConfiguration(rtcConfig,
|
2013-01-23 23:21:25 +04:00
|
|
|
"RTCPeerConnection constructor passed invalid RTCConfiguration");
|
2015-06-24 02:52:50 +03:00
|
|
|
}
|
2015-10-01 22:49:01 +03:00
|
|
|
var principal = Cu.getWebIDLCallerPrincipal();
|
|
|
|
this._isChrome = Services.scriptSecurityManager.isSystemPrincipal(principal);
|
2015-04-28 18:29:57 +03:00
|
|
|
|
2016-10-17 04:59:02 +03:00
|
|
|
if (_globalPCList._networkdown) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Can't create RTCPeerConnections when the network is down",
|
|
|
|
"InvalidStateError");
|
2012-12-11 16:58:12 +04:00
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-01-12 23:09:01 +03:00
|
|
|
this.makeGetterSetterEH("ontrack");
|
|
|
|
this.makeLegacyGetterSetterEH("onaddstream", "Use peerConnection.ontrack instead.");
|
|
|
|
this.makeLegacyGetterSetterEH("onaddtrack", "Use peerConnection.ontrack instead.");
|
2013-05-17 03:40:23 +04:00
|
|
|
this.makeGetterSetterEH("onicecandidate");
|
|
|
|
this.makeGetterSetterEH("onnegotiationneeded");
|
|
|
|
this.makeGetterSetterEH("onsignalingstatechange");
|
|
|
|
this.makeGetterSetterEH("onremovestream");
|
|
|
|
this.makeGetterSetterEH("ondatachannel");
|
|
|
|
this.makeGetterSetterEH("oniceconnectionstatechange");
|
2016-11-21 23:06:27 +03:00
|
|
|
this.makeGetterSetterEH("onicegatheringstatechange");
|
2014-02-11 02:41:46 +04:00
|
|
|
this.makeGetterSetterEH("onidentityresult");
|
|
|
|
this.makeGetterSetterEH("onpeeridentity");
|
2014-04-08 16:26:00 +04:00
|
|
|
this.makeGetterSetterEH("onidpassertionerror");
|
|
|
|
this.makeGetterSetterEH("onidpvalidationerror");
|
2013-05-17 03:40:23 +04:00
|
|
|
|
2013-10-14 20:53:56 +04:00
|
|
|
this._pc = new this._win.PeerConnectionImpl();
|
2015-01-20 17:41:55 +03:00
|
|
|
this._operationsChain = this._win.Promise.resolve();
|
2013-11-02 04:49:48 +04:00
|
|
|
|
|
|
|
this.__DOM_IMPL__._innerObject = this;
|
|
|
|
this._observer = new this._win.PeerConnectionObserver(this.__DOM_IMPL__);
|
2013-05-22 21:05:20 +04:00
|
|
|
|
2015-12-01 02:15:20 +03:00
|
|
|
var location = "" + this._win.location;
|
|
|
|
|
2016-04-07 17:40:37 +03:00
|
|
|
// Warn just once per PeerConnection about deprecated getStats usage.
|
|
|
|
this._warnDeprecatedStatsAccessNullable = { warn: () =>
|
|
|
|
this.logWarning("non-maplike pc.getStats access is deprecated! " +
|
2016-12-13 03:17:38 +03:00
|
|
|
"See http://w3c.github.io/webrtc-pc/#getstats-example for usage.") };
|
|
|
|
|
|
|
|
this._warnDeprecatedStatsCallbacksNullable = { warn: () =>
|
|
|
|
this.logWarning("Callback-based pc.getStats is deprecated! Use promise-version! " +
|
|
|
|
"See http://w3c.github.io/webrtc-pc/#getstats-example for usage.") };
|
2016-04-07 17:40:37 +03:00
|
|
|
|
2013-05-22 21:05:20 +04:00
|
|
|
// Add a reference to the PeerConnection to global list (before init).
|
|
|
|
_globalPCList.addPC(this);
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2014-02-11 02:41:46 +04:00
|
|
|
this._impl.initialize(this._observer, this._win, rtcConfig,
|
|
|
|
Services.tm.currentThread);
|
2016-11-21 23:27:23 +03:00
|
|
|
|
|
|
|
this._certificateReady = this._initCertificate(rtcConfig.certificates);
|
2014-02-11 02:41:46 +04:00
|
|
|
this._initIdp();
|
2014-05-23 01:14:56 +04:00
|
|
|
_globalPCList.notifyLifecycleObservers(this, "initialized");
|
2013-10-14 20:53:56 +04:00
|
|
|
},
|
|
|
|
|
2014-02-11 02:41:46 +04:00
|
|
|
get _impl() {
|
2013-04-17 09:16:02 +04:00
|
|
|
if (!this._pc) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException(
|
|
|
|
"RTCPeerConnection is gone (did you enter Offline mode?)",
|
|
|
|
"InvalidStateError");
|
2013-04-17 09:16:02 +04:00
|
|
|
}
|
|
|
|
return this._pc;
|
|
|
|
},
|
|
|
|
|
2016-03-04 23:05:20 +03:00
|
|
|
getConfiguration: function() {
|
|
|
|
return this._config;
|
|
|
|
},
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_initCertificate: async function(certificates = []) {
|
|
|
|
let certificate;
|
|
|
|
if (certificates.length > 1) {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"RTCPeerConnection does not currently support multiple certificates",
|
|
|
|
"NotSupportedError");
|
|
|
|
}
|
|
|
|
if (certificates.length) {
|
|
|
|
certificate = certificates.find(c => c.expires > Date.now());
|
|
|
|
if (!certificate) {
|
2015-07-06 20:40:04 +03:00
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Unable to create RTCPeerConnection with an expired certificate",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
2016-11-21 23:27:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!certificate) {
|
|
|
|
certificate = await this._win.RTCPeerConnection.generateCertificate({
|
2015-07-06 20:40:04 +03:00
|
|
|
name: "ECDSA", namedCurve: "P-256"
|
|
|
|
});
|
|
|
|
}
|
2016-11-21 23:27:23 +03:00
|
|
|
this._impl.certificate = certificate;
|
2015-07-06 20:40:04 +03:00
|
|
|
},
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_resetPeerIdentityPromise: function() {
|
2015-02-22 00:57:21 +03:00
|
|
|
this._peerIdentity = new this._win.Promise((resolve, reject) => {
|
|
|
|
this._resolvePeerIdentity = resolve;
|
|
|
|
this._rejectPeerIdentity = reject;
|
|
|
|
});
|
2016-11-21 23:27:23 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_initIdp: function() {
|
|
|
|
this._resetPeerIdentityPromise();
|
2015-02-22 00:57:21 +03:00
|
|
|
this._lastIdentityValidation = this._win.Promise.resolve();
|
|
|
|
|
2014-02-11 02:41:46 +04:00
|
|
|
let prefName = "media.peerconnection.identity.timeout";
|
|
|
|
let idpTimeout = Services.prefs.getIntPref(prefName);
|
2015-02-22 00:57:21 +03:00
|
|
|
this._localIdp = new PeerConnectionIdp(this._win, idpTimeout);
|
|
|
|
this._remoteIdp = new PeerConnectionIdp(this._win, idpTimeout);
|
2014-02-11 02:41:46 +04:00
|
|
|
},
|
|
|
|
|
2015-01-20 17:41:55 +03:00
|
|
|
// Add a function to the internal operations chain.
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_chain: async function(func) {
|
|
|
|
let p = (async () => {
|
|
|
|
await this._operationsChain;
|
2015-01-16 23:00:09 +03:00
|
|
|
// Don't _checkClosed() inside the chain, because it throws, and spec
|
2016-11-21 23:27:23 +03:00
|
|
|
// behavior is to NOT reject outstanding promises on close. This is what
|
|
|
|
// happens most of the time anyways, as the c++ code stops calling us once
|
|
|
|
// closed, hanging the chain. However, c++ may already have queued tasks
|
|
|
|
// on us, so if we're one of those then sit back.
|
|
|
|
if (this._closed) {
|
|
|
|
return;
|
2015-01-16 23:00:09 +03:00
|
|
|
}
|
2016-11-21 23:27:23 +03:00
|
|
|
return await func();
|
|
|
|
})();
|
2015-01-20 17:41:55 +03:00
|
|
|
// don't propagate errors in the operations chain (this is a fork of p).
|
|
|
|
this._operationsChain = p.catch(() => {});
|
2016-11-21 23:27:23 +03:00
|
|
|
return await p;
|
2015-01-20 17:41:55 +03:00
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
// These wrappers help implement legacy callbacks in a manner that produces
|
2015-01-20 17:41:55 +03:00
|
|
|
// correct line-numbers in errors, provided that methods validate their inputs
|
|
|
|
// before putting themselves on the pc's operations chain.
|
2016-06-07 22:50:43 +03:00
|
|
|
//
|
|
|
|
// It also serves as guard against settling promises past close().
|
2015-01-20 17:41:55 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_async: function(func) {
|
|
|
|
return this._win.Promise.resolve(this._closeWrapper(func));
|
|
|
|
},
|
2016-11-17 09:56:26 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_legacy: function(...args) {
|
|
|
|
return this._win.Promise.resolve(this._legacyCloseWrapper(...args));
|
|
|
|
},
|
|
|
|
|
|
|
|
_auto: function(onSucc, onErr, func) {
|
|
|
|
return (typeof onSucc == "function") ? this._legacy(onSucc, onErr, func)
|
|
|
|
: this._async(func);
|
|
|
|
},
|
|
|
|
|
|
|
|
_closeWrapper: async function(func) {
|
|
|
|
let closed = this._closed;
|
2015-01-20 17:41:55 +03:00
|
|
|
try {
|
2016-11-27 18:34:46 +03:00
|
|
|
let result = await func();
|
|
|
|
if (!closed && this._closed) {
|
|
|
|
await new Promise(() => {});
|
|
|
|
}
|
|
|
|
return result;
|
2015-01-20 17:41:55 +03:00
|
|
|
} catch (e) {
|
2016-11-27 18:34:46 +03:00
|
|
|
if (!closed && this._closed) {
|
|
|
|
await new Promise(() => {});
|
|
|
|
}
|
|
|
|
throw e;
|
2015-01-20 17:41:55 +03:00
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_legacyCloseWrapper: async function(onSucc, onErr, func) {
|
|
|
|
|
|
|
|
let wrapCallback = cb => result => {
|
2014-12-04 22:12:19 +03:00
|
|
|
try {
|
2016-11-27 18:34:46 +03:00
|
|
|
cb && cb(result);
|
2014-12-04 22:12:19 +03:00
|
|
|
} catch (e) {
|
|
|
|
this.logErrorAndCallOnError(e);
|
|
|
|
}
|
|
|
|
};
|
2016-11-27 18:34:46 +03:00
|
|
|
|
|
|
|
try {
|
|
|
|
wrapCallback(onSucc)(await func());
|
|
|
|
} catch (e) {
|
|
|
|
wrapCallback(onErr)(e);
|
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-01-23 23:21:25 +04:00
|
|
|
/**
|
2015-01-20 18:08:00 +03:00
|
|
|
* An RTCConfiguration may look like this:
|
2013-01-23 23:21:25 +04:00
|
|
|
*
|
2015-01-20 18:08:00 +03:00
|
|
|
* { "iceServers": [ { urls: "stun:stun.example.org", },
|
|
|
|
* { url: "stun:stun.example.org", }, // deprecated version
|
|
|
|
* { urls: ["turn:turn1.x.org", "turn:turn2.x.org"],
|
2013-05-08 04:55:21 +04:00
|
|
|
* username:"jib", credential:"mypass"} ] }
|
2013-01-23 23:21:25 +04:00
|
|
|
*
|
2015-06-24 02:52:50 +03:00
|
|
|
* This function normalizes the structure of the input for rtcConfig.iceServers for us,
|
|
|
|
* so we test well-formed stun/turn urls before passing along to C++.
|
2015-01-16 23:00:09 +03:00
|
|
|
* msg - Error message to detail which array-entry failed, if any.
|
2013-01-23 23:21:25 +04:00
|
|
|
*/
|
2016-11-20 00:47:10 +03:00
|
|
|
_mustValidateRTCConfiguration: function({ iceServers }, msg) {
|
2015-06-24 02:52:50 +03:00
|
|
|
|
|
|
|
// Normalize iceServers input
|
2016-11-20 00:47:10 +03:00
|
|
|
iceServers.forEach(server => {
|
2015-06-24 02:52:50 +03:00
|
|
|
if (typeof server.urls === "string") {
|
|
|
|
server.urls = [server.urls];
|
|
|
|
} else if (!server.urls && server.url) {
|
|
|
|
// TODO: Remove support for legacy iceServer.url eventually (Bug 1116766)
|
|
|
|
server.urls = [server.url];
|
2016-03-09 06:44:06 +03:00
|
|
|
this.logWarning("RTCIceServer.url is deprecated! Use urls instead.");
|
2015-06-24 02:52:50 +03:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-01-16 23:00:09 +03:00
|
|
|
let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
|
|
|
|
|
|
|
|
let nicerNewURI = uriStr => {
|
2013-01-23 23:21:25 +04:00
|
|
|
try {
|
|
|
|
return ios.newURI(uriStr, null, null);
|
|
|
|
} catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException(msg + " - malformed URI: " + uriStr,
|
|
|
|
"SyntaxError");
|
2013-01-23 23:21:25 +04:00
|
|
|
}
|
2015-01-16 23:00:09 +03:00
|
|
|
};
|
|
|
|
|
2016-12-09 11:38:17 +03:00
|
|
|
var stunServers = 0;
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
iceServers.forEach(({ urls, username, credential, credentialType }) => {
|
|
|
|
if (!urls) {
|
2015-01-20 18:08:00 +03:00
|
|
|
throw new this._win.DOMException(msg + " - missing urls", "InvalidAccessError");
|
2013-10-14 20:53:56 +04:00
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
urls.map(url => nicerNewURI(url)).forEach(({ scheme, spec }) => {
|
|
|
|
if (scheme in { turn:1, turns:1 }) {
|
|
|
|
if (username == undefined) {
|
|
|
|
throw new this._win.DOMException(msg + " - missing username: " + spec,
|
2015-01-20 18:08:00 +03:00
|
|
|
"InvalidAccessError");
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
if (credential == undefined) {
|
|
|
|
throw new this._win.DOMException(msg + " - missing credential: " + spec,
|
2015-01-20 18:08:00 +03:00
|
|
|
"InvalidAccessError");
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
if (credentialType != "password") {
|
2016-02-25 08:24:16 +03:00
|
|
|
this.logWarning("RTCConfiguration TURN credentialType \""+
|
2016-11-20 00:47:10 +03:00
|
|
|
credentialType +
|
2016-02-25 08:24:16 +03:00
|
|
|
"\" is not yet implemented. Treating as password."+
|
2016-03-09 06:44:06 +03:00
|
|
|
" https://bugzil.la/1247616");
|
2016-02-25 08:24:16 +03:00
|
|
|
}
|
2016-12-07 12:49:50 +03:00
|
|
|
this._hasTurnServer = true;
|
2016-12-09 11:38:17 +03:00
|
|
|
stunServers += 1;
|
2016-11-20 00:47:10 +03:00
|
|
|
} else if (scheme in { stun:1, stuns:1 }) {
|
2016-12-07 12:49:50 +03:00
|
|
|
this._hasStunServer = true;
|
2016-12-09 11:38:17 +03:00
|
|
|
stunServers += 1;
|
2016-11-20 00:47:10 +03:00
|
|
|
} else {
|
|
|
|
throw new this._win.DOMException(msg + " - improper scheme: " + scheme,
|
2015-01-20 18:08:00 +03:00
|
|
|
"SyntaxError");
|
2013-08-10 12:19:35 +04:00
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
if (scheme in { stuns:1, turns:1 }) {
|
|
|
|
this.logWarning(scheme.toUpperCase() + " is not yet supported.");
|
2015-01-20 18:08:00 +03:00
|
|
|
}
|
2016-12-09 11:38:17 +03:00
|
|
|
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");
|
|
|
|
}
|
2015-01-20 18:08:00 +03:00
|
|
|
});
|
2015-01-16 23:00:09 +03:00
|
|
|
});
|
2013-01-23 23:21:25 +04:00
|
|
|
},
|
|
|
|
|
2013-01-18 00:25:17 +04:00
|
|
|
// Ideally, this should be of the form _checkState(state),
|
|
|
|
// where the state is taken from an enumeration containing
|
|
|
|
// the valid peer connection states defined in the WebRTC
|
|
|
|
// spec. See Bug 831756.
|
|
|
|
_checkClosed: function() {
|
|
|
|
if (this._closed) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException("Peer connection is closed",
|
|
|
|
"InvalidStateError");
|
2013-01-18 00:25:17 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
dispatchEvent: function(event) {
|
2014-09-27 03:02:46 +04:00
|
|
|
// PC can close while events are firing if there is an async dispatch
|
2016-03-29 23:27:03 +03:00
|
|
|
// in c++ land. But let through "closed" signaling and ice connection events.
|
|
|
|
if (!this._closed || this._inClose) {
|
2014-09-27 03:02:46 +04:00
|
|
|
this.__DOM_IMPL__.dispatchEvent(event);
|
|
|
|
}
|
2013-05-08 04:55:21 +04:00
|
|
|
},
|
|
|
|
|
2013-05-12 04:48:29 +04:00
|
|
|
// Log error message to web console and window.onerror, if present.
|
2014-12-08 18:37:14 +03:00
|
|
|
logErrorAndCallOnError: function(e) {
|
|
|
|
this.logMsg(e.message, e.fileName, e.lineNumber, Ci.nsIScriptError.exceptionFlag);
|
2014-04-10 21:33:08 +04:00
|
|
|
|
|
|
|
// Safely call onerror directly if present (necessary for testing)
|
|
|
|
try {
|
|
|
|
if (typeof this._win.onerror === "function") {
|
2014-12-08 18:37:14 +03:00
|
|
|
this._win.onerror(e.message, e.fileName, e.lineNumber);
|
2014-04-10 21:33:08 +04:00
|
|
|
}
|
|
|
|
} catch(e) {
|
|
|
|
// If onerror itself throws, service it.
|
|
|
|
try {
|
2016-03-09 06:44:06 +03:00
|
|
|
this.logMsg(e.message, e.fileName, e.lineNumber, Ci.nsIScriptError.errorFlag);
|
2014-04-10 21:33:08 +04:00
|
|
|
} catch(e) {}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-09 06:44:06 +03:00
|
|
|
logError: function(msg) {
|
|
|
|
this.logStackMsg(msg, Ci.nsIScriptError.errorFlag);
|
|
|
|
},
|
|
|
|
|
|
|
|
logWarning: function(msg) {
|
|
|
|
this.logStackMsg(msg, Ci.nsIScriptError.warningFlag);
|
2013-05-12 04:48:29 +04:00
|
|
|
},
|
|
|
|
|
2016-03-09 06:44:06 +03:00
|
|
|
logStackMsg: function(msg, flag) {
|
|
|
|
let err = this._win.Error();
|
|
|
|
this.logMsg(msg, err.fileName, err.lineNumber, flag);
|
2013-05-12 04:48:29 +04:00
|
|
|
},
|
|
|
|
|
2014-04-10 21:33:08 +04:00
|
|
|
logMsg: function(msg, file, line, flag) {
|
2016-11-09 01:50:24 +03:00
|
|
|
return logMsg(msg, file, line, flag, this._winID);
|
2013-05-12 04:48:29 +04:00
|
|
|
},
|
|
|
|
|
2013-05-17 03:40:23 +04:00
|
|
|
getEH: function(type) {
|
2013-05-08 04:55:21 +04:00
|
|
|
return this.__DOM_IMPL__.getEventHandler(type);
|
|
|
|
},
|
|
|
|
|
2013-05-17 03:40:23 +04:00
|
|
|
setEH: function(type, handler) {
|
2013-05-08 04:55:21 +04:00
|
|
|
this.__DOM_IMPL__.setEventHandler(type, handler);
|
|
|
|
},
|
|
|
|
|
2013-05-17 03:40:23 +04:00
|
|
|
makeGetterSetterEH: function(name) {
|
|
|
|
Object.defineProperty(this, name,
|
|
|
|
{
|
|
|
|
get:function() { return this.getEH(name); },
|
|
|
|
set:function(h) { return this.setEH(name, h); }
|
|
|
|
});
|
|
|
|
},
|
2013-05-08 04:55:21 +04:00
|
|
|
|
2016-01-12 23:09:01 +03:00
|
|
|
makeLegacyGetterSetterEH: function(name, msg) {
|
|
|
|
Object.defineProperty(this, name,
|
|
|
|
{
|
|
|
|
get:function() { return this.getEH(name); },
|
|
|
|
set:function(h) {
|
2016-03-09 06:44:06 +03:00
|
|
|
this.logWarning(name + " is deprecated! " + msg);
|
2016-01-12 23:09:01 +03:00
|
|
|
return this.setEH(name, h);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
createOffer: function(optionsOrOnSucc, onErr, options) {
|
2015-07-16 22:36:34 +03:00
|
|
|
// This entry-point handles both new and legacy call sig. Decipher which one
|
2016-11-27 18:34:46 +03:00
|
|
|
if (typeof optionsOrOnSucc == "function") {
|
|
|
|
return this._legacy(optionsOrOnSucc, onErr, () => this._createOffer(options));
|
2014-09-09 09:12:24 +04:00
|
|
|
}
|
2016-11-27 18:34:46 +03:00
|
|
|
return this._async(() => this._createOffer(optionsOrOnSucc));
|
|
|
|
},
|
|
|
|
|
|
|
|
_createOffer: async function(options) {
|
|
|
|
this._checkClosed();
|
|
|
|
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
|
|
|
return await this._chain(async () => {
|
|
|
|
let haveAssertion;
|
|
|
|
if (this._localIdp.enabled) {
|
|
|
|
haveAssertion = this._getIdentityAssertion(origin);
|
|
|
|
}
|
|
|
|
await this._getPermission();
|
|
|
|
await this._certificateReady;
|
|
|
|
let sdp = await new Promise((resolve, reject) => {
|
|
|
|
this._onCreateOfferSuccess = resolve;
|
|
|
|
this._onCreateOfferFailure = reject;
|
|
|
|
this._impl.createOffer(options);
|
2015-02-22 00:57:21 +03:00
|
|
|
});
|
2016-11-27 18:34:46 +03:00
|
|
|
if (haveAssertion) {
|
|
|
|
await haveAssertion;
|
|
|
|
sdp = this._localIdp.addIdentityAttribute(sdp);
|
|
|
|
}
|
|
|
|
return Cu.cloneInto({ type: "offer", sdp }, this._win);
|
2014-12-04 22:12:19 +03:00
|
|
|
});
|
2013-01-25 07:44:30 +04:00
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
createAnswer: function(optionsOrOnSucc, onErr) {
|
2015-07-16 22:36:34 +03:00
|
|
|
// This entry-point handles both new and legacy call sig. Decipher which one
|
2016-11-27 18:34:46 +03:00
|
|
|
if (typeof optionsOrOnSucc == "function") {
|
|
|
|
return this._legacy(optionsOrOnSucc, onErr, () => this._createAnswer({}));
|
2015-07-16 22:36:34 +03:00
|
|
|
}
|
2016-11-27 18:34:46 +03:00
|
|
|
return this._async(() => this._createAnswer(optionsOrOnSucc));
|
|
|
|
},
|
|
|
|
|
|
|
|
_createAnswer: async function(options) {
|
|
|
|
this._checkClosed();
|
|
|
|
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
|
|
|
return await this._chain(async () => {
|
|
|
|
// We give up line-numbers in errors by doing this here, but do all
|
|
|
|
// state-checks inside the chain, to support the legacy feature that
|
|
|
|
// callers don't have to wait for setRemoteDescription to finish.
|
|
|
|
if (!this.remoteDescription) {
|
|
|
|
throw new this._win.DOMException("setRemoteDescription not called",
|
|
|
|
"InvalidStateError");
|
|
|
|
}
|
|
|
|
if (this.remoteDescription.type != "offer") {
|
|
|
|
throw new this._win.DOMException("No outstanding offer",
|
|
|
|
"InvalidStateError");
|
|
|
|
}
|
|
|
|
let haveAssertion;
|
|
|
|
if (this._localIdp.enabled) {
|
|
|
|
haveAssertion = this._getIdentityAssertion(origin);
|
|
|
|
}
|
|
|
|
await this._getPermission();
|
|
|
|
await this._certificateReady;
|
|
|
|
let sdp = await new Promise((resolve, reject) => {
|
|
|
|
this._onCreateAnswerSuccess = resolve;
|
|
|
|
this._onCreateAnswerFailure = reject;
|
|
|
|
this._impl.createAnswer();
|
2015-02-22 00:57:21 +03:00
|
|
|
});
|
2016-11-27 18:34:46 +03:00
|
|
|
if (haveAssertion) {
|
|
|
|
await haveAssertion;
|
|
|
|
sdp = this._localIdp.addIdentityAttribute(sdp);
|
|
|
|
}
|
|
|
|
return Cu.cloneInto({ type: "answer", sdp }, this._win);
|
2015-01-20 17:41:55 +03:00
|
|
|
});
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_getPermission: async function() {
|
|
|
|
if (!this._havePermission) {
|
|
|
|
let privileged = this._isChrome || AppConstants.MOZ_B2G ||
|
|
|
|
Services.prefs.getBoolPref("media.navigator.permission.disabled");
|
|
|
|
|
|
|
|
if (privileged) {
|
|
|
|
this._havePermission = Promise.resolve();
|
|
|
|
} else {
|
|
|
|
this._havePermission = new Promise((resolve, reject) => {
|
|
|
|
this._settlePermission = { allow: resolve, deny: reject };
|
|
|
|
let outerId = this._win.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
|
|
getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
|
|
|
|
|
|
|
|
let chrome = new CreateOfferRequest(outerId, this._winID,
|
|
|
|
this._globalPCListId, false);
|
|
|
|
let request = this._win.CreateOfferRequest._create(this._win, chrome);
|
|
|
|
Services.obs.notifyObservers(request, "PeerConnection:request", null);
|
|
|
|
});
|
|
|
|
}
|
2015-08-07 22:22:30 +03:00
|
|
|
}
|
2016-11-21 23:27:23 +03:00
|
|
|
return await this._havePermission;
|
2015-08-07 22:22:30 +03:00
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
_actions: {
|
|
|
|
offer: Ci.IPeerConnection.kActionOffer,
|
|
|
|
answer: Ci.IPeerConnection.kActionAnswer,
|
|
|
|
pranswer: Ci.IPeerConnection.kActionPRAnswer,
|
|
|
|
rollback: Ci.IPeerConnection.kActionRollback,
|
|
|
|
answer: Ci.IPeerConnection.kActionAnswer,
|
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
setLocalDescription: function(desc, onSucc, onErr) {
|
|
|
|
return this._auto(onSucc, onErr, () => this._setLocalDescription(desc));
|
|
|
|
},
|
2016-11-20 00:47:10 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_setLocalDescription: async function({ type, sdp }) {
|
|
|
|
this._checkClosed();
|
2015-08-08 02:18:58 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
this._localType = type;
|
2015-08-08 02:18:58 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
let action = this._actions[type];
|
|
|
|
if (action === undefined) {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Invalid type " + type + " provided to setLocalDescription",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
|
|
|
if (action == Ci.IPeerConnection.kActionPRAnswer) {
|
|
|
|
throw new this._win.DOMException("pranswer not yet implemented",
|
|
|
|
"NotSupportedError");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sdp && action != Ci.IPeerConnection.kActionRollback) {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Empty or null SDP provided to setLocalDescription",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
|
|
|
|
|
|
|
return await this._chain(async () => {
|
|
|
|
await this._getPermission();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
this._onSetLocalDescriptionSuccess = resolve;
|
|
|
|
this._onSetLocalDescriptionFailure = reject;
|
|
|
|
this._impl.setLocalDescription(action, sdp);
|
2016-11-21 23:27:23 +03:00
|
|
|
});
|
2014-12-04 22:12:19 +03:00
|
|
|
});
|
2013-10-14 20:53:56 +04:00
|
|
|
},
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_validateIdentity: async function(sdp, origin) {
|
2015-02-22 00:57:21 +03:00
|
|
|
let expectedIdentity;
|
|
|
|
|
|
|
|
// Only run a single identity verification at a time. We have to do this to
|
|
|
|
// avoid problems with the fact that identity validation doesn't block the
|
|
|
|
// resolution of setRemoteDescription().
|
2016-11-21 23:27:23 +03:00
|
|
|
let p = (async () => {
|
|
|
|
try {
|
|
|
|
await this._lastIdentityValidation;
|
|
|
|
let msg = await this._remoteIdp.verifyIdentityFromSDP(sdp, origin);
|
2015-02-22 00:57:21 +03:00
|
|
|
expectedIdentity = this._impl.peerIdentity;
|
|
|
|
// If this pc has an identity already, then the identity in sdp must match
|
|
|
|
if (expectedIdentity && (!msg || msg.identity !== expectedIdentity)) {
|
|
|
|
this.close();
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Peer Identity mismatch, expected: " + expectedIdentity,
|
|
|
|
"IncompatibleSessionDescriptionError");
|
|
|
|
}
|
|
|
|
if (msg) {
|
|
|
|
// Set new identity and generate an event.
|
|
|
|
this._impl.peerIdentity = msg.identity;
|
2015-04-16 19:52:36 +03:00
|
|
|
this._resolvePeerIdentity(Cu.cloneInto({
|
|
|
|
idp: this._remoteIdp.provider,
|
|
|
|
name: msg.identity
|
|
|
|
}, this._win));
|
2015-02-22 00:57:21 +03:00
|
|
|
}
|
2016-11-21 23:27:23 +03:00
|
|
|
} catch(e) {
|
2015-02-22 00:57:21 +03:00
|
|
|
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) {
|
2016-11-21 23:27:23 +03:00
|
|
|
this._resetPeerIdentityPromise();
|
2015-02-22 00:57:21 +03:00
|
|
|
}
|
|
|
|
throw e;
|
2016-11-21 23:27:23 +03:00
|
|
|
}
|
|
|
|
})();
|
|
|
|
this._lastIdentityValidation = p.catch(() => {});
|
2015-02-22 00:57:21 +03:00
|
|
|
|
|
|
|
// Only wait for IdP validation if we need identity matching
|
2016-11-21 23:27:23 +03:00
|
|
|
if (expectedIdentity) {
|
|
|
|
await p;
|
|
|
|
}
|
2015-02-22 00:57:21 +03:00
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
setRemoteDescription: function(desc, onSucc, onErr) {
|
|
|
|
return this._auto(onSucc, onErr, () => this._setRemoteDescription(desc));
|
|
|
|
},
|
2016-11-20 00:47:10 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_setRemoteDescription: async function({ type, sdp }) {
|
|
|
|
this._checkClosed();
|
|
|
|
this._remoteType = type;
|
2015-08-08 02:18:58 +03:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
let action = this._actions[type];
|
|
|
|
if (action === undefined) {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Invalid type " + type + " provided to setRemoteDescription",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
|
|
|
if (action == Ci.IPeerConnection.kActionPRAnswer) {
|
|
|
|
throw new this._win.DOMException("pranswer not yet implemented",
|
|
|
|
"NotSupportedError");
|
|
|
|
}
|
2012-10-07 09:34:30 +04:00
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
if (!sdp && type != "rollback") {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Empty or null SDP provided to setRemoteDescription",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get caller's origin before hitting the promise chain
|
|
|
|
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
|
|
|
|
|
|
|
return await this._chain(async () => {
|
|
|
|
let haveSetRemote = (async () => {
|
|
|
|
await this._getPermission();
|
|
|
|
await new Promise((resolve, reject) => {
|
|
|
|
this._onSetRemoteDescriptionSuccess = resolve;
|
|
|
|
this._onSetRemoteDescriptionFailure = reject;
|
|
|
|
this._impl.setRemoteDescription(action, sdp);
|
|
|
|
});
|
|
|
|
this._updateCanTrickle();
|
|
|
|
})();
|
|
|
|
|
|
|
|
if (action != Ci.IPeerConnection.kActionRollback) {
|
|
|
|
// Do setRemoteDescription and identity validation in parallel
|
|
|
|
await this._validateIdentity(sdp, origin);
|
|
|
|
}
|
|
|
|
await haveSetRemote;
|
2015-01-20 09:09:45 +03:00
|
|
|
});
|
2014-02-11 02:41:46 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
setIdentityProvider: function(provider, protocol, username) {
|
|
|
|
this._checkClosed();
|
|
|
|
this._localIdp.setIdentityProvider(provider, protocol, username);
|
|
|
|
},
|
|
|
|
|
2016-11-21 23:27:23 +03:00
|
|
|
_getIdentityAssertion: async function(origin) {
|
|
|
|
await this._certificateReady;
|
|
|
|
return await this._localIdp.getIdentityAssertion(this._impl.fingerprint, origin);
|
|
|
|
},
|
|
|
|
|
2014-04-08 16:26:00 +04:00
|
|
|
getIdentityAssertion: function() {
|
2016-11-17 09:56:26 +03:00
|
|
|
this._checkClosed();
|
2015-02-22 00:57:21 +03:00
|
|
|
let origin = Cu.getWebIDLCallerPrincipal().origin;
|
2016-11-21 23:27:23 +03:00
|
|
|
return this._win.Promise.resolve(this._chain(() =>
|
|
|
|
this._getIdentityAssertion(origin)));
|
2013-10-14 20:53:56 +04:00
|
|
|
},
|
|
|
|
|
2016-02-21 03:48:20 +03:00
|
|
|
get canTrickleIceCandidates() {
|
|
|
|
return this._canTrickle;
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2016-02-21 03:48:20 +03:00
|
|
|
_updateCanTrickle: function() {
|
|
|
|
let containsTrickle = section => {
|
|
|
|
let lines = section.toLowerCase().split(/(?:\r\n?|\n)/);
|
|
|
|
return lines.some(line => {
|
|
|
|
let prefix = "a=ice-options:";
|
|
|
|
if (line.substring(0, prefix.length) !== prefix) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let tokens = line.substring(prefix.length).split(" ");
|
|
|
|
return tokens.some(x => x === "trickle");
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
let desc = null;
|
|
|
|
try {
|
|
|
|
// The getter for remoteDescription can throw if the pc is closed.
|
|
|
|
desc = this.remoteDescription;
|
|
|
|
} catch (e) {}
|
|
|
|
if (!desc) {
|
|
|
|
this._canTrickle = null;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let sections = desc.sdp.split(/(?:\r\n?|\n)m=/);
|
|
|
|
let topSection = sections.shift();
|
|
|
|
this._canTrickle =
|
|
|
|
containsTrickle(topSection) || sections.every(containsTrickle);
|
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
// TODO: Implement processing for end-of-candidates (bug 1318167)
|
2016-11-27 18:34:46 +03:00
|
|
|
addIceCandidate: function(cand, onSucc, onErr) {
|
|
|
|
return this._auto(onSucc, onErr, () => cand && this._addIceCandidate(cand));
|
|
|
|
},
|
|
|
|
|
|
|
|
_addIceCandidate: async function({ candidate, sdpMid, sdpMLineIndex }) {
|
|
|
|
this._checkClosed();
|
|
|
|
if (sdpMid === null && sdpMLineIndex === null) {
|
|
|
|
throw new this._win.DOMException(
|
|
|
|
"Invalid candidate (both sdpMid and sdpMLineIndex are null).",
|
|
|
|
"TypeError");
|
|
|
|
}
|
|
|
|
return await this._chain(() => new Promise((resolve, reject) => {
|
|
|
|
this._onAddIceCandidateSuccess = resolve;
|
|
|
|
this._onAddIceCandidateError = reject;
|
|
|
|
this._impl.addIceCandidate(candidate, sdpMid || "", sdpMLineIndex);
|
|
|
|
}));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2014-07-10 22:31:25 +04:00
|
|
|
addStream: function(stream) {
|
2014-08-15 22:53:14 +04:00
|
|
|
stream.getTracks().forEach(track => this.addTrack(track, stream));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2014-08-14 05:40:54 +04:00
|
|
|
addTrack: function(track, stream) {
|
|
|
|
if (stream.currentTime === undefined) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException("invalid stream.", "InvalidParameterError");
|
2014-08-14 05:40:54 +04:00
|
|
|
}
|
|
|
|
this._checkClosed();
|
2015-03-11 19:24:38 +03:00
|
|
|
this._senders.forEach(sender => {
|
|
|
|
if (sender.track == track) {
|
|
|
|
throw new this._win.DOMException("already added.",
|
|
|
|
"InvalidParameterError");
|
|
|
|
}
|
|
|
|
});
|
2016-05-19 16:32:56 +03:00
|
|
|
this._impl.addTrack(track, stream);
|
2014-08-14 05:40:54 +04:00
|
|
|
let sender = this._win.RTCRtpSender._create(this._win,
|
2014-08-15 09:33:09 +04:00
|
|
|
new RTCRtpSender(this, track,
|
|
|
|
stream));
|
2015-03-11 19:24:38 +03:00
|
|
|
this._senders.push(sender);
|
2014-08-14 05:40:54 +04:00
|
|
|
return sender;
|
|
|
|
},
|
|
|
|
|
|
|
|
removeTrack: function(sender) {
|
2014-12-11 02:53:54 +03:00
|
|
|
this._checkClosed();
|
2015-03-11 19:24:38 +03:00
|
|
|
var i = this._senders.indexOf(sender);
|
|
|
|
if (i >= 0) {
|
|
|
|
this._senders.splice(i, 1);
|
|
|
|
this._impl.removeTrack(sender.track); // fires negotiation needed
|
|
|
|
}
|
2014-08-14 05:40:54 +04:00
|
|
|
},
|
|
|
|
|
2016-09-29 20:26:04 +03:00
|
|
|
_insertDTMF: function(sender, tones, duration, interToneGap) {
|
|
|
|
return this._impl.insertDTMF(sender.__DOM_IMPL__, tones, duration, interToneGap);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getDTMFToneBuffer: function(sender) {
|
|
|
|
return this._impl.getDTMFToneBuffer(sender.__DOM_IMPL__);
|
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
_replaceTrack: async function(sender, withTrack) {
|
|
|
|
this._checkClosed();
|
|
|
|
return await this._chain(() => new Promise((resolve, reject) => {
|
2014-12-04 22:12:19 +03:00
|
|
|
this._onReplaceTrackSender = sender;
|
|
|
|
this._onReplaceTrackWithTrack = withTrack;
|
|
|
|
this._onReplaceTrackSuccess = resolve;
|
|
|
|
this._onReplaceTrackFailure = reject;
|
2015-03-11 19:08:21 +03:00
|
|
|
this._impl.replaceTrack(sender.track, withTrack);
|
2016-11-27 18:34:46 +03:00
|
|
|
}));
|
2014-08-15 09:33:09 +04:00
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
_setParameters: function({ track }, parameters) {
|
2015-12-31 19:32:26 +03:00
|
|
|
if (!Services.prefs.getBoolPref("media.peerconnection.simulcast")) {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-31 19:32:25 +03:00
|
|
|
// validate parameters input
|
|
|
|
var encodings = parameters.encodings || [];
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
encodings.reduce((uniqueRids, { rid, scaleResolutionDownBy }) => {
|
|
|
|
if (scaleResolutionDownBy < 1.0) {
|
2016-02-13 03:56:56 +03:00
|
|
|
throw new this._win.RangeError("scaleResolutionDownBy must be >= 1.0");
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
if (!rid && encodings.length > 1) {
|
2015-12-31 19:32:25 +03:00
|
|
|
throw new this._win.DOMException("Missing rid", "TypeError");
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
if (uniqueRids[rid]) {
|
2015-12-31 19:32:25 +03:00
|
|
|
throw new this._win.DOMException("Duplicate rid", "TypeError");
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
uniqueRids[rid] = true;
|
2015-12-31 19:32:25 +03:00
|
|
|
return uniqueRids;
|
|
|
|
}, {});
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
this._impl.setParameters(track, parameters);
|
2015-12-31 19:32:24 +03:00
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
_getParameters: function({ track }) {
|
2015-12-31 19:32:26 +03:00
|
|
|
if (!Services.prefs.getBoolPref("media.peerconnection.simulcast")) {
|
|
|
|
return;
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
return this._impl.getParameters(track);
|
2015-12-31 19:32:24 +03:00
|
|
|
},
|
|
|
|
|
2012-10-07 09:34:30 +04:00
|
|
|
close: function() {
|
2014-04-04 16:34:25 +04:00
|
|
|
if (this._closed) {
|
|
|
|
return;
|
|
|
|
}
|
2016-03-29 23:27:03 +03:00
|
|
|
this._closed = true;
|
|
|
|
this._inClose = true;
|
2014-05-15 17:38:00 +04:00
|
|
|
this.changeIceConnectionState("closed");
|
2014-02-11 02:41:46 +04:00
|
|
|
this._localIdp.close();
|
|
|
|
this._remoteIdp.close();
|
|
|
|
this._impl.close();
|
2016-03-29 23:27:03 +03:00
|
|
|
this._inClose = false;
|
2013-10-14 20:53:56 +04:00
|
|
|
},
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
getLocalStreams: function() {
|
2013-01-18 00:25:17 +04:00
|
|
|
this._checkClosed();
|
2014-02-11 02:41:46 +04:00
|
|
|
return this._impl.getLocalStreams();
|
2012-09-13 20:04:31 +04:00
|
|
|
},
|
2013-01-15 02:25:54 +04:00
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
getRemoteStreams: function() {
|
2013-01-18 00:25:17 +04:00
|
|
|
this._checkClosed();
|
2014-02-11 02:41:46 +04:00
|
|
|
return this._impl.getRemoteStreams();
|
2012-09-13 20:04:31 +04:00
|
|
|
},
|
|
|
|
|
2014-08-14 05:40:54 +04:00
|
|
|
getSenders: function() {
|
2015-03-11 19:24:38 +03:00
|
|
|
return this._senders;
|
2014-08-14 05:40:54 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
getReceivers: function() {
|
2015-03-11 19:24:38 +03:00
|
|
|
return this._receivers;
|
2014-08-14 05:40:54 +04:00
|
|
|
},
|
|
|
|
|
2015-12-09 23:37:38 +03:00
|
|
|
mozSelectSsrc: function(receiver, ssrcIndex) {
|
|
|
|
this._impl.selectSsrc(receiver.track, ssrcIndex);
|
|
|
|
},
|
|
|
|
|
2013-01-15 02:25:54 +04:00
|
|
|
get localDescription() {
|
2013-01-18 00:25:17 +04:00
|
|
|
this._checkClosed();
|
2014-02-11 02:41:46 +04:00
|
|
|
let sdp = this._impl.localDescription;
|
2013-01-15 02:25:54 +04:00
|
|
|
if (sdp.length == 0) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-11-09 18:32:11 +03:00
|
|
|
return new this._win.RTCSessionDescription({ type: this._localType, sdp });
|
2013-01-15 02:25:54 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
get remoteDescription() {
|
2013-01-18 00:25:17 +04:00
|
|
|
this._checkClosed();
|
2014-02-11 02:41:46 +04:00
|
|
|
let sdp = this._impl.remoteDescription;
|
2013-01-15 02:25:54 +04:00
|
|
|
if (sdp.length == 0) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-11-09 18:32:11 +03:00
|
|
|
return new this._win.RTCSessionDescription({ type: this._remoteType, sdp });
|
2013-01-15 02:25:54 +04:00
|
|
|
},
|
|
|
|
|
2014-02-11 02:41:46 +04:00
|
|
|
get peerIdentity() { return this._peerIdentity; },
|
2015-02-22 00:57:21 +03:00
|
|
|
get idpLoginUrl() { return this._localIdp.idpLoginUrl; },
|
2014-05-23 01:14:56 +04:00
|
|
|
get id() { return this._impl.id; },
|
2014-11-20 22:43:02 +03:00
|
|
|
set id(s) { this._impl.id = s; },
|
2013-05-17 03:40:23 +04:00
|
|
|
get iceGatheringState() { return this._iceGatheringState; },
|
|
|
|
get iceConnectionState() { return this._iceConnectionState; },
|
|
|
|
|
2013-05-17 03:41:46 +04:00
|
|
|
get signalingState() {
|
|
|
|
// checking for our local pc closed indication
|
|
|
|
// before invoking the pc methods.
|
2014-02-11 02:41:46 +04:00
|
|
|
if (this._closed) {
|
2013-05-17 03:41:46 +04:00
|
|
|
return "closed";
|
|
|
|
}
|
2013-10-14 20:53:56 +04:00
|
|
|
return {
|
|
|
|
"SignalingInvalid": "",
|
|
|
|
"SignalingStable": "stable",
|
|
|
|
"SignalingHaveLocalOffer": "have-local-offer",
|
|
|
|
"SignalingHaveRemoteOffer": "have-remote-offer",
|
|
|
|
"SignalingHaveLocalPranswer": "have-local-pranswer",
|
|
|
|
"SignalingHaveRemotePranswer": "have-remote-pranswer",
|
|
|
|
"SignalingClosed": "closed"
|
2014-02-11 02:41:46 +04:00
|
|
|
}[this._impl.signalingState];
|
2013-05-17 03:41:46 +04:00
|
|
|
},
|
|
|
|
|
2013-05-17 03:40:23 +04:00
|
|
|
changeIceGatheringState: function(state) {
|
|
|
|
this._iceGatheringState = state;
|
2014-05-23 01:14:56 +04:00
|
|
|
_globalPCList.notifyLifecycleObservers(this, "icegatheringstatechange");
|
2016-11-21 23:06:27 +03:00
|
|
|
this.dispatchEvent(new this._win.Event("icegatheringstatechange"));
|
2013-05-17 03:40:23 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
changeIceConnectionState: function(state) {
|
2016-11-18 10:16:33 +03:00
|
|
|
if (state != this._iceConnectionState) {
|
|
|
|
this._iceConnectionState = state;
|
|
|
|
_globalPCList.notifyLifecycleObservers(this, "iceconnectionstatechange");
|
|
|
|
this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
|
|
|
|
}
|
2013-05-17 03:40:23 +04:00
|
|
|
},
|
|
|
|
|
2016-11-27 18:34:46 +03:00
|
|
|
getStats: function(selector, onSucc, onErr) {
|
2016-12-13 03:17:38 +03:00
|
|
|
if (typeof onSucc == "function" &&
|
|
|
|
this._warnDeprecatedStatsCallbacksNullable.warn) {
|
|
|
|
this._warnDeprecatedStatsCallbacksNullable.warn();
|
|
|
|
this._warnDeprecatedStatsCallbacksNullable.warn = null;
|
|
|
|
}
|
2016-11-27 18:34:46 +03:00
|
|
|
return this._auto(onSucc, onErr, () => this._getStats(selector));
|
|
|
|
},
|
|
|
|
|
|
|
|
_getStats: async function(selector) {
|
|
|
|
// getStats is allowed even in closed state.
|
|
|
|
return await this._chain(() => new Promise((resolve, reject) => {
|
|
|
|
this._onGetStatsSuccess = resolve;
|
|
|
|
this._onGetStatsFailure = reject;
|
|
|
|
this._impl.getStats(selector);
|
|
|
|
}));
|
2013-11-25 23:01:03 +04:00
|
|
|
},
|
|
|
|
|
2016-11-23 00:13:04 +03:00
|
|
|
createDataChannel: function(label, {
|
|
|
|
maxRetransmits, ordered, negotiated,
|
|
|
|
id = 0xFFFF,
|
|
|
|
maxRetransmitTime,
|
|
|
|
maxPacketLifeTime = maxRetransmitTime,
|
|
|
|
protocol,
|
|
|
|
} = {}) {
|
2013-01-18 00:25:17 +04:00
|
|
|
this._checkClosed();
|
2015-06-18 18:46:36 +03:00
|
|
|
|
2016-11-23 00:13:04 +03:00
|
|
|
if (maxRetransmitTime !== undefined) {
|
|
|
|
this.logWarning("Use maxPacketLifeTime instead of deprecated maxRetransmitTime which will stop working soon in createDataChannel!");
|
2013-06-04 01:34:42 +04:00
|
|
|
}
|
2016-11-23 00:13:04 +03:00
|
|
|
if (maxPacketLifeTime !== undefined && maxRetransmits !== undefined) {
|
2015-01-16 23:00:09 +03:00
|
|
|
throw new this._win.DOMException(
|
2016-11-23 00:13:04 +03:00
|
|
|
"Both maxPacketLifeTime and maxRetransmits cannot be provided",
|
2015-01-16 23:00:09 +03:00
|
|
|
"InvalidParameterError");
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
// Must determine the type where we still know if entries are undefined.
|
|
|
|
let type;
|
2016-11-23 00:13:04 +03:00
|
|
|
if (maxPacketLifeTime) {
|
2012-11-07 23:07:22 +04:00
|
|
|
type = Ci.IPeerConnection.kDataChannelPartialReliableTimed;
|
2016-11-23 00:13:04 +03:00
|
|
|
} else if (maxRetransmits) {
|
2012-11-07 23:07:22 +04:00
|
|
|
type = Ci.IPeerConnection.kDataChannelPartialReliableRexmit;
|
2012-10-07 09:34:30 +04:00
|
|
|
} else {
|
2012-11-07 23:07:22 +04:00
|
|
|
type = Ci.IPeerConnection.kDataChannelReliable;
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
// Synchronous since it doesn't block.
|
2016-11-23 00:13:04 +03:00
|
|
|
return this._impl.createDataChannel(label, protocol, type, ordered,
|
|
|
|
maxPacketLifeTime, maxRetransmits,
|
|
|
|
negotiated, id);
|
2012-10-07 09:34:30 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-04-17 03:23:07 +04:00
|
|
|
// This is a separate object because we don't want to expose it to DOM.
|
2016-11-20 00:47:10 +03:00
|
|
|
function PeerConnectionObserver() {}
|
2013-04-17 03:23:07 +04:00
|
|
|
PeerConnectionObserver.prototype = {
|
2013-10-14 20:53:56 +04:00
|
|
|
classDescription: "PeerConnectionObserver",
|
|
|
|
classID: PC_OBS_CID,
|
|
|
|
contractID: PC_OBS_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
|
|
|
Ci.nsIDOMGlobalPropertyInitializer]),
|
|
|
|
init: function(win) { this._win = win; },
|
|
|
|
|
|
|
|
__init: function(dompc) {
|
2013-11-02 04:49:48 +04:00
|
|
|
this._dompc = dompc._innerObject;
|
2013-10-14 20:53:56 +04:00
|
|
|
},
|
2013-03-30 00:30:16 +04:00
|
|
|
|
2015-01-16 23:00:09 +03:00
|
|
|
newError: function(message, code) {
|
2014-11-26 23:26:37 +03:00
|
|
|
// These strings must match those defined in the WebRTC spec.
|
|
|
|
const reasonName = [
|
|
|
|
"",
|
|
|
|
"InternalError",
|
2014-11-20 03:16:29 +03:00
|
|
|
"InvalidCandidateError",
|
2015-03-11 19:24:38 +03:00
|
|
|
"InvalidParameterError",
|
2014-11-26 23:26:37 +03:00
|
|
|
"InvalidStateError",
|
|
|
|
"InvalidSessionDescriptionError",
|
|
|
|
"IncompatibleSessionDescriptionError",
|
|
|
|
"InternalError",
|
|
|
|
"IncompatibleMediaStreamTrackError",
|
|
|
|
"InternalError"
|
|
|
|
];
|
|
|
|
let name = reasonName[Math.min(code, reasonName.length - 1)];
|
2015-01-16 23:00:09 +03:00
|
|
|
return new this._dompc._win.DOMException(message, name);
|
2014-11-26 23:26:37 +03:00
|
|
|
},
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
dispatchEvent: function(event) {
|
|
|
|
this._dompc.dispatchEvent(event);
|
|
|
|
},
|
|
|
|
|
|
|
|
onCreateOfferSuccess: function(sdp) {
|
2015-02-22 00:57:21 +03:00
|
|
|
this._dompc._onCreateOfferSuccess(sdp);
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onCreateOfferError: function(code, message) {
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onCreateOfferFailure(this.newError(message, code));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
onCreateAnswerSuccess: function(sdp) {
|
2015-02-22 00:57:21 +03:00
|
|
|
this._dompc._onCreateAnswerSuccess(sdp);
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onCreateAnswerError: function(code, message) {
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onCreateAnswerFailure(this.newError(message, code));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onSetLocalDescriptionSuccess: function() {
|
2014-11-27 06:35:54 +03:00
|
|
|
this._dompc._onSetLocalDescriptionSuccess();
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onSetRemoteDescriptionSuccess: function() {
|
2014-05-01 14:51:00 +04:00
|
|
|
this._dompc._onSetRemoteDescriptionSuccess();
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onSetLocalDescriptionError: function(code, message) {
|
2014-08-07 03:53:07 +04:00
|
|
|
this._localType = null;
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onSetLocalDescriptionFailure(this.newError(message, code));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onSetRemoteDescriptionError: function(code, message) {
|
2014-08-07 03:53:07 +04:00
|
|
|
this._remoteType = null;
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onSetRemoteDescriptionFailure(this.newError(message, code));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onAddIceCandidateSuccess: function() {
|
2014-11-27 06:35:54 +03:00
|
|
|
this._dompc._onAddIceCandidateSuccess();
|
2013-02-01 00:43:03 +04:00
|
|
|
},
|
|
|
|
|
2013-03-30 00:30:16 +04:00
|
|
|
onAddIceCandidateError: function(code, message) {
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onAddIceCandidateError(this.newError(message, code));
|
2013-02-01 00:43:03 +04:00
|
|
|
},
|
|
|
|
|
2016-11-20 00:47:10 +03:00
|
|
|
onIceCandidate: function(sdpMLineIndex, sdpMid, candidate) {
|
|
|
|
let win = this._dompc._win;
|
|
|
|
if (candidate) {
|
2016-12-08 03:59:32 +03:00
|
|
|
if (candidate.includes(" typ relay ")) {
|
|
|
|
this._dompc._iceGatheredRelayCandidates = true;
|
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
candidate = new win.RTCIceCandidate({ candidate, sdpMid, sdpMLineIndex });
|
|
|
|
} else {
|
|
|
|
candidate = null;
|
|
|
|
|
2014-08-07 21:20:26 +04:00
|
|
|
}
|
2016-11-20 00:47:10 +03:00
|
|
|
this.dispatchEvent(new win.RTCPeerConnectionIceEvent("icecandidate",
|
|
|
|
{ candidate }));
|
2013-09-17 04:42:39 +04:00
|
|
|
},
|
|
|
|
|
2014-12-11 02:53:54 +03:00
|
|
|
onNegotiationNeeded: function() {
|
|
|
|
this.dispatchEvent(new this._win.Event("negotiationneeded"));
|
|
|
|
},
|
|
|
|
|
2013-10-02 20:32:59 +04:00
|
|
|
|
2013-11-14 02:53:30 +04:00
|
|
|
// This method is primarily responsible for updating iceConnectionState.
|
|
|
|
// This state is defined in the WebRTC specification as follows:
|
2013-10-02 20:32:59 +04:00
|
|
|
//
|
|
|
|
// iceConnectionState:
|
|
|
|
// -------------------
|
2016-11-18 10:16:33 +03:00
|
|
|
// new Any of the RTCIceTransports are in the new state and none
|
|
|
|
// of them are in the checking, failed or disconnected state.
|
2013-10-02 20:32:59 +04:00
|
|
|
//
|
2016-11-18 10:16:33 +03:00
|
|
|
// checking Any of the RTCIceTransports are in the checking state and
|
|
|
|
// none of them are in the failed or disconnected state.
|
2013-10-02 20:32:59 +04:00
|
|
|
//
|
2016-11-18 10:16:33 +03:00
|
|
|
// connected All RTCIceTransports are in the connected, completed or
|
|
|
|
// closed state and at least one of them is in the connected
|
2013-10-02 20:32:59 +04:00
|
|
|
// state.
|
|
|
|
//
|
2016-11-18 10:16:33 +03:00
|
|
|
// completed All RTCIceTransports are in the completed or closed state
|
|
|
|
// and at least one of them is in the completed state.
|
|
|
|
//
|
|
|
|
// failed Any of the RTCIceTransports are in the failed state.
|
2013-10-02 20:32:59 +04:00
|
|
|
//
|
2016-11-18 10:16:33 +03:00
|
|
|
// disconnected Any of the RTCIceTransports are in the disconnected state
|
|
|
|
// and none of them are in the failed state.
|
2013-10-02 20:32:59 +04:00
|
|
|
//
|
2016-11-18 10:16:33 +03:00
|
|
|
// closed All of the RTCIceTransports are in the closed state.
|
2013-10-02 20:32:59 +04:00
|
|
|
|
2013-11-27 21:32:23 +04:00
|
|
|
handleIceConnectionStateChange: function(iceConnectionState) {
|
2016-01-06 03:51:52 +03:00
|
|
|
let pc = this._dompc;
|
2016-11-21 23:06:27 +03:00
|
|
|
if (pc.iceConnectionState === iceConnectionState) {
|
|
|
|
return;
|
|
|
|
}
|
2016-01-06 03:51:52 +03:00
|
|
|
if (pc.iceConnectionState === 'new') {
|
2015-11-17 00:01:46 +03:00
|
|
|
var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
|
|
|
|
if (iceConnectionState === 'checking') {
|
|
|
|
checking_histogram.add(true);
|
|
|
|
} else if (iceConnectionState === 'failed') {
|
|
|
|
checking_histogram.add(false);
|
|
|
|
}
|
2016-01-06 03:51:52 +03:00
|
|
|
} else if (pc.iceConnectionState === 'checking') {
|
2016-09-23 18:55:48 +03:00
|
|
|
var success_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
|
2015-11-17 00:01:46 +03:00
|
|
|
if (iceConnectionState === 'completed' ||
|
|
|
|
iceConnectionState === 'connected') {
|
|
|
|
success_histogram.add(true);
|
|
|
|
} else if (iceConnectionState === 'failed') {
|
|
|
|
success_histogram.add(false);
|
|
|
|
}
|
|
|
|
}
|
2013-10-02 20:32:59 +04:00
|
|
|
|
2013-11-27 21:32:23 +04:00
|
|
|
if (iceConnectionState === 'failed') {
|
2016-12-07 12:49:50 +03:00
|
|
|
if (!pc._hasStunServer) {
|
|
|
|
pc.logError("ICE failed, add a STUN server and see about:webrtc for more details");
|
|
|
|
}
|
|
|
|
else if (!pc._hasTurnServer) {
|
|
|
|
pc.logError("ICE failed, add a TURN server and see about:webrtc for more details");
|
|
|
|
}
|
2016-12-08 03:59:32 +03:00
|
|
|
else if (pc._hasTurnServer && !pc._iceGatheredRelayCandidates) {
|
|
|
|
pc.logError("ICE failed, your TURN server appears to be broken, see about:webrtc for more details");
|
|
|
|
}
|
2016-12-07 12:49:50 +03:00
|
|
|
else {
|
|
|
|
pc.logError("ICE failed, see about:webrtc for more details");
|
|
|
|
}
|
2013-10-02 20:32:59 +04:00
|
|
|
}
|
2015-11-17 00:01:46 +03:00
|
|
|
|
2016-01-06 03:51:52 +03:00
|
|
|
pc.changeIceConnectionState(iceConnectionState);
|
2013-11-14 02:53:30 +04:00
|
|
|
},
|
2013-10-02 20:32:59 +04:00
|
|
|
|
2013-11-14 02:53:30 +04:00
|
|
|
// This method is responsible for updating iceGatheringState. This
|
|
|
|
// state is defined in the WebRTC specification as follows:
|
|
|
|
//
|
|
|
|
// iceGatheringState:
|
|
|
|
// ------------------
|
|
|
|
// new The object was just created, and no networking has occurred
|
|
|
|
// yet.
|
|
|
|
//
|
2016-11-21 23:06:27 +03:00
|
|
|
// gathering The ICE agent is in the process of gathering candidates for
|
2013-11-14 02:53:30 +04:00
|
|
|
// this RTCPeerConnection.
|
|
|
|
//
|
2016-11-21 23:06:27 +03:00
|
|
|
// complete The ICE agent has completed gathering. Events such as adding
|
2013-11-14 02:53:30 +04:00
|
|
|
// a new interface or a new TURN server will cause the state to
|
|
|
|
// go back to gathering.
|
|
|
|
//
|
|
|
|
handleIceGatheringStateChange: function(gatheringState) {
|
2016-11-21 23:06:27 +03:00
|
|
|
let pc = this._dompc;
|
|
|
|
if (pc.iceGatheringState === gatheringState) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pc.changeIceGatheringState(gatheringState);
|
2013-05-17 03:41:46 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
onStateChange: function(state) {
|
|
|
|
switch (state) {
|
2013-10-14 20:53:56 +04:00
|
|
|
case "SignalingState":
|
2014-11-21 02:05:40 +03:00
|
|
|
this.dispatchEvent(new this._win.Event("signalingstatechange"));
|
2013-05-17 03:41:46 +04:00
|
|
|
break;
|
|
|
|
|
2013-11-14 02:53:30 +04:00
|
|
|
case "IceConnectionState":
|
2013-11-27 21:32:23 +04:00
|
|
|
this.handleIceConnectionStateChange(this._dompc._pc.iceConnectionState);
|
2013-11-14 02:53:30 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case "IceGatheringState":
|
|
|
|
this.handleIceGatheringStateChange(this._dompc._pc.iceGatheringState);
|
2013-05-17 03:41:46 +04:00
|
|
|
break;
|
|
|
|
|
2013-10-14 20:53:56 +04:00
|
|
|
case "SdpState":
|
2013-05-17 03:41:46 +04:00
|
|
|
// No-op
|
|
|
|
break;
|
|
|
|
|
2013-10-14 20:53:56 +04:00
|
|
|
case "ReadyState":
|
2013-10-02 20:32:59 +04:00
|
|
|
// No-op
|
|
|
|
break;
|
|
|
|
|
2013-10-14 20:53:56 +04:00
|
|
|
case "SipccState":
|
2013-05-17 03:41:46 +04:00
|
|
|
// No-op
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-03-09 06:44:06 +03:00
|
|
|
this._dompc.logWarning("Unhandled state type: " + state);
|
2012-10-07 09:34:30 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-10-18 02:00:05 +04:00
|
|
|
onGetStatsSuccess: function(dict) {
|
2016-01-06 03:51:52 +03:00
|
|
|
let pc = this._dompc;
|
|
|
|
let chromeobj = new RTCStatsReport(pc._win, dict);
|
|
|
|
let webidlobj = pc._win.RTCStatsReport._create(pc._win, chromeobj);
|
2016-12-13 03:17:38 +03:00
|
|
|
chromeobj.makeStatsPublic(pc._warnDeprecatedStatsCallbacksNullable &&
|
|
|
|
pc._warnDeprecatedStatsAccessNullable);
|
2016-01-06 03:51:52 +03:00
|
|
|
pc._onGetStatsSuccess(webidlobj);
|
2013-10-18 02:00:05 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
onGetStatsError: function(code, message) {
|
2015-01-16 23:00:09 +03:00
|
|
|
this._dompc._onGetStatsFailure(this.newError(message, code));
|
2013-10-18 02:00:05 +04:00
|
|
|
},
|
|
|
|
|
2013-05-08 04:55:21 +04:00
|
|
|
onAddStream: function(stream) {
|
2016-11-20 00:47:10 +03:00
|
|
|
let ev = new this._dompc._win.MediaStreamEvent("addstream", { stream });
|
2014-11-27 06:35:54 +03:00
|
|
|
this.dispatchEvent(ev);
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2016-01-06 03:51:52 +03:00
|
|
|
onRemoveStream: function(stream) {
|
2013-05-08 04:55:21 +04:00
|
|
|
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
|
2016-11-20 00:47:10 +03:00
|
|
|
{ stream }));
|
2012-10-07 09:34:30 +04:00
|
|
|
},
|
|
|
|
|
2016-01-12 23:09:01 +03:00
|
|
|
onAddTrack: function(track, streams) {
|
2015-12-12 07:47:29 +03:00
|
|
|
let pc = this._dompc;
|
|
|
|
let receiver = pc._win.RTCRtpReceiver._create(pc._win,
|
|
|
|
new RTCRtpReceiver(this,
|
|
|
|
track));
|
|
|
|
pc._receivers.push(receiver);
|
2016-11-20 00:47:10 +03:00
|
|
|
let ev = new pc._win.RTCTrackEvent("track", { receiver, track, streams });
|
2016-01-12 23:09:01 +03:00
|
|
|
this.dispatchEvent(ev);
|
|
|
|
|
|
|
|
// Fire legacy event as well for a little bit.
|
2016-11-20 00:47:10 +03:00
|
|
|
ev = new pc._win.MediaStreamTrackEvent("addtrack", { track });
|
2014-11-27 06:35:54 +03:00
|
|
|
this.dispatchEvent(ev);
|
2014-08-14 05:40:54 +04:00
|
|
|
},
|
|
|
|
|
2016-01-06 03:51:52 +03:00
|
|
|
onRemoveTrack: function(track) {
|
|
|
|
let pc = this._dompc;
|
|
|
|
let i = pc._receivers.findIndex(receiver => receiver.track == track);
|
2015-12-12 07:47:29 +03:00
|
|
|
if (i >= 0) {
|
2016-01-06 03:51:52 +03:00
|
|
|
pc._receivers.splice(i, 1);
|
2015-12-12 07:47:29 +03:00
|
|
|
}
|
2014-08-14 05:40:54 +04:00
|
|
|
},
|
|
|
|
|
2014-08-15 09:33:09 +04:00
|
|
|
onReplaceTrackSuccess: function() {
|
2014-08-27 03:31:51 +04:00
|
|
|
var pc = this._dompc;
|
|
|
|
pc._onReplaceTrackSender.track = pc._onReplaceTrackWithTrack;
|
|
|
|
pc._onReplaceTrackWithTrack = null;
|
|
|
|
pc._onReplaceTrackSender = null;
|
2014-11-27 06:35:54 +03:00
|
|
|
pc._onReplaceTrackSuccess();
|
2014-08-15 09:33:09 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
onReplaceTrackError: function(code, message) {
|
2014-08-27 03:31:51 +04:00
|
|
|
var pc = this._dompc;
|
|
|
|
pc._onReplaceTrackWithTrack = null;
|
|
|
|
pc._onReplaceTrackSender = null;
|
2015-02-26 03:11:15 +03:00
|
|
|
pc._onReplaceTrackFailure(this.newError(message, code));
|
2014-08-15 09:33:09 +04:00
|
|
|
},
|
|
|
|
|
2012-10-07 09:34:30 +04:00
|
|
|
notifyDataChannel: function(channel) {
|
2013-05-17 03:40:23 +04:00
|
|
|
this.dispatchEvent(new this._dompc._win.RTCDataChannelEvent("datachannel",
|
2016-11-20 00:47:10 +03:00
|
|
|
{ channel }));
|
2016-09-14 15:53:23 +03:00
|
|
|
},
|
|
|
|
|
2016-09-29 20:26:04 +03:00
|
|
|
onDTMFToneChange: function(trackId, tone) {
|
|
|
|
var pc = this._dompc;
|
2016-11-20 00:47:10 +03:00
|
|
|
var sender = pc._senders.find(({track}) => track.id == trackId);
|
2016-09-29 20:26:04 +03:00
|
|
|
sender.dtmf.dispatchEvent(new pc._win.RTCDTMFToneChangeEvent("tonechange",
|
2016-11-20 00:47:10 +03:00
|
|
|
{ tone }));
|
2014-07-10 22:31:25 +04:00
|
|
|
}
|
2013-10-19 20:21:06 +04:00
|
|
|
};
|
|
|
|
|
2014-05-23 01:14:56 +04:00
|
|
|
function RTCPeerConnectionStatic() {
|
|
|
|
}
|
|
|
|
RTCPeerConnectionStatic.prototype = {
|
2015-09-28 21:25:04 +03:00
|
|
|
classDescription: "RTCPeerConnectionStatic",
|
2014-05-23 01:14:56 +04:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
|
|
|
Ci.nsIDOMGlobalPropertyInitializer]),
|
|
|
|
|
|
|
|
classID: PC_STATIC_CID,
|
|
|
|
contractID: PC_STATIC_CONTRACT,
|
|
|
|
|
|
|
|
init: function(win) {
|
|
|
|
this._winID = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
|
|
|
},
|
|
|
|
|
|
|
|
registerPeerConnectionLifecycleCallback: function(cb) {
|
|
|
|
_globalPCList._registerPeerConnectionLifecycleCallback(this._winID, cb);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2016-09-29 20:26:04 +03:00
|
|
|
function RTCDTMFSender(sender) {
|
|
|
|
this._sender = sender;
|
|
|
|
}
|
|
|
|
RTCDTMFSender.prototype = {
|
|
|
|
classDescription: "RTCDTMFSender",
|
|
|
|
classID: PC_DTMF_SENDER_CID,
|
|
|
|
contractID: PC_DTMF_SENDER_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
|
|
|
|
|
|
|
get toneBuffer() {
|
|
|
|
return this._sender._pc._getDTMFToneBuffer(this._sender);
|
|
|
|
},
|
|
|
|
|
|
|
|
get ontonechange() {
|
|
|
|
return this.__DOM_IMPL__.getEventHandler("ontonechange");
|
|
|
|
},
|
|
|
|
|
|
|
|
set ontonechange(handler) {
|
|
|
|
this.__DOM_IMPL__.setEventHandler("ontonechange", handler);
|
|
|
|
},
|
|
|
|
|
|
|
|
insertDTMF: function(tones, duration, interToneGap) {
|
|
|
|
this._sender._pc._checkClosed();
|
|
|
|
|
|
|
|
if (this._sender._pc._senders.indexOf(this._sender.__DOM_IMPL__) == -1) {
|
|
|
|
throw new this._sender._pc._win.DOMException("RTCRtpSender is stopped",
|
|
|
|
"InvalidStateError");
|
|
|
|
}
|
|
|
|
|
2016-10-11 21:05:08 +03:00
|
|
|
duration = Math.max(40, Math.min(duration, 6000));
|
2016-09-29 20:26:04 +03:00
|
|
|
if (interToneGap < 30) interToneGap = 30;
|
|
|
|
|
|
|
|
tones = tones.toUpperCase();
|
|
|
|
|
|
|
|
if (tones.match(/[^0-9A-D#*,]/)) {
|
|
|
|
throw new this._sender._pc._win.DOMException("Invalid DTMF characters",
|
|
|
|
"InvalidCharacterError");
|
|
|
|
}
|
|
|
|
|
|
|
|
this._sender._pc._insertDTMF(this._sender, tones, duration, interToneGap);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2014-08-15 09:33:09 +04:00
|
|
|
function RTCRtpSender(pc, track, stream) {
|
2016-11-20 00:47:10 +03:00
|
|
|
let dtmf = pc._win.RTCDTMFSender._create(pc._win, new RTCDTMFSender(this));
|
|
|
|
Object.assign(this, { _pc: pc, track, _stream: stream, dtmf });
|
2014-08-14 05:40:54 +04:00
|
|
|
}
|
2014-08-14 05:40:41 +04:00
|
|
|
RTCRtpSender.prototype = {
|
|
|
|
classDescription: "RTCRtpSender",
|
|
|
|
classID: PC_SENDER_CID,
|
|
|
|
contractID: PC_SENDER_CONTRACT,
|
2014-08-14 05:40:54 +04:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
2014-08-15 09:33:09 +04:00
|
|
|
|
2014-12-08 18:36:18 +03:00
|
|
|
replaceTrack: function(withTrack) {
|
2016-11-27 18:34:46 +03:00
|
|
|
return this._pc._async(() => this._pc._replaceTrack(this, withTrack));
|
2015-12-31 19:32:24 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
setParameters: function(parameters) {
|
2016-02-25 03:14:57 +03:00
|
|
|
return this._pc._win.Promise.resolve()
|
|
|
|
.then(() => this._pc._setParameters(this, parameters));
|
2015-12-31 19:32:24 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
getParameters: function() {
|
|
|
|
return this._pc._getParameters(this);
|
2014-08-15 09:33:09 +04:00
|
|
|
}
|
2014-08-14 05:40:41 +04:00
|
|
|
};
|
|
|
|
|
2014-08-14 05:40:54 +04:00
|
|
|
function RTCRtpReceiver(pc, track) {
|
2016-11-20 00:47:10 +03:00
|
|
|
Object.assign(this, { _pc: pc, track });
|
2014-08-14 05:40:54 +04:00
|
|
|
}
|
2014-08-14 05:40:41 +04:00
|
|
|
RTCRtpReceiver.prototype = {
|
|
|
|
classDescription: "RTCRtpReceiver",
|
|
|
|
classID: PC_RECEIVER_CID,
|
|
|
|
contractID: PC_RECEIVER_CONTRACT,
|
2014-08-14 05:40:54 +04:00
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
2014-08-14 05:40:41 +04:00
|
|
|
};
|
|
|
|
|
2015-08-11 20:48:55 +03:00
|
|
|
function CreateOfferRequest(windowID, innerWindowID, callID, isSecure) {
|
2016-11-20 00:47:10 +03:00
|
|
|
Object.assign(this, { windowID, innerWindowID, callID, isSecure });
|
2015-08-11 20:48:55 +03:00
|
|
|
}
|
|
|
|
CreateOfferRequest.prototype = {
|
|
|
|
classDescription: "CreateOfferRequest",
|
|
|
|
classID: PC_COREQUEST_CID,
|
|
|
|
contractID: PC_COREQUEST_CONTRACT,
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
|
|
|
|
};
|
|
|
|
|
2012-10-31 20:13:28 +04:00
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
|
2013-11-25 23:01:03 +04:00
|
|
|
[GlobalPCList,
|
2016-09-29 20:26:04 +03:00
|
|
|
RTCDTMFSender,
|
2013-11-25 23:01:03 +04:00
|
|
|
RTCIceCandidate,
|
|
|
|
RTCSessionDescription,
|
|
|
|
RTCPeerConnection,
|
2014-05-23 01:14:56 +04:00
|
|
|
RTCPeerConnectionStatic,
|
2014-08-14 05:40:41 +04:00
|
|
|
RTCRtpReceiver,
|
|
|
|
RTCRtpSender,
|
2013-11-25 23:01:03 +04:00
|
|
|
RTCStatsReport,
|
2015-08-11 20:48:55 +03:00
|
|
|
PeerConnectionObserver,
|
|
|
|
CreateOfferRequest]
|
2012-10-07 09:34:30 +04:00
|
|
|
);
|