Bug 1494312 - Part 2: Make the internal PC stats API based on MozPromise. r=mjf,mayhemer
authorByron Campen [:bwc] <docfaraday@gmail.com>
Fri, 23 Nov 2018 16:46:52 +0000
changeset 507081 ca59ca222bd57c33a29a3ba204f663cb6c71d2d0
parent 507080 4185df9ce3e1e7825fba58ca6dd6b1fefe69f111
child 507082 f28667d0d2cbbb7e4f815433d5d24864188df2b2
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjf, mayhemer
bugs1494312
milestone65.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 1494312 - Part 2: Make the internal PC stats API based on MozPromise. r=mjf,mayhemer Differential Revision: https://phabricator.services.mozilla.com/D11776
media/mtransport/nricectx.cpp
media/webrtc/signaling/src/peerconnection/MediaTransportHandler.cpp
media/webrtc/signaling/src/peerconnection/MediaTransportHandler.h
media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
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
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsSocketTransportService2.h
netwerk/sctp/datachannel/DataChannel.cpp
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -786,16 +786,37 @@ NrIceStats NrIceCtx::Destroy() {
       Telemetry::Accumulate(
           Telemetry::WEBRTC_ICE_OFFERER_ABORT_TIME,
           time_delta.ToMilliseconds());
     } else {
       Telemetry::Accumulate(
           Telemetry::WEBRTC_ICE_ANSWERER_ABORT_TIME,
           time_delta.ToMilliseconds());
     }
+
+    unsigned char rate_limit_bit_pattern = 0;
+    if (!mozilla::nr_socket_short_term_violation_time().IsNull() &&
+        mozilla::nr_socket_short_term_violation_time() >= ice_start_time_) {
+      rate_limit_bit_pattern |= 1;
+    }
+    if (!mozilla::nr_socket_long_term_violation_time().IsNull() &&
+        mozilla::nr_socket_long_term_violation_time() >= ice_start_time_) {
+      rate_limit_bit_pattern |= 2;
+    }
+
+    if (connection_state_ == ICE_CTX_FAILED) {
+      Telemetry::Accumulate(
+          Telemetry::WEBRTC_STUN_RATE_LIMIT_EXCEEDED_BY_TYPE_GIVEN_FAILURE,
+          rate_limit_bit_pattern);
+    } else if (connection_state_ == ICE_CTX_CONNECTED ||
+               connection_state_ == ICE_CTX_COMPLETED) {
+      Telemetry::Accumulate(
+          Telemetry::WEBRTC_STUN_RATE_LIMIT_EXCEEDED_BY_TYPE_GIVEN_SUCCESS,
+          rate_limit_bit_pattern);
+    }
   }
 
   if (peer_) {
     nr_ice_peer_ctx_destroy(&peer_);
   }
   if (ctx_) {
     nr_ice_ctx_destroy(&ctx_);
   }
--- a/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.cpp
@@ -535,34 +535,25 @@ MediaTransportHandler::GetState(const st
 {
   RefPtr<TransportFlow> flow = GetTransportFlow(aTransportId, aRtcp);
   if (flow) {
     return flow->GetLayer(TransportLayerDtls::ID())->state();
   }
   return TransportLayer::TS_NONE;
 }
 
-void
-MediaTransportHandler::GetAllIceStats(DOMHighResTimeStamp aNow,
-                                      dom::RTCStatsReportInternal* aReport)
+RefPtr<RTCStatsQueryPromise>
+MediaTransportHandler::GetIceStats(UniquePtr<RTCStatsQuery>&& aQuery)
 {
   for (const auto& stream : mIceCtx->GetStreams()) {
-    GetIceStats(*stream, aNow, aReport);
+    if (aQuery->grabAllLevels || aQuery->transportId == stream->GetId()) {
+      GetIceStats(*stream, aQuery->now, aQuery->report);
+    }
   }
-}
-
-void
-MediaTransportHandler::GetIceStats(const std::string& aTransportId,
-                                   DOMHighResTimeStamp aNow,
-                                   dom::RTCStatsReportInternal* aReport)
-{
-  auto stream = mIceCtx->GetStream(aTransportId);
-  if (stream) {
-    GetIceStats(*stream, aNow, aReport);
-  }
+  return RTCStatsQueryPromise::CreateAndResolve(std::move(aQuery), __func__);
 }
 
 static void ToRTCIceCandidateStats(
     const std::vector<NrIceCandidate>& candidates,
     dom::RTCStatsType candidateType,
     const nsString& componentId,
     DOMHighResTimeStamp now,
     dom::RTCStatsReportInternal* report) {
--- a/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.h
+++ b/media/webrtc/signaling/src/peerconnection/MediaTransportHandler.h
@@ -8,28 +8,32 @@
 #include "mozilla/RefPtr.h"
 #include "nsISupportsImpl.h"
 #include "sigslot.h"
 #include "transportlayer.h" // Need the State enum
 #include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
 #include "nricectx.h" // Need some enums
 #include "nsDOMNavigationTiming.h" // DOMHighResTimeStamp
 
+// For RTCStatsQueryPromise typedef
+#include "signaling/src/peerconnection/PeerConnectionImpl.h"
+
 #include <map>
 #include <string>
 #include <set>
 #include <vector>
 
 namespace mozilla {
 class DtlsIdentity; // TODO(bug 1494311) Use IPC type
 class NrIceCtx;
 class NrIceMediaStream;
 class NrIceResolver;
 class SdpFingerprintAttributeList; // TODO(bug 1494311) Use IPC type
 class TransportFlow;
+class RTCStatsQuery;
 
 namespace dom {
 struct RTCConfiguration;
 struct RTCStatsReportInternal;
 }
 
 // Base-class, makes some testing easier
 class MediaTransportBase {
@@ -107,24 +111,18 @@ class MediaTransportHandler : public Med
     void SendPacket(const std::string& aTransportId,
                     MediaPacket& aPacket) override;
 
     // TODO(bug 1494312): Figure out how this fits with an async API. Maybe we
     // cache on the content process.
     TransportLayer::State GetState(const std::string& aTransportId,
                                    bool aRtcp) const override;
 
-    // TODO(bug 1494312): Stats stuff needs to be async.
-    void GetAllIceStats(DOMHighResTimeStamp now,
-                        dom::RTCStatsReportInternal* report);
-
-    // TODO(bug 1494312): Stats stuff needs to be async.
-    void GetIceStats(const std::string& aTransportId,
-                     DOMHighResTimeStamp now,
-                     dom::RTCStatsReportInternal* report);
+    RefPtr<RTCStatsQueryPromise> GetIceStats(
+        UniquePtr<RTCStatsQuery>&& aQuery);
 
     // TODO(bug 1494311) Use IPC type
     struct CandidateInfo {
       std::string mCandidate;
       std::string mDefaultHostRtp;
       uint16_t mDefaultPortRtp = 0;
       std::string mDefaultHostRtcp;
       uint16_t mDefaultPortRtcp = 0;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -179,18 +179,16 @@ void PeerConnectionCtx::Destroy() {
     gInstance->Cleanup();
     delete gInstance;
     gInstance = nullptr;
   }
 
   StopWebRtcLog();
 }
 
-typedef Vector<nsAutoPtr<RTCStatsQuery>> RTCStatsQueries;
-
 // Telemetry reporting every second after start of first call.
 // The threading model around the media pipelines is weird:
 // - The pipelines are containers,
 // - containers that are only safe on main thread, with members only safe on STS,
 // - hence the there and back again approach.
 
 static auto
 FindId(const Sequence<RTCInboundRTPStreamStats>& aArray,
@@ -198,167 +196,120 @@ FindId(const Sequence<RTCInboundRTPStrea
   for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
     if (aArray[i].mId.Value() == aId) {
       return i;
     }
   }
   return aArray.NoIndex;
 }
 
-static auto
-FindId(const nsTArray<nsAutoPtr<RTCStatsReportInternal>>& aArray,
-       const nsString &aId) -> decltype(aArray.Length()) {
-  for (decltype(aArray.Length()) i = 0; i < aArray.Length(); i++) {
-    if (aArray[i]->mPcid == aId) {
-      return i;
-    }
-  }
-  return aArray.NoIndex;
-}
-
-static void
-FreeOnMain_m(nsAutoPtr<RTCStatsQueries> aQueryList) {
-  MOZ_ASSERT(NS_IsMainThread());
-}
-
-static void
-EverySecondTelemetryCallback_s(nsAutoPtr<RTCStatsQueries> aQueryList) {
+void
+PeerConnectionCtx::DeliverStats(RTCStatsQuery& aQuery)
+{
   using namespace Telemetry;
 
-  if(!PeerConnectionCtx::isActive()) {
-    return;
+  std::unique_ptr<dom::RTCStatsReportInternal> report(aQuery.report.forget());
+  // First, get reports from a second ago, if any, for calculations below
+  std::unique_ptr<dom::RTCStatsReportInternal> lastReport;
+  {
+    auto i = mLastReports.find(report->mPcid);
+    if (i != mLastReports.end()) {
+      lastReport = std::move(i->second);
+    }
   }
-  PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
 
-  for (auto & q : *aQueryList) {
-    PeerConnectionImpl::ExecuteStatsQuery_s(q);
-    auto& r = *q->report;
-    if (r.mInboundRTPStreamStats.WasPassed()) {
-      // First, get reports from a second ago, if any, for calculations below
-      const Sequence<RTCInboundRTPStreamStats> *lastInboundStats = nullptr;
-      {
-        auto i = FindId(ctx->mLastReports, r.mPcid);
-        if (i != ctx->mLastReports.NoIndex) {
-          lastInboundStats = &ctx->mLastReports[i]->mInboundRTPStreamStats.Value();
-        }
-      }
-      // Then, look for the things we want telemetry on
-      auto& array = r.mInboundRTPStreamStats.Value();
-      for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
-        auto& s = array[i];
-        bool isAudio = (s.mId.Value().Find("audio") != -1);
-        if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed() &&
-            (s.mPacketsLost.Value() + s.mPacketsReceived.Value()) != 0) {
-          HistogramID id;
-          if (s.mIsRemote) {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_PACKETLOSS_RATE :
-                           WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE;
-          } else {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
-                           WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
-          }
-          // *1000 so we can read in 10's of a percent (permille)
-          Accumulate(id,
-                     (s.mPacketsLost.Value() * 1000) /
-                     (s.mPacketsLost.Value() + s.mPacketsReceived.Value()));
+  if (report->mInboundRTPStreamStats.WasPassed()) {
+    // Then, look for the things we want telemetry on
+    for (auto& s : report->mInboundRTPStreamStats.Value()) {
+      bool isAudio = (s.mId.Value().Find("audio") != -1);
+      if (s.mPacketsLost.WasPassed() && s.mPacketsReceived.WasPassed() &&
+          (s.mPacketsLost.Value() + s.mPacketsReceived.Value()) != 0) {
+        HistogramID id;
+        if (s.mIsRemote) {
+          id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_PACKETLOSS_RATE :
+                         WEBRTC_VIDEO_QUALITY_OUTBOUND_PACKETLOSS_RATE;
+        } else {
+          id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_PACKETLOSS_RATE :
+                         WEBRTC_VIDEO_QUALITY_INBOUND_PACKETLOSS_RATE;
         }
-        if (s.mJitter.WasPassed()) {
-          HistogramID id;
-          if (s.mIsRemote) {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
-                           WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER;
-          } else {
-            id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
-                           WEBRTC_VIDEO_QUALITY_INBOUND_JITTER;
-          }
-          Accumulate(id, s.mJitter.Value());
-        }
-        if (s.mRoundTripTime.WasPassed()) {
-          MOZ_ASSERT(s.mIsRemote);
-          HistogramID id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
-                                     WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
-          Accumulate(id, s.mRoundTripTime.Value());
+        // *1000 so we can read in 10's of a percent (permille)
+        Accumulate(id,
+                   (s.mPacketsLost.Value() * 1000) /
+                   (s.mPacketsLost.Value() + s.mPacketsReceived.Value()));
+      }
+      if (s.mJitter.WasPassed()) {
+        HistogramID id;
+        if (s.mIsRemote) {
+          id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
+                         WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER;
+        } else {
+          id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
+                         WEBRTC_VIDEO_QUALITY_INBOUND_JITTER;
         }
-        if (lastInboundStats && s.mBytesReceived.WasPassed()) {
-          auto& laststats = *lastInboundStats;
-          auto i = FindId(laststats, s.mId.Value());
-          if (i != laststats.NoIndex) {
-            auto& lasts = laststats[i];
-            if (lasts.mBytesReceived.WasPassed()) {
-              auto delta_ms = int32_t(s.mTimestamp.Value() -
-                                      lasts.mTimestamp.Value());
-              // In theory we're called every second, so delta *should* be in that range.
-              // Small deltas could cause errors due to division
-              if (delta_ms > 500 && delta_ms < 60000) {
-                HistogramID id;
-                if (s.mIsRemote) {
-                  id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
-                                 WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
-                } else {
-                  id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
-                                 WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
-                }
-                Accumulate(id, ((s.mBytesReceived.Value() -
-                                 lasts.mBytesReceived.Value()) * 8) / delta_ms);
+        Accumulate(id, s.mJitter.Value());
+      }
+      if (s.mRoundTripTime.WasPassed()) {
+        MOZ_ASSERT(s.mIsRemote);
+        HistogramID id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
+                                   WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
+        Accumulate(id, s.mRoundTripTime.Value());
+      }
+      if (lastReport && lastReport->mInboundRTPStreamStats.WasPassed() &&
+          s.mBytesReceived.WasPassed()) {
+        auto& laststats = lastReport->mInboundRTPStreamStats.Value();
+        auto i = FindId(laststats, s.mId.Value());
+        if (i != laststats.NoIndex) {
+          auto& lasts = laststats[i];
+          if (lasts.mBytesReceived.WasPassed()) {
+            auto delta_ms = int32_t(s.mTimestamp.Value() -
+                                    lasts.mTimestamp.Value());
+            // In theory we're called every second, so delta *should* be in that range.
+            // Small deltas could cause errors due to division
+            if (delta_ms > 500 && delta_ms < 60000) {
+              HistogramID id;
+              if (s.mIsRemote) {
+                id = isAudio ? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
+                               WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
+              } else {
+                id = isAudio ? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
+                               WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
               }
-              // We could accumulate values until enough time has passed
-              // and then Accumulate() but this isn't that important.
+              Accumulate(id, ((s.mBytesReceived.Value() -
+                               lasts.mBytesReceived.Value()) * 8) / delta_ms);
             }
+            // We could accumulate values until enough time has passed
+            // and then Accumulate() but this isn't that important.
           }
         }
       }
     }
   }
-  // Steal and hang on to reports for the next second
-  ctx->mLastReports.Clear();
-  for (auto & q : *aQueryList) {
-    ctx->mLastReports.AppendElement(q->report.forget()); // steal avoids copy
-  }
-  // Container must be freed back on main thread
-  NS_DispatchToMainThread(WrapRunnableNM(&FreeOnMain_m, aQueryList),
-                          NS_DISPATCH_NORMAL);
+
+  mLastReports[report->mPcid] = std::move(report);
 }
 
 void
 PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, void *closure) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(PeerConnectionCtx::isActive());
-  auto ctx = static_cast<PeerConnectionCtx*>(closure);
-  if (ctx->mPeerConnections.empty()) {
-    return;
-  }
-  nsresult rv;
-  nsCOMPtr<nsIEventTarget> stsThread =
-      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-  MOZ_ASSERT(stsThread);
 
-  nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
-  for (auto p = ctx->mPeerConnections.begin();
-        p != ctx->mPeerConnections.end(); ++p) {
-    if (p->second->HasMedia()) {
-      if (!queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)))) {
-        return;
-      }
-      if (NS_WARN_IF(NS_FAILED(p->second->BuildStatsQuery_m(nullptr, // all tracks
-                                                            queries->back())))) {
-        queries->popBack();
-      } else {
-        MOZ_ASSERT(queries->back()->report);
-      }
+  for (auto& idAndPc : GetInstance()->mPeerConnections) {
+    if (idAndPc.second->HasMedia()) {
+      idAndPc.second->GetStats(nullptr, true)->Then(
+          GetMainThreadSerialEventTarget(),
+          __func__,
+          [=] (UniquePtr<RTCStatsQuery>&& aQuery) {
+            if(PeerConnectionCtx::isActive()) {
+              PeerConnectionCtx::GetInstance()->DeliverStats(*aQuery);
+            }
+          },
+          [=] (nsresult aError) {}
+        );
     }
   }
-  if (!queries->empty()) {
-    rv = RUN_ON_THREAD(stsThread,
-                       WrapRunnableNM(&EverySecondTelemetryCallback_s, queries),
-                       NS_DISPATCH_NORMAL);
-    NS_ENSURE_SUCCESS_VOID(rv);
-  }
 }
 
 void
 PeerConnectionCtx::UpdateNetworkState(bool online) {
   auto ctx = GetInstance();
   if (ctx->mPeerConnections.empty()) {
     return;
   }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
@@ -1,16 +1,17 @@
 /* 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 peerconnectionctx_h___h__
 #define peerconnectionctx_h___h__
 
 #include <string>
+#include <map>
 
 #include "WebrtcGlobalChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 #include "PeerConnectionImpl.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "nsIRunnable.h"
@@ -74,22 +75,21 @@ class PeerConnectionCtx {
 
   void initGMP();
 
   static void
   EverySecondTelemetryCallback_m(nsITimer* timer, void *);
 
   nsCOMPtr<nsITimer> mTelemetryTimer;
 
-public:
-  // TODO(jib): If we ever enable move semantics on std::map...
-  //std::map<nsString,nsAutoPtr<mozilla::dom::RTCStatsReportInternal>> mLastReports;
-  nsTArray<nsAutoPtr<mozilla::dom::RTCStatsReportInternal>> mLastReports;
 private:
 
+  void DeliverStats(RTCStatsQuery& aQuery);
+
+  std::map<nsString,std::unique_ptr<mozilla::dom::RTCStatsReportInternal>> mLastReports;
   // We cannot form offers/answers properly until the Gecko Media Plugin stuff
   // has been initted, which is a complicated mess of thread dispatches,
   // including sync dispatches to main. So, we need to be able to queue up
   // offer creation (or SetRemote, when we're the answerer) until all of this is
   // ready to go, since blocking on this init is just begging for deadlock.
   nsCOMPtr<mozIGeckoMediaPluginService> mGMPService;
   bool mGMPReady;
   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedJSEPOperations;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -228,27 +228,24 @@ template<>
 struct nsISupportsWeakReference::COMTypeInfo<nsSupportsWeakReference, void> {
   static const nsIID kIID;
 };
 const nsIID nsISupportsWeakReference::COMTypeInfo<nsSupportsWeakReference, void>::kIID = NS_ISUPPORTSWEAKREFERENCE_IID;
 
 namespace mozilla {
 
 RTCStatsQuery::RTCStatsQuery(bool internal) :
-  failed(false),
   internalStats(internal),
   grabAllLevels(false),
   now(0.0) {
 }
 
 RTCStatsQuery::~RTCStatsQuery() {
-  MOZ_ASSERT(NS_IsMainThread());
 }
 
-
 NS_IMPL_ISUPPORTS0(PeerConnectionImpl)
 
 already_AddRefed<PeerConnectionImpl>
 PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv)
 {
   RefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal);
 
   CSFLogDebug(LOGTAG, "Created PeerConnection: %p", pc.get());
@@ -1662,32 +1659,29 @@ public:
     mRawRemoteCandidates.Construct();
   }
 };
 
 NS_IMETHODIMP
 PeerConnectionImpl::GetStats(MediaStreamTrack *aSelector) {
   PC_AUTO_ENTER_API_CALL(true);
 
-  if (!mMedia) {
-    // Since we zero this out before the d'tor, we should check.
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(false));
-
-  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);
+  GetStats(aSelector, false)->Then(
+      GetMainThreadSerialEventTarget(),
+      __func__,
+      [handle = mHandle] (UniquePtr<RTCStatsQuery>&& aQuery) {
+        DeliverStatsReportToPCObserver_m(
+            handle, NS_OK, nsAutoPtr<RTCStatsQuery>(aQuery.release()));
+      },
+      [handle = mHandle] (nsresult aError) {
+        DeliverStatsReportToPCObserver_m(
+            handle, aError, nsAutoPtr<RTCStatsQuery>());
+      }
+    );
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, const dom::Nullable<unsigned short>& aLevel) {
   PC_AUTO_ENTER_API_CALL(true);
 
   if (mForceIceTcp && std::string::npos != std::string(aCandidate).find(" UDP ")) {
@@ -2878,49 +2872,71 @@ PeerConnectionImpl::UpdateDefaultCandida
   CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
   mJsepSession->UpdateDefaultCandidate(defaultAddr,
                                        defaultPort,
                                        defaultRtcpAddr,
                                        defaultRtcpPort,
                                        transportId);
 }
 
+RefPtr<RTCStatsQueryPromise>
+PeerConnectionImpl::GetStats(dom::MediaStreamTrack* aSelector,
+                             bool aInternalStats)
+{
+  UniquePtr<RTCStatsQuery> query(new RTCStatsQuery(aInternalStats));
+  nsresult rv = BuildStatsQuery_m(aSelector, query.get());
+  if (NS_FAILED(rv)) {
+    return RTCStatsQueryPromise::CreateAndReject(rv, __func__);
+  }
+
+  nsTArray<RefPtr<MediaPipeline>> pipelines;
+  // Gather up pipelines from mMedia so they may be inspected on STS
+  mMedia->GetTransmitPipelinesMatching(aSelector, &pipelines);
+  mMedia->GetReceivePipelinesMatching(aSelector, &pipelines);
+  if (!pipelines.Length()) {
+    CSFLogError(LOGTAG,
+        "%s: Found no pipelines matching selector.",
+        __FUNCTION__);
+  }
+
+  return InvokeAsync(
+      mSTSThread,
+      __func__,
+      [transportHandler = mMedia->mTransportHandler,
+       pipelines,
+       aQuery = std::move(query)] () mutable
+      {
+        return PeerConnectionImpl::ExecuteStatsQuery_s(std::move(aQuery),
+                                                       pipelines,
+                                                       transportHandler);
+      }
+    );
+}
+
 nsresult
 PeerConnectionImpl::BuildStatsQuery_m(
     mozilla::dom::MediaStreamTrack *aSelector,
     RTCStatsQuery *query) {
 
   if (!HasMedia()) {
     return NS_ERROR_UNEXPECTED;
   }
 
-  if (!mThread) {
-    CSFLogError(LOGTAG, "Could not build stats query, no MainThread");
-    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;
   }
 
-  query->media = mMedia;
-  if (!query->media) {
-    CSFLogError(LOGTAG, "Could not build stats query, no ice_ctx");
-    return NS_ERROR_UNEXPECTED;
-  }
-
   // We do not use the pcHandle here, since that's risky to expose to content.
   query->report = new RTCStatsReportInternalConstruct(
       NS_ConvertASCIItoUTF16(mName.c_str()),
       query->now);
 
   query->iceStartTime = mIceStartTime;
-  query->failed = isFailed(mIceConnectionState);
   query->report->mIceRestarts.Construct(mIceRestartCount);
   query->report->mIceRollbacks.Construct(mIceRollbackCount);
 
   // Populate SDP on main
   if (query->internalStats) {
     if (mJsepSession) {
       // TODO we probably should report Current and Pending SDPs here
       // separately. Plus the raw SDP we got from JS (mLocalRequestedSDP).
@@ -2936,50 +2952,43 @@ PeerConnectionImpl::BuildStatsQuery_m(
       query->report->mOfferer.Construct(mJsepSession->IsOfferer());
       for (const auto& candidate : mRawTrickledCandidates) {
         query->report->mRawRemoteCandidates.Value().AppendElement(
             NS_ConvertASCIItoUTF16(candidate.c_str()), fallible);
       }
     }
   }
 
-  // Gather up pipelines from mMedia so they may be inspected on STS
-  mMedia->GetTransmitPipelinesMatching(aSelector, &query->pipelines);
-  mMedia->GetReceivePipelinesMatching(aSelector, &query->pipelines);
-  if (!query->pipelines.Length()) {
-    CSFLogError(LOGTAG,
-        "%s: Found no pipelines matching selector.",
-        __FUNCTION__);
-  }
   if (aSelector) {
     query->transportId = mMedia->GetTransportIdMatching(*aSelector);
   }
 
   if (!aSelector) {
     query->grabAllLevels = true;
   }
 
-  return rv;
+  return NS_OK;
 }
 
-nsresult
-PeerConnectionImpl::ExecuteStatsQuery_s(RTCStatsQuery *query) {
-
-  ASSERT_ON_THREAD(query->media->GetSTSThread());
-
+RefPtr<RTCStatsQueryPromise>
+PeerConnectionImpl::ExecuteStatsQuery_s(
+    UniquePtr<RTCStatsQuery>&& query,
+    const nsTArray<RefPtr<MediaPipeline>>& aPipelines,
+    const RefPtr<MediaTransportHandler>& aTransportHandler)
+{
   // Gather stats from pipelines provided (can't touch mMedia + stream on STS)
 
-  for (size_t p = 0; p < query->pipelines.Length(); ++p) {
-    MOZ_ASSERT(query->pipelines[p]);
-    MOZ_ASSERT(query->pipelines[p]->Conduit());
-    if (!query->pipelines[p] || !query->pipelines[p]->Conduit()) {
+  for (size_t p = 0; p < aPipelines.Length(); ++p) {
+    MOZ_ASSERT(aPipelines[p]);
+    MOZ_ASSERT(aPipelines[p]->Conduit());
+    if (!aPipelines[p] || !aPipelines[p]->Conduit()) {
       // continue if we don't have a valid conduit
       continue;
     }
-    const MediaPipeline& mp = *query->pipelines[p];
+    const MediaPipeline& mp = *aPipelines[p];
     bool isAudio = (mp.Conduit()->type() == MediaSessionConduit::AUDIO);
     nsString kind = isAudio ?
         NS_LITERAL_STRING("audio") : NS_LITERAL_STRING("video");
     nsString idstr = kind;
     idstr.AppendLiteral("_");
     idstr.AppendInt((uint32_t)p);
 
     // TODO(@@NG):ssrcs handle Conduits having multiple stats at the same level
@@ -3181,47 +3190,17 @@ PeerConnectionImpl::ExecuteStatsQuery_s(
         // Fill in Contributing Source statistics
         mp.GetContributingSourceStats(localId,
             query->report->mRtpContributingSourceStats.Value());
         break;
       }
     }
   }
 
-  if (query->media->mTransportHandler) {
-    if (query->grabAllLevels) {
-      query->media->mTransportHandler->GetAllIceStats(query->now,
-                                                      query->report);
-    } else {
-      query->media->mTransportHandler->GetIceStats(query->transportId,
-                                                   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->media);
-  ASSERT_ON_THREAD(query->media->GetSTSThread());
-
-  nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
-
-  NS_DispatchToMainThread(
-      WrapRunnableNM(
-          &PeerConnectionImpl::DeliverStatsReportToPCObserver_m,
-          pcHandle,
-          rv,
-          query),
-      NS_DISPATCH_NORMAL);
+  return aTransportHandler->GetIceStats(std::move(query));
 }
 
 void PeerConnectionImpl::DeliverStatsReportToPCObserver_m(
     const std::string& pcHandle,
     nsresult result,
     const nsAutoPtr<RTCStatsQuery>& query) {
 
   // Is the PeerConnectionImpl still around?
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -81,17 +81,17 @@ class PeerConnectionObserver;
 typedef NS_ConvertUTF8toUTF16 PCObserverString;
 #endif
 }
 }
 
 #if defined(__cplusplus) && __cplusplus >= 201103L
 typedef struct Timecard Timecard;
 #else
-#include "timecard.h"
+#include "signaling/src/common/time_profiling/timecard.h"
 #endif
 
 // To preserve blame, convert nsresult to ErrorResult with wrappers. These macros
 // help declare wrappers w/function being wrapped when there are no differences.
 
 #define NS_IMETHODIMP_TO_ERRORRESULT(func, rv, ...) \
 NS_IMETHODIMP func(__VA_ARGS__);                    \
 void func (__VA_ARGS__, rv)
@@ -124,36 +124,32 @@ class PCUuidGenerator : public mozilla::
  private:
   nsCOMPtr<nsIUUIDGenerator> mGenerator;
 };
 
 // Not an inner class so we can forward declare.
 class RTCStatsQuery {
   public:
     explicit RTCStatsQuery(bool internalStats);
+    RTCStatsQuery(RTCStatsQuery&& aOrig) = default;
     ~RTCStatsQuery();
 
     nsAutoPtr<mozilla::dom::RTCStatsReportInternal> report;
-    std::string error;
     // A timestamp to help with telemetry.
     mozilla::TimeStamp iceStartTime;
-    // Just for convenience, maybe integrate into the report later
-    bool failed;
 
-  private:
-    friend class PeerConnectionImpl;
-    std::string pcName;
     bool internalStats;
-    nsTArray<RefPtr<mozilla::MediaPipeline>> pipelines;
     std::string transportId;
-    RefPtr<PeerConnectionMedia> media;
     bool grabAllLevels;
     DOMHighResTimeStamp now;
 };
 
+typedef MozPromise<UniquePtr<RTCStatsQuery>, nsresult, true>
+  RTCStatsQueryPromise;
+
 // 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 */    \
       nsresult res = CheckApiState(assert_ice_ready);             \
       if (NS_FAILED(res)) return res; \
     } while(0)
@@ -539,21 +535,18 @@ public:
   // called when DTLS connects; we only need this once
   nsresult OnAlpnNegotiated(const std::string& aAlpn);
 
   bool HasMedia() const;
 
   // initialize telemetry for when calls start
   void startCallTelem();
 
-  nsresult BuildStatsQuery_m(
-      mozilla::dom::MediaStreamTrack *aSelector,
-      RTCStatsQuery *query);
-
-  static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query);
+  RefPtr<RTCStatsQueryPromise> GetStats(
+      dom::MediaStreamTrack* aSelector, bool aInternalStats);
 
   // for monitoring changes in track ownership
   // PeerConnectionMedia can't do it because it doesn't know about principals
   virtual void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
 
   void OnMediaError(const std::string& aError);
 
   bool ShouldDumpPacket(size_t level, dom::mozPacketDumpType type,
@@ -561,16 +554,24 @@ public:
 
   void DumpPacket_m(size_t level, dom::mozPacketDumpType type, bool sending,
                     UniquePtr<uint8_t[]>& packet, size_t size);
 
 private:
   virtual ~PeerConnectionImpl();
   PeerConnectionImpl(const PeerConnectionImpl&rhs);
   PeerConnectionImpl& operator=(PeerConnectionImpl);
+  nsresult BuildStatsQuery_m(
+      mozilla::dom::MediaStreamTrack *aSelector,
+      RTCStatsQuery *query);
+  static RefPtr<RTCStatsQueryPromise> ExecuteStatsQuery_s(
+    UniquePtr<RTCStatsQuery>&& query,
+    const nsTArray<RefPtr<MediaPipeline>>& aPipelines,
+    const RefPtr<MediaTransportHandler>& aTransportHandler);
+
   nsresult CalculateFingerprint(const std::string& algorithm,
                                 std::vector<uint8_t>* fingerprint) const;
   nsresult ConfigureJsepSessionCodecs();
 
   NS_IMETHODIMP EnsureDataConnection(uint16_t aLocalPort, uint16_t aNumstreams,
                                      uint32_t aMaxMessageSize, bool aMMSSet);
 
   nsresult CloseInt();
@@ -611,20 +612,16 @@ private:
       bool* client) const;
 
   nsresult AddRtpTransceiverToJsepSession(RefPtr<JsepTransceiver>& transceiver);
   already_AddRefed<TransceiverImpl> CreateTransceiverImpl(
       JsepTransceiver* aJsepTransceiver,
       dom::MediaStreamTrack* aSendTrack,
       ErrorResult& aRv);
 
-  static void GetStatsForPCObserver_s(
-      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,
       const nsAutoPtr<RTCStatsQuery>& query);
 
   // When ICE completes, we record a bunch of statistics that outlive the
   // PeerConnection. This is just telemetry right now, but this can also
@@ -676,17 +673,17 @@ private:
 
   // A handle to refer to this PC with
   std::string mHandle;
 
   // A name for this PC that we are willing to expose to content.
   std::string mName;
 
   // The target to run stuff on
-  nsCOMPtr<nsIEventTarget> mSTSThread;
+  nsCOMPtr<nsISerialEventTarget> mSTSThread;
 
   // DataConnection that's used to get all the DataChannels
   RefPtr<mozilla::DataChannelConnection> mDataConnection;
 
   bool mForceIceTcp;
   RefPtr<PeerConnectionMedia> mMedia;
 
   // The JSEP negotiation session.
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -234,32 +234,33 @@ static PeerConnectionCtx* GetPeerConnect
     return PeerConnectionCtx::GetInstance();
   }
   return nullptr;
 }
 
 static void
 OnStatsReport_m(WebrtcGlobalChild* aThisChild,
                 const int aRequestId,
-                nsAutoPtr<RTCStatsQueries> aQueryList)
+                nsTArray<UniquePtr<RTCStatsQuery>>&& aQueryList)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aQueryList);
 
   if (aThisChild) {
     Stats stats;
 
     // Copy stats generated for the currently active PeerConnections
-    for (auto&& query : *aQueryList) {
-      stats.AppendElement(*(query->report));
+    for (auto& query : aQueryList) {
+      if (query) {
+        stats.AppendElement(*query->report);
+      }
     }
     // Reports saved for closed/destroyed PeerConnections
     auto ctx = PeerConnectionCtx::GetInstance();
     if (ctx) {
-      for (auto&& pc : ctx->mStatsForClosedPeerConnections) {
+      for (auto& pc : ctx->mStatsForClosedPeerConnections) {
         stats.AppendElement(pc);
       }
     }
 
     Unused << aThisChild->SendGetStatsResult(aRequestId, stats);
     return;
   }
 
@@ -268,55 +269,35 @@ OnStatsReport_m(WebrtcGlobalChild* aThis
 
   StatsRequest* request = StatsRequest::Get(aRequestId);
 
   if (!request) {
     CSFLogError(LOGTAG, "Bad RequestId");
     return;
   }
 
-  for (auto&& query : *aQueryList) {
-    request->mResult.mReports.Value().AppendElement(*(query->report), fallible);
+  for (auto& query : aQueryList) {
+    if (query) {
+      request->mResult.mReports.Value().AppendElement(
+          *(query->report), fallible);
+    }
   }
 
   // Reports saved for closed/destroyed PeerConnections
   auto ctx = PeerConnectionCtx::GetInstance();
   if (ctx) {
     for (auto&& pc : ctx->mStatsForClosedPeerConnections) {
       request->mResult.mReports.Value().AppendElement(pc, fallible);
     }
   }
 
   request->Complete();
   StatsRequest::Delete(aRequestId);
 }
 
-static void
-GetAllStats_s(WebrtcGlobalChild* aThisChild,
-              const int aRequestId,
-              nsAutoPtr<RTCStatsQueries> aQueryList)
-{
-  MOZ_ASSERT(aQueryList);
-  // The call to PeerConnetionImpl must happen from a runnable
-  // dispatched on the STS thread.
-
-  // Get stats from active connections.
-  for (auto&& query : *aQueryList) {
-    PeerConnectionImpl::ExecuteStatsQuery_s(query);
-  }
-
-  // After the RTCStatsQueries have been filled in, control must return
-  // to the main thread before their eventual destruction.
-  NS_DispatchToMainThread(WrapRunnableNM(&OnStatsReport_m,
-                                         aThisChild,
-                                         aRequestId,
-                                         aQueryList),
-                          NS_DISPATCH_NORMAL);
-}
-
 static void OnGetLogging_m(WebrtcGlobalChild* aThisChild,
                            const int aRequestId,
                            nsAutoPtr<std::deque<std::string>> aLogList)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aThisChild) {
     // Add this log to the collection of logs and call into
@@ -371,75 +352,59 @@ static void GetLogging_s(WebrtcGlobalChi
   // Return to main thread to complete processing.
   NS_DispatchToMainThread(WrapRunnableNM(&OnGetLogging_m,
                                          aThisChild,
                                          aRequestId,
                                          result),
                           NS_DISPATCH_NORMAL);
 }
 
-static nsresult
-BuildStatsQueryList(
-  const std::map<const std::string, PeerConnectionImpl *>& aPeerConnections,
-  const nsAString& aPcIdFilter,
-  RTCStatsQueries* queries)
-{
-  nsresult rv;
-
-  for (auto&& pc : aPeerConnections) {
-    MOZ_ASSERT(pc.second);
-    if (aPcIdFilter.IsEmpty() ||
-        aPcIdFilter.EqualsASCII(pc.second->GetIdAsAscii().c_str())) {
-      if (pc.second->HasMedia()) {
-        if (!queries->append(nsAutoPtr<RTCStatsQuery>(new RTCStatsQuery(true)))) {
-	  return NS_ERROR_OUT_OF_MEMORY;
-	}
-        rv = pc.second->BuildStatsQuery_m(nullptr, queries->back()); // all tracks
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
-        MOZ_ASSERT(queries->back()->report);
-      }
-    }
-  }
-
-  return NS_OK;
-}
-
-static nsresult
+static void
 RunStatsQuery(
   const std::map<const std::string, PeerConnectionImpl *>& aPeerConnections,
   const nsAString& aPcIdFilter,
   WebrtcGlobalChild* aThisChild,
   const int aRequestId)
 {
-  nsAutoPtr<RTCStatsQueries> queries(new RTCStatsQueries);
-  nsresult rv = BuildStatsQueryList(aPeerConnections, aPcIdFilter, queries);
+  nsTArray<RefPtr<RTCStatsQueryPromise>> promises;
 
-  if (NS_FAILED(rv)) {
-    return rv;
+  for (auto& idAndPc : aPeerConnections) {
+    MOZ_ASSERT(idAndPc.second);
+    PeerConnectionImpl& pc = *idAndPc.second;
+    if (aPcIdFilter.IsEmpty() ||
+        aPcIdFilter.EqualsASCII(pc.GetIdAsAscii().c_str())) {
+      if (pc.HasMedia()) {
+        promises.AppendElement(
+          pc.GetStats(nullptr, true)->Then(
+            GetMainThreadSerialEventTarget(),
+            __func__,
+            [=] (UniquePtr<RTCStatsQuery>&& aQuery) {
+              return RTCStatsQueryPromise::CreateAndResolve(
+                  std::move(aQuery), __func__);
+            },
+            [=] (nsresult aError) {
+              // Ignore errors! Just resolve with a nullptr.
+              return RTCStatsQueryPromise::CreateAndResolve(
+                  UniquePtr<RTCStatsQuery>(), __func__);
+            }
+          )
+        );
+      }
+    }
   }
 
-  nsCOMPtr<nsIEventTarget> stsThread =
-    do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (!stsThread) {
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = RUN_ON_THREAD(stsThread,
-                     WrapRunnableNM(&GetAllStats_s,
-                                    aThisChild,
-                                    aRequestId,
-                                    queries),
-                     NS_DISPATCH_NORMAL);
-  return rv;
+  RTCStatsQueryPromise::All(GetMainThreadSerialEventTarget(), promises)->Then(
+      GetMainThreadSerialEventTarget(),
+      __func__,
+      [aThisChild, aRequestId] (
+        nsTArray<UniquePtr<RTCStatsQuery>>&& aQueries) {
+        OnStatsReport_m(aThisChild, aRequestId, std::move(aQueries));
+      },
+      [=] (nsresult) {MOZ_CRASH();}
+    );
 }
 
 void ClearClosedStats()
 {
   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
 
   if (ctx) {
     ctx->mStatsForClosedPeerConnections.Clear();
@@ -511,33 +476,27 @@ WebrtcGlobalInformation::GetAllStats(
       aRv = next->SendGetStatsRequest(request->mRequestId, request->mPcIdFilter) ?
                 NS_OK : NS_ERROR_FAILURE;
       return;
     }
   }
   // No content resident PeerConnectionCtx instances.
   // Check this process.
   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
-  nsresult rv;
 
   if (ctx) {
-    rv = RunStatsQuery(ctx->mGetPeerConnections(),
-                       filter, nullptr, request->mRequestId);
-
-    if (NS_FAILED(rv)) {
-      StatsRequest::Delete(request->mRequestId);
-    }
+    RunStatsQuery(ctx->mGetPeerConnections(),
+                  filter, nullptr, request->mRequestId);
   } else {
     // Just send back an empty report.
-    rv = NS_OK;
     request->Complete();
     StatsRequest::Delete(request->mRequestId);
   }
 
-  aRv = rv;
+  aRv = NS_OK;
 }
 
 static nsresult
 RunLogQuery(const nsCString& aPattern,
             WebrtcGlobalChild* aThisChild,
             const int aRequestId)
 {
   nsresult rv;
@@ -716,53 +675,49 @@ WebrtcGlobalInformation::GetAecDebugLogD
   aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(EmptyCString()));
 }
 
 mozilla::ipc::IPCResult
 WebrtcGlobalParent::RecvGetStatsResult(const int& aRequestId,
                                        nsTArray<RTCStatsReportInternal>&& Stats)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  nsresult rv = NS_OK;
 
   StatsRequest* request = StatsRequest::Get(aRequestId);
 
   if (!request) {
     CSFLogError(LOGTAG, "Bad RequestId");
     return IPC_FAIL_NO_REASON(this);
   }
 
-  for (auto&& s : Stats) {
+  for (auto& s : Stats) {
     request->mResult.mReports.Value().AppendElement(s, fallible);
   }
 
   auto next = request->GetNextParent();
   if (next) {
     // There are more content instances to query.
     if (!next->SendGetStatsRequest(request->mRequestId, request->mPcIdFilter)) {
       return IPC_FAIL_NO_REASON(this);
     }
     return IPC_OK();
   }
 
   // Content queries complete, run chrome instance query if applicable
   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
 
   if (ctx) {
-    rv = RunStatsQuery(ctx->mGetPeerConnections(),
-                       request->mPcIdFilter, nullptr, aRequestId);
+    RunStatsQuery(ctx->mGetPeerConnections(),
+                  request->mPcIdFilter, nullptr, aRequestId);
   } else {
     // No instance in the process, return the collections as is
     request->Complete();
     StatsRequest::Delete(aRequestId);
   }
 
-  if (NS_FAILED(rv)) {
-    return IPC_FAIL_NO_REASON(this);
-  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebrtcGlobalParent::RecvGetLogResult(const int& aRequestId,
                                      const WebrtcGlobalLog& aLog)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -839,21 +794,17 @@ WebrtcGlobalChild::RecvGetStatsRequest(c
 {
   if (mShutdown) {
     return IPC_OK();
   }
 
   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
 
   if (ctx) {
-    nsresult rv = RunStatsQuery(ctx->mGetPeerConnections(),
-                                aPcIdFilter, this, aRequestId);
-    if (NS_FAILED(rv)) {
-      return IPC_FAIL_NO_REASON(this);
-    }
+    RunStatsQuery(ctx->mGetPeerConnections(), aPcIdFilter, this, aRequestId);
     return IPC_OK();
   }
 
   nsTArray<RTCStatsReportInternal> empty_stats;
   SendGetStatsResult(aRequestId, empty_stats);
 
   return IPC_OK();
 }
@@ -995,22 +946,21 @@ static uint32_t GetCandidateIpAndTranspo
     res |= CANDIDATE_BITMASK_IPV6;
   }
 
   return res;
 };
 
 static void StoreLongTermICEStatisticsImpl_m(
     nsresult result,
-    nsAutoPtr<RTCStatsQuery> query) {
+    RTCStatsQuery* query) {
 
   using namespace Telemetry;
 
   if (NS_FAILED(result) ||
-      !query->error.empty() ||
       !query->report->mIceCandidateStats.WasPassed()) {
     return;
   }
 
   query->report->mClosed.Construct(true);
 
   // TODO(bcampen@mozilla.com): Do we need to watch out for cases where the
   // components within a stream didn't have the same types of relayed
@@ -1192,74 +1142,33 @@ static void StoreLongTermICEStatisticsIm
   // Finally, store the stats
 
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();
   if (ctx) {
     ctx->mStatsForClosedPeerConnections.AppendElement(*query->report, fallible);
   }
 }
 
-static void GetStatsForLongTermStorage_s(
-    nsAutoPtr<RTCStatsQuery> query) {
-
-  MOZ_ASSERT(query);
-
-  nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
-
-  // Check whether packets were dropped due to rate limiting during
-  // this call. (These calls must be made on STS)
-  unsigned char rate_limit_bit_pattern = 0;
-  if (!mozilla::nr_socket_short_term_violation_time().IsNull() &&
-      !query->iceStartTime.IsNull() &&
-      mozilla::nr_socket_short_term_violation_time() >= query->iceStartTime) {
-    rate_limit_bit_pattern |= 1;
-  }
-  if (!mozilla::nr_socket_long_term_violation_time().IsNull() &&
-      !query->iceStartTime.IsNull() &&
-      mozilla::nr_socket_long_term_violation_time() >= query->iceStartTime) {
-    rate_limit_bit_pattern |= 2;
-  }
-
-  if (query->failed) {
-    Telemetry::Accumulate(
-        Telemetry::WEBRTC_STUN_RATE_LIMIT_EXCEEDED_BY_TYPE_GIVEN_FAILURE,
-        rate_limit_bit_pattern);
-  } else {
-    Telemetry::Accumulate(
-        Telemetry::WEBRTC_STUN_RATE_LIMIT_EXCEEDED_BY_TYPE_GIVEN_SUCCESS,
-        rate_limit_bit_pattern);
-  }
-
-  // Even if Telemetry::Accumulate is threadsafe, we still need to send the
-  // query back to main, since that is where it must be destroyed.
-  NS_DispatchToMainThread(
-      WrapRunnableNM(
-          &StoreLongTermICEStatisticsImpl_m,
-          rv,
-          query),
-      NS_DISPATCH_NORMAL);
-}
-
 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
     PeerConnectionImpl& aPc) {
   Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE,
                         static_cast<uint32_t>(aPc.IceConnectionState()));
 
   if (aPc.IceConnectionState() == PCImplIceConnectionState::New) {
     // ICE has not started; we won't have any remote candidates, so recording
     // statistics on gathered candidates is pointless.
     return;
   }
 
-  nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(true));
-
-  nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get());
-
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  RUN_ON_THREAD(aPc.GetSTSThread(),
-                WrapRunnableNM(&GetStatsForLongTermStorage_s,
-                               query),
-                NS_DISPATCH_NORMAL);
+  aPc.GetStats(nullptr, true)->Then(
+      GetMainThreadSerialEventTarget(),
+      __func__,
+      [=] (UniquePtr<RTCStatsQuery>&& aQuery) {
+        StoreLongTermICEStatisticsImpl_m(NS_OK, aQuery.get());
+      },
+      [=] (nsresult aError) {
+        StoreLongTermICEStatisticsImpl_m(aError, nullptr);
+      }
+    );
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -599,16 +599,17 @@ nsSocketTransportService::Poll(TimeDurat
 
 //-----------------------------------------------------------------------------
 // xpcom api
 
 NS_IMPL_ISUPPORTS(nsSocketTransportService,
                   nsISocketTransportService,
                   nsIRoutedSocketTransportService,
                   nsIEventTarget,
+                  nsISerialEventTarget,
                   nsIThreadObserver,
                   nsIRunnable,
                   nsPISocketTransportService,
                   nsIObserver)
 
 static const char* gCallbackPrefs[] = {
     SEND_BUFFER_PREF,
     KEEPALIVE_ENABLED_PREF,
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -78,17 +78,17 @@ public:
   }
 private:
     nsCOMPtr<nsIRunnable> mEvent;
 };
 
 //-----------------------------------------------------------------------------
 
 class nsSocketTransportService final : public nsPISocketTransportService
-                                     , public nsIEventTarget
+                                     , public nsISerialEventTarget
                                      , public nsIThreadObserver
                                      , public nsIRunnable
                                      , public nsIObserver
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSPISOCKETTRANSPORTSERVICE
     NS_DECL_NSISOCKETTRANSPORTSERVICE
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -27,16 +27,18 @@
 #include "usrsctp.h"
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 #include "DataChannelLog.h"
 
+#define DATACHANNEL_LOG(args) LOG(args)
+
 #include "nsServiceManagerUtils.h"
 #include "nsIObserverService.h"
 #include "nsIObserver.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "mozilla/Services.h"
 #include "mozilla/Sprintf.h"
 #include "nsProxyRelease.h"
@@ -49,17 +51,16 @@
 #include "mozilla/StaticMutex.h"
 #include "mozilla/Unused.h"
 #ifdef MOZ_PEERCONNECTION
 #include "mtransport/runnable_utils.h"
 #include "signaling/src/peerconnection/MediaTransportHandler.h"
 #include "mediapacket.h"
 #endif
 
-#define DATACHANNEL_LOG(args) LOG(args)
 #include "DataChannel.h"
 #include "DataChannelProtocol.h"
 
 // Let us turn on and off important assertions in non-debug builds
 #ifdef DEBUG
 #define ASSERT_WEBRTC(x) MOZ_ASSERT((x))
 #elif defined(MOZ_WEBRTC_ASSERT_ALWAYS)
 #define ASSERT_WEBRTC(x) do { if (!(x)) { MOZ_CRASH(); } } while (0)