--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -17,28 +17,30 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/AppConstants.jsm");
const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1";
+const PC_STATS_WARN_CONTRACT = "@mozilla.org/dom/rtcstatsdeprecationwarningentry;1";
const PC_STATIC_CONTRACT = "@mozilla.org/dom/peerconnectionstatic;1";
const PC_SENDER_CONTRACT = "@mozilla.org/dom/rtpsender;1";
const PC_RECEIVER_CONTRACT = "@mozilla.org/dom/rtpreceiver;1";
const PC_COREQUEST_CONTRACT = "@mozilla.org/dom/createofferrequest;1";
const PC_DTMF_SENDER_CONTRACT = "@mozilla.org/dom/rtcdtmfsender;1";
const PC_CID = Components.ID("{bdc2e533-b308-4708-ac8e-a8bfade6d851}");
const PC_OBS_CID = Components.ID("{d1748d4c-7f6a-4dc5-add6-d55b7678537e}");
const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}");
+const PC_STATS_WARN_CID = Components.ID("{7b78e3f4-4720-4c14-b02d-5421cbeee134}");
const PC_STATIC_CID = Components.ID("{0fb47c47-a205-4583-a9fc-cbadf8c95880}");
const PC_SENDER_CID = Components.ID("{4fff5d46-d827-4cd4-a970-8fd53977440e}");
const PC_RECEIVER_CID = Components.ID("{d974b814-8fde-411c-8c45-b86791b81030}");
const PC_COREQUEST_CID = Components.ID("{74b2122d-65a8-4824-aa9e-3d664cb75dc2}");
const PC_DTMF_SENDER_CID = Components.ID("{3610C242-654E-11E6-8EC0-6D1BE389A607}");
function logMsg(msg, file, line, flag, winID) {
let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
@@ -280,49 +282,96 @@ class RTCSessionDescription {
setupPrototype(RTCSessionDescription, {
classID: PC_SESSION_CID,
contractID: PC_SESSION_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer])
});
class RTCStatsReport {
- constructor(win, dict) {
- this._win = win;
+ constructor(pc, dict) {
+ this._pc = pc;
+ this._win = pc._win;
this._pcid = dict.pcid;
this._report = convertToRTCStatsReport(dict);
}
setInternal(aKey, aObj) {
return this.__DOM_IMPL__.__set(aKey, aObj);
}
+ warnGet(id) {
+ let stat = this.__DOM_IMPL__.nativeGet(id);
+ if (stat && stat._isRemote &&
+ this._pc._warnDeprecatedStatsRemoteAccessNullable.warn) {
+ // Proper stats.get(localStat.remoteId) access detected. Null warning.
+ this._pc._warnDeprecatedStatsRemoteAccessNullable.warn = null;
+ }
+ return stat;
+ }
+
// TODO: Remove legacy API eventually
// see Bug 1328194
//
// Since maplike is recent, we still also make the stats available as legacy
// enumerable read-only properties directly on our content-facing object.
+ //
+ // In addition, we warn on iteration over isRemote:true entries, which is set
+ // to break in Firefox 58.
+ //
// Must be called after our webidl sandwich is made.
- makeStatsPublic(warnNullable, isLegacy) {
+ makeStatsPublic(warnCallbackNullable, warnRemoteNullable, isLegacy) {
let legacyProps = {};
for (let key in this._report) {
let internal = Cu.cloneInto(this._report[key], this._win);
if (isLegacy) {
internal.type = this._specToLegacyFieldMapping[internal.type] || internal.type;
+ } else if (warnRemoteNullable.warn) {
+ let entry = new RTCStatsDeprecationWarningEntry();
+ entry = this._win.RTCStatsDeprecationWarningEntry._create(this._win, entry);
+ let warnProps = {}, stat = internal;
+ for (let key in stat) {
+ warnProps[key] = {
+ enumerable: true, configurable: false,
+ get: Cu.exportFunction(function() {
+ // Warn on remote stat access other than the recommended approach of
+ //
+ // for (let stat of stats.values()) {
+ // switch (stat.type) {
+ // case "outbound-rtp": {
+ // if (stat.isRemote) continue;
+ // let rtcp = stats.get(stat.remoteId);
+ //
+ if (warnRemoteNullable.warn && stat.isRemote &&
+ key != "type" &&
+ key != "isRemote") {
+ // id is first prop, a sign of JSON.stringify(), cancel warnings.
+ if (key != "id") {
+ warnRemoteNullable.warn();
+ }
+ warnRemoteNullable.warn = null;
+ }
+ return stat[key];
+ }, entry)
+ };
+ }
+ Object.defineProperties(entry.wrappedJSObject, warnProps);
+ entry._isRemote = stat.isRemote;
+ internal = entry;
}
this.setInternal(key, internal);
let value = Cu.cloneInto(this._report[key], this._win);
value.type = this._specToLegacyFieldMapping[value.type] || value.type;
legacyProps[key] = {
enumerable: true, configurable: false,
get: Cu.exportFunction(function() {
- if (warnNullable.warn) {
- warnNullable.warn();
- warnNullable.warn = null;
+ if (warnCallbackNullable.warn) {
+ warnCallbackNullable.warn();
+ warnCallbackNullable.warn = null;
}
return value;
}, this.__DOM_IMPL__.wrappedJSObject)
};
}
Object.defineProperties(this.__DOM_IMPL__.wrappedJSObject, legacyProps);
}
@@ -340,16 +389,26 @@ setupPrototype(RTCStatsReport, {
"inbound-rtp": "inboundrtp",
"outbound-rtp": "outboundrtp",
"candidate-pair": "candidatepair",
"local-candidate": "localcandidate",
"remote-candidate": "remotecandidate"
}
});
+
+class RTCStatsDeprecationWarningEntry {
+}
+setupPrototype(RTCStatsDeprecationWarningEntry, {
+ classID: PC_STATS_WARN_CID,
+ contractID: PC_STATS_WARN_CONTRACT,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
+});
+
+
class RTCPeerConnection {
constructor() {
this._senders = [];
this._receivers = [];
this._pc = null;
this._closed = false;
@@ -443,16 +502,20 @@ class RTCPeerConnection {
this._warnDeprecatedStatsAccessNullable = { warn: () =>
this.logWarning("non-maplike pc.getStats access is deprecated, and will be removed in the near future! " +
"See http://w3c.github.io/webrtc-pc/#getstats-example for usage.") };
this._warnDeprecatedStatsCallbacksNullable = { warn: () =>
this.logWarning("Callback-based pc.getStats is deprecated, and will be removed in the near future! Use promise-version! " +
"See http://w3c.github.io/webrtc-pc/#getstats-example for usage.") };
+ this._warnDeprecatedStatsRemoteAccessNullable = { warn: () =>
+ this.logWarning("Detected soon-to-break getStats() use! stat.isRemote goes away in Firefox 58, but won't warn there!\
+ - See https://blog.mozilla.org/webrtc/getstats-firefox-58/") };
+
// Add a reference to the PeerConnection to global list (before init).
_globalPCList.addPC(this);
this._impl.initialize(this._observer, this._win, rtcConfig,
Services.tm.currentThread);
this._certificateReady = this._initCertificate(rtcConfig.certificates);
this._initIdp();
@@ -1541,21 +1604,25 @@ class PeerConnectionObserver {
default:
this._dompc.logWarning("Unhandled state type: " + state);
break;
}
}
onGetStatsSuccess(dict) {
let pc = this._dompc;
- let chromeobj = new RTCStatsReport(pc._win, dict);
+ let chromeobj = new RTCStatsReport(pc, dict);
let webidlobj = pc._win.RTCStatsReport._create(pc._win, chromeobj);
- chromeobj.makeStatsPublic(pc._warnDeprecatedStatsCallbacksNullable &&
- pc._warnDeprecatedStatsAccessNullable,
+ chromeobj.makeStatsPublic(pc._warnDeprecatedStatsCallbacksNullable,
+ pc._warnDeprecatedStatsRemoteAccessNullable,
pc._onGetStatsIsLegacy);
+ webidlobj.nativeGet = webidlobj.get;
+ if (pc._warnDeprecatedStatsRemoteAccessNullable.warn) {
+ webidlobj.wrappedJSObject.get = webidlobj.wrappedJSObject.warnGet;
+ }
pc._onGetStatsSuccess(webidlobj);
}
onGetStatsError(code, message) {
this._dompc._onGetStatsFailure(this.newError(message, code));
}
onAddStream(stream) {
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -184,16 +184,24 @@ dictionary RTCStatsReportInternal {
DOMString remoteSdp;
DOMHighResTimeStamp timestamp;
unsigned long iceRestarts;
unsigned long iceRollbacks;
boolean closed; // Is the PC now closed
};
[Pref="media.peerconnection.enabled",
-// TODO: Use MapClass here once it's available (Bug 928114)
-// MapClass(DOMString, object)
JSImplementation="@mozilla.org/dom/rtcstatsreport;1"]
interface RTCStatsReport {
readonly maplike<DOMString, object>;
[ChromeOnly]
readonly attribute DOMString mozPcid;
+
+ // Special internal method to help intercept Map.get() to detect proper
+ // stats.get(otherStat.remoteId) use to turn off deprecation warnings.
+ any warnGet(DOMString id);
};
+
+[Pref="media.peerconnection.enabled",
+ ChromeOnly,
+ JSImplementation="@mozilla.org/dom/rtcstatsdeprecationwarningentry;1"]
+interface RTCStatsDeprecationWarningEntry {
+};