Bug 958221 - Part 3: New webidl for WebrtcGlobalInformation, c++ impl, and removing logging-related stuff from PeerConnectionImpl. r=jib
authorByron Campen [:bwc] <docfaraday@gmail.com>
Thu, 20 Feb 2014 09:35:35 -0800
changeset 172100 5cdd9dc14f1e3c45621180df060ea06bd22fd71f
parent 172099 95e8301cb71e00e271cb9461fd79736df70e2d12
child 172101 4ac5c87912d059a8803937f31c8447f8ecec04c8
push id26349
push userkwierso@gmail.com
push dateThu, 06 Mar 2014 02:08:58 +0000
treeherdermozilla-central@c7d401d189e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs958221
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 958221 - Part 3: New webidl for WebrtcGlobalInformation, c++ impl, and removing logging-related stuff from PeerConnectionImpl. r=jib
dom/bindings/Bindings.conf
dom/media/PeerConnection.js
dom/media/PeerConnection.manifest
dom/webidl/PeerConnectionImpl.webidl
dom/webidl/PeerConnectionObserver.webidl
dom/webidl/RTCPeerConnection.webidl
dom/webidl/WebrtcGlobalInformation.webidl
dom/webidl/moz.build
media/webrtc/moz.build
media/webrtc/signaling/signaling.gyp
media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
toolkit/content/aboutWebrtc.xhtml
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1415,16 +1415,23 @@ DOMInterfaces = {
     'wrapperCache': False
 },
 
 'WebGLVertexArray': {
     'nativeType': 'mozilla::WebGLVertexArray',
     'headerFile': 'WebGLVertexArray.h'
 },
 
+'WebrtcGlobalInformation': {
+    'nativeType': 'mozilla::dom::WebrtcGlobalInformation',
+    'headerFile': 'WebrtcGlobalInformation.h',
+    'wrapperCache': False,
+    'concrete': False,
+},
+
 'WebSocket': {
     'headerFile': 'WebSocket.h',
 },
 
 'Window': {
     'nativeType': 'nsGlobalWindow',
     # When turning on Window, remember to drop the "'register': False"
     # from ChromeWindow.
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -9,26 +9,24 @@ const {classes: Cc, interfaces: Ci, util
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PopupNotifications.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp",
   "resource://gre/modules/media/PeerConnectionIdp.jsm");
 
 const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
-const WEBRTC_GLOBAL_CONTRACT = "@mozilla.org/dom/webrtcglobalinformation1";
 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_IDENTITY_CONTRACT = "@mozilla.org/dom/rtcidentityassertion;1";
 
 const PC_CID = Components.ID("{00e0e20d-1494-4776-8e0e-0f0acbea3c79}");
-const WEBRTC_GLOBAL_CID = Components.ID("{f6063d11-f467-49ad-9765-e7923050dc08}");
 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_IDENTITY_CID = Components.ID("{1abc7499-3c54-43e0-bd60-686e2703f072}");
 
 // Global list of PeerConnection objects, so they can be cleaned up when
@@ -119,82 +117,19 @@ GlobalPCList.prototype = {
         // this._list shold be empty here
         this._networkdown = true;
       } else if (data == "online") {
         this._networkdown = false;
       }
     }
   },
 
-  getStatsForEachPC: function(callback, errorCallback) {
-    function getStatsFromPC(pcref) {
-      try {
-        pcref.get().getStatsInternal(null, callback, errorCallback);
-      } catch (e) {
-        errorCallback("Some error getting stats from PC: " + e.toString());
-      }
-    }
-
-    for (let winId in this._list) {
-      if (this._list.hasOwnProperty(winId)) {
-        this.removeNullRefs(winId);
-        if (this._list[winId]) {
-          this._list[winId].forEach(getStatsFromPC);
-        }
-      }
-    }
-  },
-
-  // TODO(bcampen@mozilla.com): Handle this with a global object in c++
-  // (Bug 958221)
-  getLoggingFromFirstPC: function(pattern, callback, errorCallback) {
-    for (let winId in this._list) {
-      this.removeNullRefs(winId);
-      if (this._list[winId]) {
-        // We expect removeNullRefs to not leave us with an empty array here
-        let pcref = this._list[winId][0];
-        pcref.get().getLogging(pattern, callback, errorCallback);
-        return;
-      }
-    }
-  },
 };
 let _globalPCList = new GlobalPCList();
 
-function WebrtcGlobalInformation() {
-}
-WebrtcGlobalInformation.prototype = {
-  classDescription: "WebrtcGlobalInformation",
-  classID: WEBRTC_GLOBAL_CID,
-  contractID: WEBRTC_GLOBAL_CONTRACT,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
-
-  getAllStats: function(successCallback, failureCallback) {
-    // TODO(bcampen@mozilla.com): Move the work of fanout into c++, and
-    // only callback once. (Bug 958221)
-    if (_globalPCList) {
-      _globalPCList.getStatsForEachPC(successCallback, failureCallback);
-    } else {
-      failureCallback("No global PeerConnection list");
-    }
-  },
-
-  getLogs: function(pattern, callback, errorCallback) {
-    if (_globalPCList) {
-      _globalPCList.getLoggingFromFirstPC(pattern, callback, errorCallback);
-    } else {
-      errorCallback("No global PeerConnection list");
-    }
-  },
-
-  getCandPairLogs: function(candPairId, callback, errorCallback) {
-    this.getLogs('CAND-PAIR(' + candPairId + ')', callback, errorCallback);
-  },
-};
-
 function RTCIceCandidate() {
   this.candidate = this.sdpMid = this.sdpMLineIndex = null;
 }
 RTCIceCandidate.prototype = {
   classDescription: "mozRTCIceCandidate",
   classID: PC_ICE_CID,
   contractID: PC_ICE_CONTRACT,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
@@ -320,18 +255,16 @@ function RTCPeerConnection() {
   this._closed = false;
 
   this._onCreateOfferSuccess = null;
   this._onCreateOfferFailure = null;
   this._onCreateAnswerSuccess = null;
   this._onCreateAnswerFailure = null;
   this._onGetStatsSuccess = null;
   this._onGetStatsFailure = null;
-  this._onGetLoggingSuccess = null;
-  this._onGetLoggingFailure = null;
 
   this._pendingType = null;
   this._localType = null;
   this._remoteType = null;
   this._trickleIce = false;
   this._peerIdentity = null;
 
   /**
@@ -917,49 +850,26 @@ RTCPeerConnection.prototype = {
   changeIceConnectionState: function(state) {
     this._iceConnectionState = state;
     this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
   },
 
   getStats: function(selector, onSuccess, onError) {
     this._queueOrRun({
       func: this._getStats,
-      args: [selector, onSuccess, onError, false],
-      wait: true
-    });
-  },
-
-  getStatsInternal: function(selector, onSuccess, onError) {
-    this._queueOrRun({
-      func: this._getStats,
-      args: [selector, onSuccess, onError, true],
+      args: [selector, onSuccess, onError],
       wait: true
     });
   },
 
-  _getStats: function(selector, onSuccess, onError, internal) {
+  _getStats: function(selector, onSuccess, onError) {
     this._onGetStatsSuccess = onSuccess;
     this._onGetStatsFailure = onError;
 
-    this._impl.getStats(selector, internal);
-  },
-
-  getLogging: function(pattern, onSuccess, onError) {
-    this._queueOrRun({
-      func: this._getLogging,
-      args: [pattern, onSuccess, onError],
-      wait: true
-    });
-  },
-
-  _getLogging: function(pattern, onSuccess, onError) {
-    this._onGetLoggingSuccess = onSuccess;
-    this._onGetLoggingFailure = onError;
-
-    this._impl.getLogging(pattern);
+    this._impl.getStats(selector);
   },
 
   createDataChannel: function(label, dict) {
     this._checkClosed();
     if (dict == undefined) {
       dict = {};
     }
     if (dict.maxRetransmitNum != undefined) {
@@ -1301,26 +1211,16 @@ PeerConnectionObserver.prototype = {
     this._dompc._executeNext();
   },
 
   onGetStatsError: function(code, message) {
     this.callCB(this._dompc._onGetStatsFailure, new RTCError(code, message));
     this._dompc._executeNext();
   },
 
-  onGetLoggingSuccess: function(logs) {
-    this.callCB(this._dompc._onGetLoggingSuccess, logs);
-    this._dompc._executeNext();
-  },
-
-  onGetLoggingError: function(code, message) {
-    this.callCB(this._dompc._onGetLoggingFailure, new RTCError(code, message));
-    this._dompc._executeNext();
-  },
-
   onAddStream: function(stream) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
                                                              { stream: stream }));
   },
 
   onRemoveStream: function(stream, type) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
                                                              { stream: stream }));
@@ -1351,11 +1251,10 @@ PeerConnectionObserver.prototype = {
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList,
    RTCIceCandidate,
    RTCSessionDescription,
    RTCPeerConnection,
    RTCStatsReport,
    RTCIdentityAssertion,
-   PeerConnectionObserver,
-   WebrtcGlobalInformation]
+   PeerConnectionObserver]
 );
--- a/dom/media/PeerConnection.manifest
+++ b/dom/media/PeerConnection.manifest
@@ -1,17 +1,15 @@
 component {00e0e20d-1494-4776-8e0e-0f0acbea3c79} PeerConnection.js
-component {f6063d11-f467-49ad-9765-e7923050dc08} PeerConnection.js
 component {d1748d4c-7f6a-4dc5-add6-d55b7678537e} PeerConnection.js
 component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
 component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
 component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
 component {7fe6e18b-0da3-4056-bf3b-440ef3809e06} PeerConnection.js
 component {1abc7499-3c54-43e0-bd60-686e2703f072} PeerConnection.js
 
 contract @mozilla.org/dom/peerconnection;1 {00e0e20d-1494-4776-8e0e-0f0acbea3c79}
-contract @mozilla.org/dom/webrtcglobalinformation;1 {f6063d11-f467-49ad-9765-e7923050dc08}
 contract @mozilla.org/dom/peerconnectionobserver;1 {d1748d4c-7f6a-4dc5-add6-d55b7678537e}
 contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
 contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
 contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
 contract @mozilla.org/dom/rtcstatsreport;1 {7fe6e18b-0da3-4056-bf3b-440ef3809e06}
 contract @mozilla.org/dom/rtcidentityassertion;1 {1abc7499-3c54-43e0-bd60-686e2703f072}
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -31,23 +31,17 @@ interface PeerConnectionImpl  {
   [Throws]
   void setLocalDescription(long action, DOMString sdp);
   [Throws]
   void setRemoteDescription(long action, DOMString sdp);
 
   /* Stats call, calls either |onGetStatsSuccess| or |onGetStatsError| on our
      observer. (see the |PeerConnectionObserver| interface) */
   [Throws]
-  void getStats(MediaStreamTrack? selector, boolean internalStats);
-
-  /* Scrapes the RLogRingbuffer, and calls either |onGetLoggingSuccess|
-     or |onGetLoggingError| on our observer.
-     (see the |PeerConnectionObserver| interface) */
-  [Throws]
-  void getLogging(DOMString pattern);
+  void getStats(MediaStreamTrack? selector);
 
   /* Adds the stream created by GetUserMedia */
   [Throws]
   void addStream(MediaStream stream,
                  optional MediaConstraintsInternal constraints);
   [Throws]
   void removeStream(MediaStream stream);
   [Throws]
--- a/dom/webidl/PeerConnectionObserver.webidl
+++ b/dom/webidl/PeerConnectionObserver.webidl
@@ -23,20 +23,16 @@ interface PeerConnectionObserver
   void onAddIceCandidateSuccess();
   void onAddIceCandidateError(unsigned long name, DOMString message);
   void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate);
 
   /* Stats callbacks */
   void onGetStatsSuccess(optional RTCStatsReportInternal report);
   void onGetStatsError(unsigned long name, DOMString message);
 
-  /* Logging callbacks */
-  void onGetLoggingSuccess(sequence<DOMString> logs);
-  void onGetLoggingError(unsigned long name, DOMString message);
-
   /* Data channel callbacks */
   void notifyDataChannel(DataChannel channel);
   void notifyConnection();
   void notifyClosedConnection();
 
   /* Notification of one of several types of state changed */
   void onStateChange(PCObserverStateType state);
 
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -130,40 +130,20 @@ interface mozRTCPeerConnection : EventTa
   attribute EventHandler onaddstream;
   attribute EventHandler onremovestream;
   attribute EventHandler oniceconnectionstatechange;
 
   void getStats (MediaStreamTrack? selector,
                  RTCStatsCallback successCallback,
                  RTCPeerConnectionErrorCallback failureCallback);
 
-  [ChromeOnly]
-  void getStatsInternal (MediaStreamTrack? selector,
-                         RTCStatsCallback successCallback,
-                         RTCPeerConnectionErrorCallback failureCallback);
-
   // Data channel.
   RTCDataChannel createDataChannel (DOMString label,
                                     optional RTCDataChannelInit dataChannelDict);
   attribute EventHandler ondatachannel;
   attribute EventHandler onconnection;
   attribute EventHandler onclosedconnection;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onidentityresult;
   [Pref="media.peerconnection.identity.enabled"]
   attribute EventHandler onpeeridentity;
 };
 
-callback RTCLogCallback = void (sequence<DOMString> logMessages);
-
-[JSImplementation="@mozilla.org/dom/webrtcglobalinformation;1",
- ChromeOnly,
- Constructor ()]
-interface WebrtcGlobalInformation {
-    void getAllStats(RTCStatsCallback callback,
-                     RTCPeerConnectionErrorCallback errorCallback);
-    void getCandPairLogs(DOMString candPairId,
-                         RTCLogCallback callback,
-                         RTCPeerConnectionErrorCallback errorCallback);
-    void getLogs(DOMString pattern,
-                 RTCLogCallback callback,
-                 RTCPeerConnectionErrorCallback errorCallback);
-};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WebrtcGlobalInformation.webidl
@@ -0,0 +1,25 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+dictionary WebrtcGlobalStatisticsReport {
+  sequence<RTCStatsReportInternal> reports;
+};
+
+callback WebrtcGlobalStatisticsCallback = void (WebrtcGlobalStatisticsReport reports);
+callback WebrtcGlobalLoggingCallback = void (sequence<DOMString> logMessages);
+
+[ChromeOnly]
+interface WebrtcGlobalInformation {
+
+  [Throws]
+  static void getAllStats(WebrtcGlobalStatisticsCallback callback);
+
+  [Throws]
+  static void getLogging(DOMString pattern,
+                         WebrtcGlobalLoggingCallback callback);
+};
+
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -464,16 +464,17 @@ if CONFIG['MOZ_WEBGL']:
 if CONFIG['MOZ_WEBRTC']:
     WEBIDL_FILES += [
         'DataChannel.webidl',
         'MediaStreamList.webidl',
         'PeerConnectionImpl.webidl',
         'PeerConnectionImplEnums.webidl',
         'PeerConnectionObserver.webidl',
         'PeerConnectionObserverEnums.webidl',
+        'WebrtcGlobalInformation.webidl',
     ]
 
 if CONFIG['MOZ_WEBSPEECH']:
     WEBIDL_FILES += [
         'SpeechGrammar.webidl',
         'SpeechGrammarList.webidl',
         'SpeechRecognition.webidl',
         'SpeechRecognitionAlternative.webidl',
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -64,16 +64,17 @@ if CONFIG['MOZ_WEBRTC_SIGNALING']:
         'signaling/src/media/CSFVideoControlWrapper.cpp',
         'signaling/src/media/VcmSIPCCBinding.cpp',
         'signaling/src/mediapipeline/MediaPipeline.cpp',
         'signaling/src/mediapipeline/SrtpFlow.cpp',
         'signaling/src/peerconnection/MediaStreamList.cpp',
         'signaling/src/peerconnection/PeerConnectionCtx.cpp',
         'signaling/src/peerconnection/PeerConnectionImpl.cpp',
         'signaling/src/peerconnection/PeerConnectionMedia.cpp',
+        'signaling/src/peerconnection/WebrtcGlobalInformation.cpp',
         'signaling/src/sipcc/core/ccapp/call_logger.c',
         'signaling/src/sipcc/core/ccapp/capability_set.c',
         'signaling/src/sipcc/core/ccapp/cc_call_feature.c',
         'signaling/src/sipcc/core/ccapp/cc_config.c',
         'signaling/src/sipcc/core/ccapp/cc_device_feature.c',
         'signaling/src/sipcc/core/ccapp/cc_device_manager.c',
         'signaling/src/sipcc/core/ccapp/cc_info.c',
         'signaling/src/sipcc/core/ccapp/cc_service.c',
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -188,16 +188,20 @@
       #
       # Conditionals
       #
       'conditions': [
         ['build_for_test==0', {
           'defines' : [
             'MOZILLA_INTERNAL_API'
           ],
+          'sources': [
+            './src/peerconnection/WebrtcGlobalInformation.cpp',
+            './src/peerconnection/WebrtcGlobalInformation.h',
+          ],
         }],
         ['build_for_test!=0', {
           'include_dirs': [
             './test'
           ],
           'defines' : [
             'NO_CHROMIUM_LOGGING',
             'USE_FAKE_MEDIA_STREAMS',
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
@@ -20,16 +20,20 @@
 #include "cpr_stdlib.h"
 
 #include "StaticPtr.h"
 #include "PeerConnectionImpl.h"
 
 namespace mozilla {
 class PeerConnectionCtxShutdown;
 
+namespace dom {
+class WebrtcGlobalInformation;
+}
+
 // Unit-test helper, because cc_media_constraints_t is hard to forward-declare
 
 class MediaConstraintsExternal {
 public:
   MediaConstraintsExternal();
   MediaConstraintsExternal(const dom::MediaConstraintsInternal &aOther);
   cc_media_constraints_t* build() const;
 protected:
@@ -71,16 +75,17 @@ class PeerConnectionCtx : public CSF::CC
   // Create a SIPCC Call
   CSF::CC_CallPtr createCall();
 
   mozilla::dom::PCImplSipccState sipcc_state() { return mSipccState; }
 
   // Make these classes friend so that they can access mPeerconnections.
   friend class PeerConnectionImpl;
   friend class PeerConnectionWrapper;
+  friend class mozilla::dom::WebrtcGlobalInformation;
 
  private:
   // We could make these available only via accessors but it's too much trouble.
   std::map<const std::string, PeerConnectionImpl *> mPeerConnections;
 
   PeerConnectionCtx() :  mSipccState(mozilla::dom::PCImplSipccState::Idle),
                          mCCM(nullptr), mDevice(nullptr) {}
   // This is a singleton, so don't copy construct it, etc.
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -142,16 +142,26 @@ PRLogModuleInfo *signalingLogInfo() {
     logModuleInfo = PR_NewLogModule("signaling");
   }
   return logModuleInfo;
 }
 
 
 namespace sipcc {
 
+#ifdef MOZILLA_INTERNAL_API
+RTCStatsQuery::RTCStatsQuery(bool internal) : internalStats(internal) {
+}
+
+RTCStatsQuery::~RTCStatsQuery() {
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+#endif
+
 // Getting exceptions back down from PCObserver is generally not harmful.
 namespace {
 class JSErrorResult : public ErrorResult
 {
 public:
   ~JSErrorResult()
   {
 #ifdef MOZILLA_INTERNAL_API
@@ -1277,70 +1287,53 @@ public:
     mIceCandidateStats.Construct();
     mCodecStats.Construct();
   }
 };
 
 // Specialized helper - push map[key] if specified or all map values onto array
 
 static void
-PushBackSelect(std::vector<RefPtr<MediaPipeline>>& aDst,
+PushBackSelect(nsTArray<RefPtr<MediaPipeline>>& aDst,
                const std::map<TrackID, RefPtr<mozilla::MediaPipeline>> & aSrc,
                TrackID aKey = 0) {
   auto begin = aKey ? aSrc.find(aKey) : aSrc.begin(), it = begin;
   for (auto end = (aKey && begin != aSrc.end())? ++begin : aSrc.end();
        it != end; ++it) {
-    aDst.push_back(it->second);
+    aDst.AppendElement(it->second);
   }
 }
 #endif
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector, bool internalStats) {
+PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
   PC_AUTO_ENTER_API_CALL(true);
 
 #ifdef MOZILLA_INTERNAL_API
   if (!mMedia) {
     // Since we zero this out before the d'tor, we should check.
     return NS_ERROR_UNEXPECTED;
   }
 
-  nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery);
+  nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false));
 
-  nsresult rv = BuildStatsQuery_m(aSelector, internalStats, query.get());
+  nsresult rv = BuildStatsQuery_m(aSelector, query.get());
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   RUN_ON_THREAD(mSTSThread,
                 WrapRunnableNM(&PeerConnectionImpl::GetStatsForPCObserver_s,
                                mHandle,
                                query),
                 NS_DISPATCH_NORMAL);
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PeerConnectionImpl::GetLogging(const nsAString& aPattern) {
-  PC_AUTO_ENTER_API_CALL_NO_CHECK();
-
-#ifdef MOZILLA_INTERNAL_API
-  std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get());
-  RUN_ON_THREAD(mSTSThread,
-                WrapRunnableNM(&PeerConnectionImpl::GetLogging_s,
-                               mHandle,
-                               mThread,
-                               pattern),
-                NS_DISPATCH_NORMAL);
-
-#endif
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Add Ice Candidate");
 
   mInternal->mCall->addICECandidate(aCandidate, aMid, aLevel, tc);
@@ -1751,16 +1744,22 @@ PeerConnectionImpl::SetSignalingState_m(
   if (!pco) {
     return;
   }
   JSErrorResult rv;
   pco->OnStateChange(PCObserverStateType::SignalingState, rv);
   MOZ_ASSERT(!rv.Failed());
 }
 
+bool
+PeerConnectionImpl::IsClosed() const
+{
+  return !mMedia;
+}
+
 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
     : impl_(nullptr) {
   if (PeerConnectionCtx::GetInstance()->mPeerConnections.find(handle) ==
     PeerConnectionCtx::GetInstance()->mPeerConnections.end()) {
     return;
   }
 
   PeerConnectionImpl *impl = PeerConnectionCtx::GetInstance()->mPeerConnections[handle];
@@ -1928,68 +1927,85 @@ PeerConnectionImpl::IceGatheringStateCha
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 #ifdef MOZILLA_INTERNAL_API
 nsresult
 PeerConnectionImpl::BuildStatsQuery_m(
     mozilla::dom::MediaStreamTrack *aSelector,
-    bool internalStats,
     RTCStatsQuery *query) {
 
-  if (!mMedia || !mMedia->ice_ctx() || !mThread) {
-    MOZ_CRASH(); // Crash if debug build, return immediately if not
+  if (IsClosed()) {
+    return NS_OK;
+  }
+
+  if (!mMedia->ice_ctx() || !mThread) {
+    CSFLogError(logTag, "Could not build stats query, critical components of "
+                        "PeerConnectionImpl not set.");
     return NS_ERROR_UNEXPECTED;
   }
 
+  nsresult rv = GetTimeSinceEpoch(&(query->now));
+
+  if (NS_FAILED(rv)) {
+    CSFLogError(logTag, "Could not build stats query, could not get timestamp");
+    return rv;
+  }
+
   // We do not use the pcHandle here, since that's risky to expose to content.
   query->report = RTCStatsReportInternalConstruct(
       NS_ConvertASCIItoUTF16(mName.c_str()),
       query->now);
 
-  // Gather up pipelines from mMedia and dispatch them to STS for inspection
+  // Gather up pipelines from mMedia so they may be inspected on STS
   TrackID trackId = aSelector ? aSelector->GetTrackID() : 0;
 
   for (int i = 0, len = mMedia->LocalStreamsLength(); i < len; i++) {
     PushBackSelect(query->pipelines,
                    mMedia->GetLocalStream(i)->GetPipelines(),
                    trackId);
   }
+
   for (int i = 0, len = mMedia->RemoteStreamsLength(); i < len; i++) {
     PushBackSelect(query->pipelines,
                    mMedia->GetRemoteStream(i)->GetPipelines(),
                    trackId);
   }
 
   query->iceCtx = mMedia->ice_ctx();
 
   // From the list of MediaPipelines, determine the set of NrIceMediaStreams
   // we are interested in.
-  for (auto p = query->pipelines.begin(); p != query->pipelines.end(); ++p) {
-    size_t level = p->get()->level();
+  std::set<size_t> streamsGrabbed;
+  for (size_t p = 0; p < query->pipelines.Length(); ++p) {
+
+    size_t level = query->pipelines[p]->level();
+
+    // Don't grab the same stream twice, since that causes duplication
+    // of the ICE stats.
+    if (streamsGrabbed.count(level)) {
+      continue;
+    }
+
+    streamsGrabbed.insert(level);
     // TODO(bcampen@mozilla.com): I may need to revisit this for bundle.
     // (Bug 786234)
     RefPtr<NrIceMediaStream> temp(mMedia->ice_media_stream(level-1));
     if (temp.get()) {
-      query->streams.push_back(temp);
+      query->streams.AppendElement(temp);
     } else {
        CSFLogError(logTag, "Failed to get NrIceMediaStream for level %u "
                            "in %s:  %s",
                            uint32_t(level), __FUNCTION__, mHandle.c_str());
        MOZ_CRASH();
     }
   }
 
-  query->mainThread = mThread;
-
-  nsresult rv = GetTimeSinceEpoch(&(query->now));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return rv;
 }
 
 static void ToRTCIceCandidateStats(
     const std::vector<NrIceCandidate>& candidates,
     RTCStatsType candidateType,
     const nsString& componentId,
     DOMHighResTimeStamp now,
     RTCStatsReportInternal* report) {
@@ -2067,20 +2083,25 @@ static void RecordIceStats_s(
   }
 }
 
 nsresult
 PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
 
   ASSERT_ON_THREAD(query->iceCtx->thread());
 
+  // NrIceCtx must be destroyed on STS, so it is not safe to dispatch it back
+  // to main.
+  RefPtr<NrIceCtx> iceCtxTmp(query->iceCtx);
+  query->iceCtx = nullptr;
+
   // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
 
-  for (auto it = query->pipelines.begin(); it != query->pipelines.end(); ++it) {
-    const MediaPipeline& mp = **it;
+  for (size_t p = 0; p < query->pipelines.Length(); ++p) {
+    const MediaPipeline& mp = *query->pipelines[p];
     nsString idstr = (mp.Conduit()->type() == MediaSessionConduit::AUDIO) ?
         NS_LITERAL_STRING("audio_") : NS_LITERAL_STRING("video_");
     idstr.AppendInt(mp.trackid());
 
     switch (mp.direction()) {
       case MediaPipeline::TRANSMIT: {
         nsString localId = NS_LITERAL_STRING("outbound_rtp_") + idstr;
         nsString remoteId;
@@ -2187,44 +2208,43 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
         s.mBytesReceived.Construct(mp.rtp_bytes_received());
         query->report.mInboundRTPStreamStats.Value().AppendElement(s);
         break;
       }
     }
   }
 
   // Gather stats from ICE
-  for (auto s = query->streams.begin(); s != query->streams.end(); ++s) {
-    RecordIceStats_s(**s, query->internalStats, query->now, &(query->report));
+  for (size_t s = 0; s != query->streams.Length(); ++s) {
+    RecordIceStats_s(*query->streams[s],
+                     query->internalStats,
+                     query->now,
+                     &(query->report));
   }
 
   return NS_OK;
 }
 
 void PeerConnectionImpl::GetStatsForPCObserver_s(
     const std::string& pcHandle, // The Runnable holds the memory
     nsAutoPtr<RTCStatsQuery> query) {
 
   MOZ_ASSERT(query);
   MOZ_ASSERT(query->iceCtx);
-  MOZ_ASSERT(query->mainThread);
   ASSERT_ON_THREAD(query->iceCtx->thread());
 
   nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
 
-  // Some platforms will init the WrapRunnable (thus nulling out query) before
-  // query->mainThread is evaluated if placed in the RUN_ON_THREAD macro.
-  nsCOMPtr<nsIThread> mainThread(query->mainThread);
-  RUN_ON_THREAD(mainThread,
-                WrapRunnableNM(
-                    &PeerConnectionImpl::DeliverStatsReportToPCObserver_m,
-                    pcHandle,
-                    rv,
-                    query),
-                NS_DISPATCH_NORMAL);
+  NS_DispatchToMainThread(
+      WrapRunnableNM(
+          &PeerConnectionImpl::DeliverStatsReportToPCObserver_m,
+          pcHandle,
+          rv,
+          query),
+      NS_DISPATCH_NORMAL);
 }
 
 void PeerConnectionImpl::DeliverStatsReportToPCObserver_m(
     const std::string& pcHandle,
     nsresult result,
     nsAutoPtr<RTCStatsQuery> query) {
 
   // Is the PeerConnectionImpl still around?
@@ -2244,59 +2264,16 @@ void PeerConnectionImpl::DeliverStatsRep
 
       if (rv.Failed()) {
         CSFLogError(logTag, "Error firing stats observer callback");
       }
     }
   }
 }
 
-void PeerConnectionImpl::GetLogging_s(const std::string& pcHandle,
-                                      nsCOMPtr<nsIThread> callbackThread,
-                                      const std::string& pattern) {
-  RLogRingBuffer* logs = RLogRingBuffer::GetInstance();
-  nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>);
-  logs->Filter(pattern, 0, result);
-  RUN_ON_THREAD(callbackThread,
-                WrapRunnableNM(&PeerConnectionImpl::OnGetLogging_m,
-                               pcHandle,
-                               pattern,
-                               result),
-                NS_DISPATCH_NORMAL);
-}
-
-void PeerConnectionImpl::OnGetLogging_m(
-    const std::string& pcHandle,
-    const std::string& pattern,
-    nsAutoPtr<std::deque<std::string>> logging) {
-
-  // Is the PeerConnectionImpl still around?
-  PeerConnectionWrapper pcw(pcHandle);
-  if (pcw.impl()) {
-    nsRefPtr<PeerConnectionObserver> pco =
-        do_QueryObjectReferent(pcw.impl()->mPCObserver);
-    if (pco) {
-      JSErrorResult rv;
-      if (!logging->empty()) {
-        Sequence<nsString> nsLogs;
-        for (auto l = logging->begin(); l != logging->end(); ++l) {
-          nsLogs.AppendElement(ObString(l->c_str()));
-        }
-        pco->OnGetLoggingSuccess(nsLogs, rv);
-      } else {
-        pco->OnGetLoggingError(kInternalError,
-            ObString(("No logging matching pattern " + pattern).c_str()), rv);
-      }
-
-      if (rv.Failed()) {
-        CSFLogError(logTag, "Error firing stats observer callback");
-      }
-    }
-  }
-}
 #endif
 
 void
 PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
   MOZ_ASSERT(aStream);
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -155,24 +155,30 @@ private:
   std::vector<NrIceStunServer> mStunServers;
   std::vector<NrIceTurnServer> mTurnServers;
 };
 
 #ifdef MOZILLA_INTERNAL_API
 // Not an inner class so we can forward declare.
 class RTCStatsQuery {
   public:
+    explicit RTCStatsQuery(bool internalStats);
+    ~RTCStatsQuery();
+
+    mozilla::dom::RTCStatsReportInternal report;
+    std::string error;
+
+  private:
+    friend class PeerConnectionImpl;
     std::string pcName;
     bool internalStats;
-    std::vector<mozilla::RefPtr<mozilla::MediaPipeline>> pipelines;
+    nsTArray<mozilla::RefPtr<mozilla::MediaPipeline>> pipelines;
     mozilla::RefPtr<NrIceCtx> iceCtx;
-    std::vector<mozilla::RefPtr<NrIceMediaStream>> streams;
+    nsTArray<mozilla::RefPtr<NrIceMediaStream>> streams;
     DOMHighResTimeStamp now;
-    mozilla::dom::RTCStatsReportInternal report;
-    nsCOMPtr<nsIThread> mainThread;
 };
 #endif // MOZILLA_INTERNAL_API
 
 // Enter an API call and check that the state is OK,
 // the PC isn't closed, etc.
 #define PC_AUTO_ENTER_API_CALL(assert_ice_ready) \
     do { \
       /* do/while prevents res from conflicting with locals */    \
@@ -325,26 +331,19 @@ public:
   NS_IMETHODIMP SetRemoteDescription (int32_t aAction, const char* aSDP);
 
   void SetRemoteDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
   {
     rv = SetRemoteDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
   }
 
   NS_IMETHODIMP_TO_ERRORRESULT(GetStats, ErrorResult &rv,
-                               mozilla::dom::MediaStreamTrack *aSelector,
-                               bool internalStats)
+                               mozilla::dom::MediaStreamTrack *aSelector)
   {
-    rv = GetStats(aSelector, internalStats);
-  }
-
-  NS_IMETHODIMP_TO_ERRORRESULT(GetLogging, ErrorResult &rv,
-                               const nsAString& pattern)
-  {
-    rv = GetLogging(pattern);
+    rv = GetStats(aSelector);
   }
 
   NS_IMETHODIMP AddIceCandidate(const char* aCandidate, const char* aMid,
                                 unsigned short aLevel);
 
   void AddIceCandidate(const nsAString& aCandidate, const nsAString& aMid,
                        unsigned short aLevel, ErrorResult &rv)
   {
@@ -502,25 +501,24 @@ public:
   void ClearSdpParseErrorMessages();
 
   // Called to retreive the list of parsing errors.
   const std::vector<std::string> &GetSdpParseErrors();
 
   // Sets the RTC Signaling State
   void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState);
 
+  bool IsClosed() const;
+
 #ifdef MOZILLA_INTERNAL_API
   // initialize telemetry for when calls start
   void startCallTelem();
 
-  // This is an intermediate form, to make this refactoring easier to review.
-  // Please forgive the use of & for out-params.
   nsresult BuildStatsQuery_m(
       mozilla::dom::MediaStreamTrack *aSelector,
-      bool internalStats,
       RTCStatsQuery *query);
 
   static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query);
 #endif
 
 private:
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
@@ -574,26 +572,16 @@ private:
       const std::string& pcHandle,
       nsAutoPtr<RTCStatsQuery> query);
 
   // Sends an RTCStatsReport to JS. Must run on main thread.
   static void DeliverStatsReportToPCObserver_m(
       const std::string& pcHandle,
       nsresult result,
       nsAutoPtr<RTCStatsQuery> query);
-
-  // Fetches logs matching pattern from RLogRingBuffer. Must be run on STS.
-  static void GetLogging_s(const std::string& pcHandle,
-                           nsCOMPtr<nsIThread> callbackThread,
-                           const std::string& pattern);
-
-  // Sends logging to JS. Must run on main thread.
-  static void OnGetLogging_m(const std::string& pcHandle,
-                             const std::string& pattern,
-                             nsAutoPtr<std::deque<std::string>> logging);
 #endif
 
   // Timecard used to measure processing time. This should be the first class
   // attribute so that we accurately measure the time required to instantiate
   // any other attributes of this class.
   Timecard *mTimeCard;
 
   // The call
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -0,0 +1,216 @@
+/* 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/. */
+
+#include "WebrtcGlobalInformation.h"
+
+#include <deque>
+#include <string>
+
+#include "CSFLog.h"
+
+#include "mozilla/dom/WebrtcGlobalInformationBinding.h"
+
+#include "nsAutoPtr.h"
+#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
+#include "nsServiceManagerUtils.h" // do_GetService
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Vector.h"
+#include "nsProxyRelease.h"
+
+#include "rlogringbuffer.h"
+#include "runnable_utils.h"
+#include "PeerConnectionCtx.h"
+#include "PeerConnectionImpl.h"
+
+using sipcc::PeerConnectionImpl;
+using sipcc::PeerConnectionCtx;
+using sipcc::RTCStatsQuery;
+
+static const char* logTag = "WebrtcGlobalInformation";
+
+namespace mozilla {
+namespace dom {
+
+typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
+
+static void OnStatsReport_m(
+  nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
+  nsAutoPtr<RTCStatsQueries> aQueryList)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aQueryList);
+
+  WebrtcGlobalStatisticsReport report;
+  report.mReports.Construct();
+  for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
+    MOZ_ASSERT(*q);
+    report.mReports.Value().AppendElement((*q)->report);
+  }
+
+  ErrorResult rv;
+  aStatsCallback.get()->Call(report, rv);
+
+  if (rv.Failed()) {
+    CSFLogError(logTag, "Error firing stats observer callback");
+  }
+}
+
+static void GetAllStats_s(
+  nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> aStatsCallback,
+  nsAutoPtr<RTCStatsQueries> aQueryList)
+{
+  MOZ_ASSERT(aQueryList);
+
+  for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
+    MOZ_ASSERT(*q);
+    PeerConnectionImpl::ExecuteStatsQuery_s(*q);
+  }
+
+  NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m,
+                                         aStatsCallback,
+                                         aQueryList),
+                          NS_DISPATCH_NORMAL);
+}
+
+static void OnGetLogging_m(
+  nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
+  const std::string& aPattern,
+  nsAutoPtr<std::deque<std::string>> aLogList)
+{
+  ErrorResult rv;
+  if (!aLogList->empty()) {
+    Sequence<nsString> nsLogs;
+    for (auto l = aLogList->begin(); l != aLogList->end(); ++l) {
+      nsLogs.AppendElement(NS_ConvertUTF8toUTF16(l->c_str()));
+    }
+    aLoggingCallback.get()->Call(nsLogs, rv);
+  }
+
+  if (rv.Failed()) {
+    CSFLogError(logTag, "Error firing logging observer callback");
+  }
+}
+
+static void GetLogging_s(
+  nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> aLoggingCallback,
+  const std::string& aPattern)
+{
+  RLogRingBuffer* logs = RLogRingBuffer::GetInstance();
+  nsAutoPtr<std::deque<std::string>> result(new std::deque<std::string>);
+  // Might not exist yet.
+  if (logs) {
+    logs->Filter(aPattern, 0, result);
+  }
+  NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m,
+                                         aLoggingCallback,
+                                         aPattern,
+                                         result),
+                          NS_DISPATCH_NORMAL);
+}
+
+
+void
+WebrtcGlobalInformation::GetAllStats(
+  const GlobalObject& aGlobal,
+  WebrtcGlobalStatisticsCallback& aStatsCallback,
+  ErrorResult& aRv)
+{
+  if (!NS_IsMainThread()) {
+    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
+    return;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIEventTarget> stsThread =
+    do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  if (!stsThread) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
+
+  // If there is no PeerConnectionCtx, go through the same motions, since
+  // the API consumer doesn't care why there are no PeerConnectionImpl.
+  if (PeerConnectionCtx::isActive()) {
+    PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
+    MOZ_ASSERT(ctx);
+    for (auto p = ctx->mPeerConnections.begin();
+         p != ctx->mPeerConnections.end();
+         ++p) {
+      MOZ_ASSERT(p->second);
+
+      if (!p->second->IsClosed()) {
+        queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)));
+        p->second->BuildStatsQuery_m(nullptr, // all tracks
+                                     queries->back());
+      }
+    }
+  }
+
+  // CallbackObject does not support threadsafe refcounting, and must be
+  // destroyed on main.
+  nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback> callbackHandle(
+    new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(&aStatsCallback));
+
+  rv = RUN_ON_THREAD(stsThread,
+                     WrapRunnableNM(&GetAllStats_s, callbackHandle, queries),
+                     NS_DISPATCH_NORMAL);
+
+  aRv = rv;
+}
+
+void
+WebrtcGlobalInformation::GetLogging(
+  const GlobalObject& aGlobal,
+  const nsAString& aPattern,
+  WebrtcGlobalLoggingCallback& aLoggingCallback,
+  ErrorResult& aRv)
+{
+  if (!NS_IsMainThread()) {
+    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
+    return;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsIEventTarget> stsThread =
+    do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  if (!stsThread) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  std::string pattern(NS_ConvertUTF16toUTF8(aPattern).get());
+
+  // CallbackObject does not support threadsafe refcounting, and must be
+  // destroyed on main.
+  nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> callbackHandle(
+    new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(&aLoggingCallback));
+
+  rv = RUN_ON_THREAD(stsThread,
+                     WrapRunnableNM(&GetLogging_s, callbackHandle, pattern),
+                     NS_DISPATCH_NORMAL);
+
+  if (NS_FAILED(rv)) {
+    aLoggingCallback.Release();
+  }
+
+  aRv = rv;
+}
+
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.h
@@ -0,0 +1,41 @@
+/* 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/. */
+
+#ifndef _WEBRTC_GLOBAL_INFORMATION_H_
+#define _WEBRTC_GLOBAL_INFORMATION_H_
+
+#include "nsString.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+class GlobalObject;
+class WebrtcGlobalStatisticsCallback;
+class WebrtcGlobalLoggingCallback;
+
+class WebrtcGlobalInformation
+{
+public:
+  static void GetAllStats(const GlobalObject& aGlobal,
+                          WebrtcGlobalStatisticsCallback& aStatsCallback,
+                          ErrorResult& aRv);
+
+  static void GetLogging(const GlobalObject& aGlobal,
+                         const nsAString& aPattern,
+                         WebrtcGlobalLoggingCallback& aLoggingCallback,
+                         ErrorResult& aRv);
+
+private:
+  WebrtcGlobalInformation() MOZ_DELETE;
+  WebrtcGlobalInformation(const WebrtcGlobalInformation& aOrig) MOZ_DELETE;
+  WebrtcGlobalInformation& operator=(
+    const WebrtcGlobalInformation& aRhs) MOZ_DELETE;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif  // _WEBRTC_GLOBAL_INFORMATION_H_
+
--- a/toolkit/content/aboutWebrtc.xhtml
+++ b/toolkit/content/aboutWebrtc.xhtml
@@ -10,72 +10,46 @@
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>Webrtc Internals</title>
   </head>
   <script>
 
 
-function getCandPairLogs(id) {
-  try {
-    var wg = new WebrtcGlobalInformation();
-    wg.getCandPairLogs(id, displayLogs, console.log);
-  } catch (e) {
-    console.log("Exception while creating WebrtcGlobalInformation" + e.toString());
-  }
-}
-
-function getAllLogs() {
-  try {
-    var wg = new WebrtcGlobalInformation();
-    wg.getLogs('', displayLogs, console.log);
-  } catch(e) {
-    console.log("Exception while creating WebrtcGlobalInformation" + e.toString());
-  }
-}
-
 function displayLogs(logs) {
   var logsDiv = document.getElementById('logs');
   while (logsDiv.lastChild) {
     logsDiv.removeChild(logsDiv.lastChild);
   }
   logsDiv.appendChild(document.createElement('h3'))
          .appendChild(document.createTextNode('Logging:'));
   logs.forEach(function(logLine){
     logsDiv.appendChild(document.createElement('div'))
            .appendChild(document.createTextNode(logLine));
   });
 }
 
-function buildCandPairTableRow(candPair, stats) {
+function buildCandPairTableRow(candPair, localCand, remoteCand) {
   var row = document.createElement('tr');
   row.onclick = function() {
-    getCandPairLogs(candPair.id);
+    WebrtcGlobalInformation.getLogging("CAND-PAIR(" + row.id, displayLogs);
   }
 
-  if (stats.has(candPair.localCandidateId)) {
-    // TODO: Might have collisions; we either need to be able to supply
-    // desired type to get(), or do the ugly business of putting the type
-    // in the id as well as the type field.
-    var localCand = stats.get(candPair.localCandidateId);
+  if (localCand) {
     row.appendChild(document.createElement('td'))
        .appendChild(document.createTextNode(localCand.ipAddress + ':' +
                                             localCand.portNumber + '/' +
                                             localCand.candidateType));
   } else {
     row.appendChild(document.createElement('td'))
        .appendChild(document.createTextNode(candPair.localCandidateId));
   }
 
-  if (stats.has(candPair.remoteCandidateId)) {
-    // TODO: Might have collisions; we either need to be able to supply
-    // desired type to get(), or do the ugly business of putting the type
-    // in the id as well as the type field.
-    var remoteCand = stats.get(candPair.remoteCandidateId);
+  if (remoteCand) {
     row.appendChild(document.createElement('td'))
        .appendChild(document.createTextNode(remoteCand.ipAddress + ':' +
                                             remoteCand.portNumber + '/' +
                                             remoteCand.candidateType));
   } else {
     row.appendChild(document.createElement('td'))
        .appendChild(document.createTextNode(candPair.remoteCandidateId));
   }
@@ -87,17 +61,17 @@ function buildCandPairTableRow(candPair,
 
   row.appendChild(document.createElement('td'))
      .appendChild(document.createTextNode(candPair.nominated ? '*' : ''));
   row.appendChild(document.createElement('td'))
      .appendChild(document.createTextNode(candPair.selected ? '*' : ''));
   return row;
 }
 
-function buildCandTableRow(cand, stats) {
+function buildCandTableRow(cand) {
   var row = document.createElement('tr');
 
   row.appendChild(document.createElement('td'))
      .appendChild(document.createTextNode(cand.ipAddress + ':' +
                                           cand.portNumber));
 
   row.appendChild(document.createElement('td'))
      .appendChild(document.createTextNode(cand.candidateType));
@@ -163,102 +137,171 @@ function dumpStat(stat, label) {
     if (stat.bytesSent !== undefined) {
       statsString += " (" + round00(stat.bytesSent/1024) + " Kb)";
     }
   }
   div.appendChild(document.createTextNode(statsString));
   return div;
 }
 
-function buildPcDiv(stats, pcDivId) {
+function buildPcDiv(stats, pcDivHeading) {
   var newPcDiv = document.createElement('div');
 
   var heading = document.createElement('h3');
-  heading.appendChild(document.createTextNode(pcDivId));
+  heading.appendChild(document.createTextNode(pcDivHeading));
   newPcDiv.appendChild(heading);
 
-  var subDivs = {};
+  // First, ICE stats
+  var iceHeading = document.createElement('h4');
+  iceHeading.appendChild(document.createTextNode("ICE statistics"));
+  newPcDiv.appendChild(iceHeading);
+
+  var iceTablesByComponent = {};
 
-  stats.forEach(function(stat) {
-    if (!stat.componentId) {
-      if (!stat.isRemote) {
-        newPcDiv.appendChild(document.createElement('h4'))
-                .appendChild(document.createTextNode(stat.id));
-        if (stat.remoteId) {
-          newPcDiv.appendChild(dumpStat(stat, "Local: "));
-          newPcDiv.appendChild(dumpStat(stats.get(stat.remoteId), "Remote: "));
-        } else {
-          newPcDiv.appendChild(dumpStat(stat, ""));
-        }
-      }
-      return;
-    }
-
-    if (!subDivs[stat.componentId]) {
-      subDivs[stat.componentId] = {
-        candPairTable: buildEmptyCandPairTable(),
-        localCandTable: buildEmptyCandTable(true),
-        remoteCandTable: buildEmptyCandTable(false)
+  function getIceTables(componentId) {
+    if (!iceTablesByComponent[componentId]) {
+      iceTablesByComponent[componentId] = {
+        candidatePairTable: buildEmptyCandPairTable(),
+        localCandidateTable: buildEmptyCandTable(true),
+        remoteCandidateTable: buildEmptyCandTable(false)
       };
     }
+    return iceTablesByComponent[componentId];
+  }
 
-    var subDiv = subDivs[stat.componentId];
+  // Candidates
+  var candidateMap = {}; // Used later to speed up recording of candidate pairs
+
+  if (stats.iceCandidateStats) {
+    stats.iceCandidateStats.forEach(function(cand) {
+        var tables = getIceTables(cand.componentId);
+
+        candidateMap[cand.id] = cand;
 
-    if (stat.type == 'candidatepair') {
-      subDiv.candPairTable.appendChild(buildCandPairTableRow(stat, stats));
-    } else if (stat.type == 'localcandidate') {
-      subDiv.localCandTable.appendChild(buildCandTableRow(stat, stats));
-    } else if (stat.type == 'remotecandidate') {
-      subDiv.remoteCandTable.appendChild(buildCandTableRow(stat, stats));
-    }
-  });
+        if (cand.type == "localcandidate") {
+          tables.localCandidateTable.appendChild(buildCandTableRow(cand));
+        } else {
+          tables.remoteCandidateTable.appendChild(buildCandTableRow(cand));
+        }
+    });
+  }
 
-  for (var cid in subDivs) {
-    if (subDivs.hasOwnProperty(cid)) {
-      var subDiv = subDivs[cid];
+  // Candidate pairs
+  if (stats.iceCandidatePairStats) {
+    stats.iceCandidatePairStats.forEach(function(candPair) {
+      var candPairTable =
+        getIceTables(candPair.componentId).candidatePairTable;
+      candPairTable.appendChild(
+          buildCandPairTableRow(candPair,
+                                candidateMap[candPair.localCandidateId],
+                                candidateMap[candPair.remoteCandidateId]));
+    });
+  }
+
+  // Now that tables are completely built, put them on the page.
+  for (var cid in iceTablesByComponent) {
+    if (iceTablesByComponent.hasOwnProperty(cid)) {
+      var tables = iceTablesByComponent[cid];
       newPcDiv.appendChild(document.createElement('h4'))
               .appendChild(document.createTextNode(cid));
-      newPcDiv.appendChild(subDiv.candPairTable);
-      newPcDiv.appendChild(subDiv.localCandTable);
-      newPcDiv.appendChild(subDiv.remoteCandTable);
+      newPcDiv.appendChild(tables.candidatePairTable);
+      newPcDiv.appendChild(tables.localCandidateTable);
+      newPcDiv.appendChild(tables.remoteCandidateTable);
+    }
+  }
+
+  // end of ICE stats
+
+  // Now, RTP stats
+  var rtpHeading = document.createElement('h4');
+  rtpHeading.appendChild(document.createTextNode("RTP statistics"));
+  newPcDiv.appendChild(rtpHeading);
+
+  // Build map from id -> remote RTP stats (ie; stats obtained from RTCP
+  // from the other end). This allows us to pair up local/remote stats for
+  // the same stream more easily.
+  var remoteRtpStatsMap = {};
+
+  var addRemoteStatToMap = function (rtpStat) {
+    if (rtpStat.isRemote) {
+      remoteRtpStatsMap[rtpStat.id] = rtpStat;
     }
   }
 
+  if (stats.inboundRTPStreamStats) {
+    stats.inboundRTPStreamStats.forEach(addRemoteStatToMap);
+  }
+
+  if (stats.outboundRTPStreamStats) {
+    stats.outboundRTPStreamStats.forEach(addRemoteStatToMap);
+  }
+
+  var addRtpStatPairToDocument = function (rtpStat) {
+    if (!rtpStat.isRemote) {
+      newPcDiv.appendChild(document.createElement('h5'))
+              .appendChild(document.createTextNode(rtpStat.id));
+      newPcDiv.appendChild(dumpStat(rtpStat, "Local: "));
+
+      // Might not be receiving RTCP, so we have no idea what the
+      // statistics look like from the perspective of the other end.
+      if (rtpStat.remoteId) {
+        var remoteRtpStat = remoteRtpStatsMap[rtpStat.remoteId];
+        newPcDiv.appendChild(dumpStat(remoteRtpStat, "Remote: "));
+      }
+    }
+  }
+
+  if (stats.outboundRTPStreamStats) {
+    stats.outboundRTPStreamStats.forEach(addRtpStatPairToDocument);
+  }
+
+  if (stats.inboundRTPStreamStats) {
+    stats.inboundRTPStreamStats.forEach(addRtpStatPairToDocument);
+  }
+
   return newPcDiv;
 }
 
-function displayStats(obj) {
+function displayStats(globalReport) {
   console.log("Got stats callback.");
-  var pcid = obj.mozPcid;
-  var pcDivId = 'PeerConnection:' + pcid;
+  globalReport.reports.forEach(function (report) {
+    var pcDivHeading = 'PeerConnection:' + report.pcid;
 
-  var pcDiv = document.getElementById(pcDivId);
-  var newPcDiv = buildPcDiv(obj, pcDivId);
-  newPcDiv.id = pcDivId;
+    var pcDiv = document.getElementById(pcDivHeading);
+    var newPcDiv = buildPcDiv(report, pcDivHeading);
+    newPcDiv.id = pcDivHeading;
 
-  if (!pcDiv) {
-    document.getElementById('stats').appendChild(newPcDiv);
-  } else {
-    document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
-  }
-}
+    if (!pcDiv) {
+      document.getElementById('stats').appendChild(newPcDiv);
+    } else {
+      document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
+    }
+  });
+
+  globalReport.errors.forEach(function (error) {
+    var pcDivHeading = 'PeerConnection:' + error.pcid;
 
-function refreshStats() {
-  try {
-    var wg = new WebrtcGlobalInformation();
-    wg.getAllStats(displayStats, console.log);
-  } catch(e) {
-    console.log("Exception while creating WebrtcGlobalInformation:" + e.toString());
-  }
+    var pcDiv = document.getElementById(pcDivHeading);
+    var newPcDiv = buildPcDiv(error, pcDivHeading);
+    newPcDiv.id = pcDivHeading;
+
+    if (pcDiv) {
+      document.getElementById('stats').replaceChild(newPcDiv, pcDiv);
+    } else {
+      document.getElementById('stats').appendChild(newPcDiv);
+    }
+  });
 }
 
   </script>
 
-  <body id="body" onload="refreshStats()">
+  <body id="body" onload="WebrtcGlobalInformation.getAllStats(displayStats)">
     <div id="stats">
     </div>
-    <button onclick="getAllLogs()">Show/refresh logging</button>
+    <button onclick="WebrtcGlobalInformation.getLogging('', displayLogs)">
+      Show/refresh logging
+    </button>
     <div id="logs">
     </div>
   </body>
 </html>
 <!-- vim: softtabstop=2:shiftwidth=2:expandtab
 -->