--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -272,39 +272,74 @@ class RTCSessionDescription {
}
setupPrototype(RTCSessionDescription, {
classID: PC_SESSION_CID,
contractID: PC_SESSION_CONTRACT,
QueryInterface: ChromeUtils.generateQI([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);
}
// 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(warnNullable, 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 = Cu.createObjectIn(this._win);
+ let stat = internal;
+ for (let key in stat) {
+ Object.defineProperty(entry, 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)
+ });
+ }
+ Cu.unwaiveXrays(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) {
@@ -315,18 +350,23 @@ class RTCStatsReport {
}, this.__DOM_IMPL__.wrappedJSObject)
};
}
Object.defineProperties(this.__DOM_IMPL__.wrappedJSObject, legacyProps);
}
get mozPcid() { return this._pcid; }
- __onget(key, value) {
- /* Do whatever here */
+ __onget(key, stat) {
+ if (stat && stat._isRemote &&
+ this._pc._warnDeprecatedStatsRemoteAccessNullable.warn) {
+ // Proper stats.get(localStat.remoteId) access detected. Null warning.
+ this._pc._warnDeprecatedStatsRemoteAccessNullable.warn = null;
+ }
+ return stat;
}
}
setupPrototype(RTCStatsReport, {
classID: PC_STATS_CID,
contractID: PC_STATS_CONTRACT,
QueryInterface: ChromeUtils.generateQI([]),
_specToLegacyFieldMapping: {
"inbound-rtp": "inboundrtp",
@@ -455,16 +495,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();
@@ -1811,20 +1855,20 @@ 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._warnDeprecatedStatsAccessNullable,
+ pc._warnDeprecatedStatsRemoteAccessNullable,
pc._onGetStatsIsLegacy);
pc._onGetStatsSuccess(webidlobj);
}
onGetStatsError(code, message) {
this._dompc._onGetStatsFailure(this.newError(message, code));
}
--- a/dom/webidl/RTCStatsReport.webidl
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -190,16 +190,14 @@ dictionary RTCStatsReportInternal {
boolean offerer; // Is the PC the offerer
boolean closed; // Is the PC now closed
sequence<RTCIceCandidateStats> trickledIceCandidateStats;
sequence<DOMString> rawLocalCandidates;
sequence<DOMString> rawRemoteCandidates;
};
[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;
};