tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PeerConnectionImpl.cpp (168760B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "PeerConnectionImpl.h"
      6 
      7 #include <cerrno>
      8 #include <cstdlib>
      9 #include <deque>
     10 #include <set>
     11 #include <sstream>
     12 #include <vector>
     13 
     14 #include "IPeerConnection.h"
     15 #include "MediaTrackGraph.h"
     16 #include "PeerConnectionCtx.h"
     17 #include "RTCDataChannelDeclarations.h"
     18 #include "RemoteTrackSource.h"
     19 #include "base/histogram.h"
     20 #include "common/browser_logging/CSFLog.h"
     21 #include "common/time_profiling/timecard.h"
     22 #include "jsapi.h"
     23 #include "jsapi/RTCRtpReceiver.h"
     24 #include "jsapi/RTCRtpSender.h"
     25 #include "jsep/JsepSession.h"
     26 #include "jsep/JsepSessionImpl.h"
     27 #include "jsep/JsepTrack.h"
     28 #include "libwebrtcglue/AudioConduit.h"
     29 #include "libwebrtcglue/VideoConduit.h"
     30 #include "libwebrtcglue/WebrtcCallWrapper.h"
     31 #include "libwebrtcglue/WebrtcEnvironmentWrapper.h"
     32 #include "mozilla/IntegerPrintfMacros.h"
     33 #include "mozilla/Sprintf.h"
     34 #include "mozilla/StaticPrefs_media.h"
     35 #include "mozilla/glean/DomMediaWebrtcMetrics.h"
     36 #include "mozilla/media/MediaUtils.h"
     37 #include "nsEffectiveTLDService.h"
     38 #include "nsFmtString.h"
     39 #include "nsILoadContext.h"
     40 #include "nsIPrefBranch.h"
     41 #include "nsIPrefService.h"
     42 #include "nsNetCID.h"
     43 #include "nsProxyRelease.h"
     44 #include "nsServiceManagerUtils.h"
     45 #include "nsThreadUtils.h"
     46 #include "nspr.h"
     47 #include "nss.h"
     48 #include "pk11pub.h"
     49 #include "prtime.h"
     50 #include "sdp/SdpAttribute.h"
     51 #include "transport/dtlsidentity.h"
     52 #include "transport/runnable_utils.h"
     53 #include "transportbridge/MediaPipeline.h"
     54 #include "transportbridge/RtpLogger.h"
     55 
     56 #ifdef XP_WIN
     57 // We need to undef the MS macro for Document::CreateEvent
     58 #  ifdef CreateEvent
     59 #    undef CreateEvent
     60 #  endif
     61 #endif  // XP_WIN
     62 
     63 #include "AudioStreamTrack.h"
     64 #include "DOMMediaStream.h"
     65 #include "MediaManager.h"
     66 #include "MediaStreamTrack.h"
     67 #include "RTCDataChannel.h"
     68 #include "RTCDtlsTransport.h"
     69 #include "RTCSctpTransport.h"
     70 #include "VideoStreamTrack.h"
     71 #include "WebrtcGlobalInformation.h"
     72 #include "js/ArrayBuffer.h"    // JS::NewArrayBufferWithContents
     73 #include "js/GCAnnotations.h"  // JS_HAZ_ROOTED
     74 #include "js/RootingAPI.h"     // JS::{{,Mutable}Handle,Rooted}
     75 #include "jsep/JsepTransport.h"
     76 #include "mozilla/EventDispatcher.h"
     77 #include "mozilla/LoadInfo.h"
     78 #include "mozilla/NullPrincipal.h"
     79 #include "mozilla/PeerIdentity.h"
     80 #include "mozilla/Preferences.h"
     81 #include "mozilla/PublicSSL.h"
     82 #include "mozilla/TimeStamp.h"
     83 #include "mozilla/dom/BrowserChild.h"
     84 #include "mozilla/dom/Document.h"
     85 #include "mozilla/dom/Event.h"
     86 #include "mozilla/dom/Location.h"
     87 #include "mozilla/dom/PeerConnectionImplBinding.h"
     88 #include "mozilla/dom/PluginCrashedEvent.h"
     89 #include "mozilla/dom/Promise.h"
     90 #include "mozilla/dom/RTCCertificate.h"
     91 #include "mozilla/dom/RTCDataChannelBinding.h"
     92 #include "mozilla/dom/RTCDtlsTransportBinding.h"  // RTCDtlsTransportState
     93 #include "mozilla/dom/RTCIceTransportBinding.h"   // RTCIceTransportState
     94 #include "mozilla/dom/RTCPeerConnectionBinding.h"
     95 #include "mozilla/dom/RTCRtpReceiverBinding.h"
     96 #include "mozilla/dom/RTCRtpSenderBinding.h"
     97 #include "mozilla/dom/RTCSctpTransportBinding.h"  // RTCSctpTransportState
     98 #include "mozilla/dom/RTCStatsReportBinding.h"
     99 #include "mozilla/glean/DomMediaWebrtcMetrics.h"
    100 #include "mozilla/net/DataChannelProtocol.h"
    101 #include "mozilla/net/WebrtcProxyConfig.h"
    102 #include "nsContentUtils.h"
    103 #include "nsDOMJSUtils.h"
    104 #include "nsGlobalWindowInner.h"
    105 #include "nsILoadInfo.h"
    106 #include "nsIPrincipal.h"
    107 #include "nsIProxiedChannel.h"
    108 #include "nsIScriptGlobalObject.h"
    109 #include "nsNetUtil.h"
    110 #include "nsPrintfCString.h"
    111 #include "nsURLHelper.h"
    112 #include "nsXULAppAPI.h"
    113 #include "transport/nr_socket_proxy_config.h"
    114 
    115 #ifdef XP_WIN
    116 // We need to undef the MS macro again in case the windows include file
    117 // got imported after we included mozilla/dom/Document.h
    118 #  ifdef CreateEvent
    119 #    undef CreateEvent
    120 #  endif
    121 #endif  // XP_WIN
    122 
    123 #include "MediaSegment.h"
    124 
    125 #ifdef USE_FAKE_PCOBSERVER
    126 #  include "FakePCObserver.h"
    127 #else
    128 #  include "mozilla/dom/PeerConnectionObserverBinding.h"
    129 #endif
    130 #include "mozilla/dom/PeerConnectionObserverEnumsBinding.h"
    131 
    132 #define ICE_PARSING \
    133  "In RTCConfiguration passed to RTCPeerConnection constructor"
    134 
    135 using namespace mozilla;
    136 using namespace mozilla::dom;
    137 using glean::webrtc_signaling::AudioMsectionNegotiatedExtra;
    138 using glean::webrtc_signaling::SdpNegotiatedExtra;
    139 using glean::webrtc_signaling::VideoMsectionNegotiatedExtra;
    140 
    141 typedef PCObserverString ObString;
    142 
    143 static const char* pciLogTag = "PeerConnectionImpl";
    144 #ifdef LOGTAG
    145 #  undef LOGTAG
    146 #endif
    147 #define LOGTAG pciLogTag
    148 
    149 static mozilla::LazyLogModule logModuleInfo("signaling");
    150 
    151 // Getting exceptions back down from PCObserver is generally not harmful.
    152 namespace {
    153 // This is a terrible hack.  The problem is that SuppressException is not
    154 // inline, and we link this file without libxul in some cases (e.g. for our test
    155 // setup).  So we can't use ErrorResult or IgnoredErrorResult because those call
    156 // SuppressException...  And we can't use FastErrorResult because we can't
    157 // include BindingUtils.h, because our linking is completely broken. Use
    158 // BaseErrorResult directly.  Please do not let me see _anyone_ doing this
    159 // without really careful review from someone who knows what they are doing.
    160 class JSErrorResult : public binding_danger::TErrorResult<
    161                          binding_danger::JustAssertCleanupPolicy> {
    162 public:
    163  ~JSErrorResult() { SuppressException(); }
    164 } JS_HAZ_ROOTED;
    165 
    166 // The WrapRunnable() macros copy passed-in args and passes them to the function
    167 // later on the other thread. ErrorResult cannot be passed like this because it
    168 // disallows copy-semantics.
    169 //
    170 // This WrappableJSErrorResult hack solves this by not actually copying the
    171 // ErrorResult, but creating a new one instead, which works because we don't
    172 // care about the result.
    173 //
    174 // Since this is for JS-calls, these can only be dispatched to the main thread.
    175 
    176 class WrappableJSErrorResult {
    177 public:
    178  WrappableJSErrorResult() : mRv(MakeUnique<JSErrorResult>()), isCopy(false) {}
    179  WrappableJSErrorResult(const WrappableJSErrorResult& other)
    180      : mRv(MakeUnique<JSErrorResult>()), isCopy(true) {}
    181  ~WrappableJSErrorResult() {
    182    if (isCopy) {
    183      MOZ_ASSERT(NS_IsMainThread());
    184    }
    185  }
    186  operator ErrorResult&() { return *mRv; }
    187 
    188 private:
    189  mozilla::UniquePtr<JSErrorResult> mRv;
    190  bool isCopy;
    191 } JS_HAZ_ROOTED;
    192 
    193 }  // namespace
    194 
    195 static nsresult InitNSSInContent() {
    196  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
    197 
    198  if (!XRE_IsContentProcess()) {
    199    MOZ_ASSERT_UNREACHABLE("Must be called in content process");
    200    return NS_ERROR_FAILURE;
    201  }
    202 
    203  static bool nssStarted = false;
    204  if (nssStarted) {
    205    return NS_OK;
    206  }
    207 
    208  if (NSS_NoDB_Init(nullptr) != SECSuccess) {
    209    CSFLogError(LOGTAG, "NSS_NoDB_Init failed.");
    210    return NS_ERROR_FAILURE;
    211  }
    212 
    213  if (NS_FAILED(mozilla::psm::InitializeCipherSuite())) {
    214    CSFLogError(LOGTAG, "Fail to set up nss cipher suite.");
    215    return NS_ERROR_FAILURE;
    216  }
    217 
    218  mozilla::psm::DisableMD5();
    219 
    220  nssStarted = true;
    221 
    222  return NS_OK;
    223 }
    224 
    225 namespace mozilla {
    226 class DataChannel;
    227 }
    228 
    229 namespace mozilla {
    230 
    231 void PeerConnectionAutoTimer::RegisterConnection() { mRefCnt++; }
    232 
    233 void PeerConnectionAutoTimer::UnregisterConnection(bool aContainedAV) {
    234  MOZ_ASSERT(mRefCnt);
    235  mRefCnt--;
    236  mUsedAV |= aContainedAV;
    237  if (mRefCnt == 0) {
    238    TimeDuration sample = TimeStamp::Now() - mStart;
    239    if (mUsedAV) {
    240      glean::webrtc::av_call_duration.AccumulateRawDuration(sample);
    241    }
    242    glean::webrtc::call_duration.AccumulateRawDuration(sample);
    243  }
    244 }
    245 
    246 bool PeerConnectionAutoTimer::IsStopped() { return mRefCnt == 0; }
    247 
    248 // There is not presently an implementation of these for nsTHashMap :(
    249 inline void ImplCycleCollectionUnlink(
    250    PeerConnectionImpl::RTCDtlsTransportMap& aMap) {
    251  for (auto& tableEntry : aMap) {
    252    ImplCycleCollectionUnlink(*tableEntry.GetModifiableData());
    253  }
    254  aMap.Clear();
    255 }
    256 
    257 inline void ImplCycleCollectionTraverse(
    258    nsCycleCollectionTraversalCallback& aCallback,
    259    PeerConnectionImpl::RTCDtlsTransportMap& aMap, const char* aName,
    260    uint32_t aFlags = 0) {
    261  for (auto& tableEntry : aMap) {
    262    ImplCycleCollectionTraverse(aCallback, *tableEntry.GetModifiableData(),
    263                                aName, aFlags);
    264  }
    265 }
    266 
    267 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(PeerConnectionImpl)
    268 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PeerConnectionImpl)
    269  tmp->Close();
    270  tmp->BreakCycles();
    271  NS_IMPL_CYCLE_COLLECTION_UNLINK(
    272      mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams,
    273      mOperations, mTransportIdToRTCDtlsTransport, mSctpTransport,
    274      mLastStableSctpTransport, mLastStableSctpDtlsTransport, mKungFuDeathGrip)
    275  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    276 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    277 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PeerConnectionImpl)
    278  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
    279      mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams,
    280      mOperations, mTransceivers, mTransportIdToRTCDtlsTransport,
    281      mSctpTransport, mLastStableSctpTransport, mLastStableSctpDtlsTransport,
    282      mKungFuDeathGrip)
    283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    284 
    285 NS_IMPL_CYCLE_COLLECTING_ADDREF(PeerConnectionImpl)
    286 NS_IMPL_CYCLE_COLLECTING_RELEASE(PeerConnectionImpl)
    287 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PeerConnectionImpl)
    288  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    289  NS_INTERFACE_MAP_ENTRY(nsISupports)
    290 NS_INTERFACE_MAP_END
    291 
    292 already_AddRefed<PeerConnectionImpl> PeerConnectionImpl::Constructor(
    293    const dom::GlobalObject& aGlobal) {
    294  RefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal);
    295 
    296  CSFLogDebug(LOGTAG, "Created PeerConnection: %p", pc.get());
    297 
    298  return pc.forget();
    299 }
    300 
    301 JSObject* PeerConnectionImpl::WrapObject(JSContext* aCx,
    302                                         JS::Handle<JSObject*> aGivenProto) {
    303  return PeerConnectionImpl_Binding::Wrap(aCx, this, aGivenProto);
    304 }
    305 
    306 nsPIDOMWindowInner* PeerConnectionImpl::GetParentObject() const {
    307  return mWindow;
    308 }
    309 
    310 bool PCUuidGenerator::Generate(std::string* idp) {
    311  nsresult rv;
    312 
    313  if (!mGenerator) {
    314    mGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv);
    315    if (NS_FAILED(rv)) {
    316      return false;
    317    }
    318    if (!mGenerator) {
    319      return false;
    320    }
    321  }
    322 
    323  nsID id;
    324  rv = mGenerator->GenerateUUIDInPlace(&id);
    325  if (NS_FAILED(rv)) {
    326    return false;
    327  }
    328  char buffer[NSID_LENGTH];
    329  id.ToProvidedString(buffer);
    330  idp->assign(buffer);
    331 
    332  return true;
    333 }
    334 
    335 bool IsPrivateBrowsing(nsPIDOMWindowInner* aWindow) {
    336  if (!aWindow) {
    337    return false;
    338  }
    339 
    340  Document* doc = aWindow->GetExtantDoc();
    341  if (!doc) {
    342    return false;
    343  }
    344 
    345  nsILoadContext* loadContext = doc->GetLoadContext();
    346  return loadContext && loadContext->UsePrivateBrowsing();
    347 }
    348 
    349 PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal)
    350    : mTimeCard(MOZ_LOG_TEST(logModuleInfo, LogLevel::Error) ? create_timecard()
    351                                                             : nullptr),
    352      mSignalingState(RTCSignalingState::Stable),
    353      mIceConnectionState(RTCIceConnectionState::New),
    354      mIceGatheringState(RTCIceGatheringState::New),
    355      mConnectionState(RTCPeerConnectionState::New),
    356      mWindow(do_QueryInterface(aGlobal ? aGlobal->GetAsSupports() : nullptr)),
    357      mCertificate(nullptr),
    358      mSTSThread(nullptr),
    359      mForceIceTcp(false),
    360      mTransportHandler(nullptr),
    361      mUuidGen(MakeUnique<PCUuidGenerator>()),
    362      mIceRestartCount(0),
    363      mIceRollbackCount(0),
    364      mTrickle(true)  // TODO(ekr@rtfm.com): Use pref
    365      ,
    366      mPrivateWindow(false),
    367      mActiveOnWindow(false),
    368      mTimestampMaker(dom::RTCStatsTimestampMaker::Create(mWindow)),
    369      mIdGenerator(new RTCStatsIdGenerator()),
    370      listenPort(0),
    371      connectPort(0),
    372      connectStr(nullptr) {
    373  MOZ_ASSERT(NS_IsMainThread());
    374  MOZ_ASSERT_IF(aGlobal, mWindow);
    375  mKungFuDeathGrip = this;
    376  if (aGlobal) {
    377    if (IsPrivateBrowsing(mWindow)) {
    378      mPrivateWindow = true;
    379      mDisableLongTermStats = true;
    380    }
    381    mWindow->AddPeerConnection();
    382    mActiveOnWindow = true;
    383 
    384    if (mWindow->GetDocumentURI()) {
    385      mWindow->GetDocumentURI()->GetAsciiHost(mHostname);
    386      nsresult rv;
    387      nsCOMPtr<nsIEffectiveTLDService> eTLDService(
    388          do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
    389      if (eTLDService) {
    390        (void)eTLDService->GetBaseDomain(mWindow->GetDocumentURI(), 0,
    391                                         mEffectiveTLDPlus1);
    392      }
    393 
    394      mRtxIsAllowed = !media::HostnameInPref(
    395          "media.peerconnection.video.use_rtx.blocklist", mHostname);
    396      mDuplicateFingerprintQuirk = media::HostnameInPref(
    397          "media.peerconnection.sdp.quirk.duplicate_fingerprint.allowlist",
    398          mHostname);
    399    }
    400  }
    401 
    402  if (!mUuidGen->Generate(&mHandle)) {
    403    MOZ_CRASH();
    404  }
    405 
    406  CSFLogInfo(LOGTAG, "%s: PeerConnectionImpl constructor for %s", __FUNCTION__,
    407             mHandle.c_str());
    408  STAMP_TIMECARD(mTimeCard, "Constructor Completed");
    409  mForceIceTcp =
    410      Preferences::GetBool("media.peerconnection.ice.force_ice_tcp", false);
    411  memset(mMaxReceiving, 0, sizeof(mMaxReceiving));
    412  memset(mMaxSending, 0, sizeof(mMaxSending));
    413  mJsConfiguration.mCertificatesProvided = false;
    414  mJsConfiguration.mPeerIdentityProvided = false;
    415 }
    416 
    417 PeerConnectionImpl::~PeerConnectionImpl() {
    418  MOZ_ASSERT(NS_IsMainThread());
    419 
    420  MOZ_ASSERT(!mTransportHandler,
    421             "PeerConnection should either be closed, or not initted in the "
    422             "first place.");
    423 
    424  if (mTimeCard) {
    425    STAMP_TIMECARD(mTimeCard, "Destructor Invoked");
    426    STAMP_TIMECARD(mTimeCard, mHandle.c_str());
    427    print_timecard(mTimeCard);
    428    destroy_timecard(mTimeCard);
    429    mTimeCard = nullptr;
    430  }
    431 
    432  CSFLogInfo(LOGTAG, "%s: PeerConnectionImpl destructor invoked for %s",
    433             __FUNCTION__, mHandle.c_str());
    434 }
    435 
    436 struct CompareCodecPriority {
    437  bool operator()(const UniquePtr<JsepCodecDescription>& lhs,
    438                  const UniquePtr<JsepCodecDescription>& rhs) const {
    439    // If only the left side is strongly preferred, prefer it
    440    return lhs->mStronglyPreferred && !rhs->mStronglyPreferred;
    441  }
    442 };
    443 
    444 nsresult PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
    445                                        nsGlobalWindowInner* aWindow) {
    446  nsresult res;
    447 
    448  MOZ_ASSERT(NS_IsMainThread());
    449 
    450  mPCObserver = &aObserver;
    451 
    452  // Find the STS thread
    453 
    454  mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
    455  MOZ_ASSERT(mSTSThread);
    456 
    457  RefPtr transportHandler = MediaTransportHandler::Create();
    458  if (mPrivateWindow) {
    459    transportHandler->EnterPrivateMode();
    460  }
    461 
    462  // Initialize NSS if we are in content process. For chrome process, NSS should
    463  // already been initialized.
    464  if (XRE_IsParentProcess()) {
    465    // This code interferes with the C++ unit test startup code.
    466    nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res);
    467    NS_ENSURE_SUCCESS(res, res);
    468  } else {
    469    NS_ENSURE_SUCCESS(res = InitNSSInContent(), res);
    470  }
    471 
    472  // Currently no standalone unit tests for DataChannel,
    473  // which is the user of mWindow
    474  MOZ_ASSERT(aWindow);
    475  mWindow = aWindow;
    476  NS_ENSURE_STATE(mWindow);
    477 
    478  // Now rummage through the objects to get a usable origin
    479  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mWindow);
    480  NS_ENSURE_STATE(sgo);
    481  nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
    482  NS_ENSURE_STATE(scriptContext);
    483 
    484  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal(
    485      do_QueryInterface(mWindow));
    486  NS_ENSURE_STATE(scriptPrincipal);
    487  nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
    488  NS_ENSURE_STATE(principal);
    489 
    490  res = nsContentUtils::GetWebExposedOriginSerialization(principal, mOrigin);
    491  NS_ENSURE_SUCCESS(res, res);
    492 
    493  PRTime timestamp = PR_Now();
    494  // Ok if we truncate this, but we want it to be large enough to reliably
    495  // contain the location on the tests we run in CI.
    496  char temp[256];
    497 
    498  nsAutoCString locationCStr;
    499 
    500  RefPtr<Location> location = mWindow->Location();
    501  res = location->GetHref(locationCStr);
    502  NS_ENSURE_SUCCESS(res, res);
    503 
    504  SprintfLiteral(temp, "%s %" PRIu64 " (id=%" PRIu64 " url=%s)",
    505                 mHandle.c_str(), static_cast<uint64_t>(timestamp),
    506                 static_cast<uint64_t>(mWindow ? mWindow->WindowID() : 0),
    507                 locationCStr.get() ? locationCStr.get() : "NULL");
    508 
    509  mName = temp;
    510 
    511  STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
    512  res = PeerConnectionCtx::InitializeGlobal();
    513  NS_ENSURE_SUCCESS(res, res);
    514 
    515  // Only set mTransportHandler here, after the NS_ENSURE_ exit guards, to not
    516  // leave it in an unusable state -- CreateIceCtx must have been called for
    517  // other calls to work.
    518  mTransportHandler = std::move(transportHandler);
    519  mTransportHandler->CreateIceCtx("PC:" + GetName());
    520 
    521  mJsepSession =
    522      MakeUnique<JsepSessionImpl>(mName, MakeUnique<PCUuidGenerator>());
    523  mJsepSession->SetRtxIsAllowed(mRtxIsAllowed);
    524 
    525  res = mJsepSession->Init();
    526  if (NS_FAILED(res)) {
    527    CSFLogError(LOGTAG, "%s: Couldn't init JSEP Session, res=%u", __FUNCTION__,
    528                static_cast<unsigned>(res));
    529    return res;
    530  }
    531 
    532  std::vector<UniquePtr<JsepCodecDescription>> preferredCodecs;
    533  SetupPreferredCodecs(preferredCodecs);
    534  mJsepSession->SetDefaultCodecs(preferredCodecs);
    535 
    536  // We use this to sort the list of codecs once everything is configured
    537  CompareCodecPriority comparator;
    538  // Sort by priority
    539  mJsepSession->SortCodecs(comparator);
    540 
    541  std::vector<RtpExtensionHeader> preferredHeaders;
    542  SetupPreferredRtpExtensions(preferredHeaders);
    543 
    544  for (const auto& header : preferredHeaders) {
    545    mJsepSession->AddRtpExtension(header.mMediaType, header.extensionname,
    546                                  header.direction);
    547  }
    548 
    549  if (XRE_IsContentProcess()) {
    550    mStunAddrsRequest =
    551        new net::StunAddrsRequestChild(new StunAddrsHandler(this));
    552  }
    553 
    554  // Initialize the media object.
    555  mForceProxy = ShouldForceProxy();
    556 
    557  // We put this here, in case we later want to set this based on a non-standard
    558  // param in RTCConfiguration.
    559  mAllowOldSetParameters = Preferences::GetBool(
    560      "media.peerconnection.allow_old_setParameters", false);
    561 
    562  // setup the stun local addresses IPC async call
    563  InitLocalAddrs();
    564 
    565  PeerConnectionCtx::GetInstance()->AddPeerConnection(mHandle, this);
    566 
    567  mGatheringStateChangeListener =
    568      mTransportHandler->GetGatheringStateChange().Connect(
    569          GetMainThreadSerialEventTarget(), this,
    570          &PeerConnectionImpl::IceGatheringStateChange);
    571  mConnectionStateChangeListener =
    572      mTransportHandler->GetConnectionStateChange().Connect(
    573          GetMainThreadSerialEventTarget(), this,
    574          &PeerConnectionImpl::IceConnectionStateChange);
    575  mCandidateListener = mTransportHandler->GetCandidateGathered().Connect(
    576      GetMainThreadSerialEventTarget(), this,
    577      &PeerConnectionImpl::OnCandidateFound);
    578  mAlpnNegotiatedListener = mTransportHandler->GetAlpnNegotiated().Connect(
    579      GetMainThreadSerialEventTarget(), this,
    580      &PeerConnectionImpl::OnAlpnNegotiated);
    581  mStateChangeListener = mTransportHandler->GetStateChange().Connect(
    582      GetMainThreadSerialEventTarget(), this,
    583      &PeerConnectionImpl::OnDtlsStateChange);
    584  mRtcpStateChangeListener = mTransportHandler->GetRtcpStateChange().Connect(
    585      GetMainThreadSerialEventTarget(), this,
    586      &PeerConnectionImpl::OnDtlsStateChange);
    587 
    588  return NS_OK;
    589 }
    590 
    591 void PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
    592                                    nsGlobalWindowInner& aWindow,
    593                                    ErrorResult& rv) {
    594  MOZ_ASSERT(NS_IsMainThread());
    595 
    596  nsresult res = Initialize(aObserver, &aWindow);
    597  if (NS_FAILED(res)) {
    598    rv.Throw(res);
    599    return;
    600  }
    601 }
    602 
    603 void PeerConnectionImpl::SetCertificate(
    604    mozilla::dom::RTCCertificate& aCertificate) {
    605  PC_AUTO_ENTER_API_CALL_NO_CHECK();
    606  MOZ_ASSERT(!mCertificate, "This can only be called once");
    607  mCertificate = &aCertificate;
    608 
    609  std::vector<uint8_t> fingerprint;
    610  nsresult rv =
    611      CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, &fingerprint);
    612  if (NS_FAILED(rv)) {
    613    CSFLogError(LOGTAG, "%s: Couldn't calculate fingerprint, rv=%u",
    614                __FUNCTION__, static_cast<unsigned>(rv));
    615    mCertificate = nullptr;
    616    return;
    617  }
    618  rv = mJsepSession->AddDtlsFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM,
    619                                        fingerprint);
    620  if (NS_FAILED(rv)) {
    621    CSFLogError(LOGTAG, "%s: Couldn't set DTLS credentials, rv=%u",
    622                __FUNCTION__, static_cast<unsigned>(rv));
    623    mCertificate = nullptr;
    624  }
    625 
    626  if (mUncommittedJsepSession) {
    627    (void)mUncommittedJsepSession->AddDtlsFingerprint(
    628        DtlsIdentity::DEFAULT_HASH_ALGORITHM, fingerprint);
    629  }
    630 }
    631 
    632 const RefPtr<mozilla::dom::RTCCertificate>& PeerConnectionImpl::Certificate()
    633    const {
    634  PC_AUTO_ENTER_API_CALL_NO_CHECK();
    635  return mCertificate;
    636 }
    637 
    638 RefPtr<DtlsIdentity> PeerConnectionImpl::Identity() const {
    639  PC_AUTO_ENTER_API_CALL_NO_CHECK();
    640  MOZ_ASSERT(mCertificate);
    641  return mCertificate->CreateDtlsIdentity();
    642 }
    643 
    644 void RecordCodecTelemetry() {
    645  const auto prefs = PeerConnectionImpl::GetDefaultCodecPreferences();
    646  if (WebrtcVideoConduit::HasH264Hardware()) {
    647    glean::webrtc::has_h264_hardware
    648        .EnumGet(glean::webrtc::HasH264HardwareLabel::eTrue)
    649        .Add();
    650  }
    651 
    652  glean::webrtc::software_h264_enabled
    653      .EnumGet(static_cast<glean::webrtc::SoftwareH264EnabledLabel>(
    654          prefs.SoftwareH264Enabled()))
    655      .Add();
    656  glean::webrtc::hardware_h264_enabled
    657      .EnumGet(static_cast<glean::webrtc::HardwareH264EnabledLabel>(
    658          prefs.HardwareH264Enabled()))
    659      .Add();
    660  glean::webrtc::h264_enabled
    661      .EnumGet(
    662          static_cast<glean::webrtc::H264EnabledLabel>(prefs.H264Enabled()))
    663      .Add();
    664 }
    665 
    666 // Data channels won't work without a window, so in order for the C++ unit
    667 // tests to work (it doesn't have a window available) we ifdef the following
    668 // two implementations.
    669 //
    670 // Note: 'media.peerconnection.sctp.force_maximum_message_size' changes
    671 // behaviour triggered by these parameters.
    672 NS_IMETHODIMP
    673 PeerConnectionImpl::EnsureDataConnection(uint16_t aLocalPort,
    674                                         uint16_t aNumstreams) {
    675  PC_AUTO_ENTER_API_CALL(false);
    676 
    677  if (mDataConnection) {
    678    CSFLogDebug(LOGTAG, "%s DataConnection already connected", __FUNCTION__);
    679    return NS_OK;
    680  }
    681 
    682  nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
    683  if (auto res = DataChannelConnection::Create(this, target, mTransportHandler,
    684                                               aLocalPort, aNumstreams)) {
    685    mDataConnection = res.value();
    686    CSFLogDebug(LOGTAG, "%s DataChannelConnection %p attached to %s",
    687                __FUNCTION__, (void*)mDataConnection.get(), mHandle.c_str());
    688    return NS_OK;
    689  }
    690  CSFLogError(LOGTAG, "%s DataConnection Create Failed", __FUNCTION__);
    691  return NS_ERROR_FAILURE;
    692 }
    693 
    694 nsresult PeerConnectionImpl::GetDatachannelParameters(
    695    uint32_t* channels, uint16_t* localport, uint16_t* remoteport,
    696    uint32_t* remotemaxmessagesize, std::string* transportId,
    697    bool* client) const {
    698  // Clear, just in case we fail.
    699  *channels = 0;
    700  *localport = 0;
    701  *remoteport = 0;
    702  *remotemaxmessagesize = 0;
    703  transportId->clear();
    704 
    705  Maybe<const JsepTransceiver> datachannelTransceiver =
    706      mJsepSession->FindTransceiver([](const JsepTransceiver& aTransceiver) {
    707        return aTransceiver.GetMediaType() == SdpMediaSection::kApplication;
    708      });
    709 
    710  if (!datachannelTransceiver ||
    711      !datachannelTransceiver->mTransport.mComponents ||
    712      !datachannelTransceiver->mTransport.mDtls ||
    713      !datachannelTransceiver->mSendTrack.GetNegotiatedDetails()) {
    714    return NS_ERROR_FAILURE;
    715  }
    716 
    717  // This will release assert if there is no such index, and that's ok
    718  const JsepTrackEncoding& encoding =
    719      datachannelTransceiver->mSendTrack.GetNegotiatedDetails()->GetEncoding(0);
    720 
    721  if (NS_WARN_IF(encoding.GetCodecs().empty())) {
    722    CSFLogError(LOGTAG,
    723                "%s: Negotiated m=application with no codec. "
    724                "This is likely to be broken.",
    725                __FUNCTION__);
    726    return NS_ERROR_FAILURE;
    727  }
    728 
    729  for (const auto& codec : encoding.GetCodecs()) {
    730    if (codec->Type() != SdpMediaSection::kApplication) {
    731      CSFLogError(LOGTAG,
    732                  "%s: Codec type for m=application was %u, this "
    733                  "is a bug.",
    734                  __FUNCTION__, static_cast<unsigned>(codec->Type()));
    735      MOZ_ASSERT(false, "Codec for m=application was not \"application\"");
    736      return NS_ERROR_FAILURE;
    737    }
    738 
    739    if (codec->mName != "webrtc-datachannel") {
    740      CSFLogWarn(LOGTAG,
    741                 "%s: Codec for m=application was not "
    742                 "webrtc-datachannel (was instead %s). ",
    743                 __FUNCTION__, codec->mName.c_str());
    744      continue;
    745    }
    746 
    747    if (codec->mChannels) {
    748      *channels = codec->mChannels;
    749    } else {
    750      *channels = std::clamp(
    751          Preferences::GetInt("media.peerconnection.sctp.default_max_streams",
    752                              WEBRTC_DATACHANNEL_STREAMS_DEFAULT),
    753          256, 2048);
    754      ;
    755    }
    756    const JsepApplicationCodecDescription* appCodec =
    757        static_cast<const JsepApplicationCodecDescription*>(codec.get());
    758    *localport = appCodec->mLocalPort;
    759    *remoteport = appCodec->mRemotePort;
    760    *remotemaxmessagesize = appCodec->mRemoteMaxMessageSize;
    761    MOZ_ASSERT(!datachannelTransceiver->mTransport.mTransportId.empty());
    762    *transportId = datachannelTransceiver->mTransport.mTransportId;
    763    *client = datachannelTransceiver->mTransport.mDtls->GetRole() ==
    764              JsepDtlsTransport::kJsepDtlsClient;
    765    return NS_OK;
    766  }
    767  return NS_ERROR_FAILURE;
    768 }
    769 
    770 nsresult PeerConnectionImpl::AddRtpTransceiverToJsepSession(
    771    JsepTransceiver& transceiver) {
    772  mJsepSession->AddTransceiver(transceiver);
    773  return NS_OK;
    774 }
    775 
    776 static Maybe<SdpMediaSection::MediaType> ToSdpMediaType(
    777    const nsAString& aKind) {
    778  if (aKind.EqualsASCII("audio")) {
    779    return Some(SdpMediaSection::MediaType::kAudio);
    780  } else if (aKind.EqualsASCII("video")) {
    781    return Some(SdpMediaSection::MediaType::kVideo);
    782  }
    783  return Nothing();
    784 }
    785 
    786 already_AddRefed<RTCRtpTransceiver> PeerConnectionImpl::AddTransceiver(
    787    const dom::RTCRtpTransceiverInit& aInit, const nsAString& aKind,
    788    dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv) {
    789  // Copy, because we might need to modify
    790  RTCRtpTransceiverInit init(aInit);
    791 
    792  Maybe<SdpMediaSection::MediaType> type = ToSdpMediaType(aKind);
    793  if (NS_WARN_IF(!type.isSome())) {
    794    MOZ_ASSERT(false, "Invalid media kind");
    795    aRv = NS_ERROR_INVALID_ARG;
    796    return nullptr;
    797  }
    798 
    799  JsepTransceiver jsepTransceiver(*type, *mUuidGen);
    800  jsepTransceiver.SetRtxIsAllowed(mRtxIsAllowed);
    801 
    802  // Do this last, since it is not possible to roll back.
    803  nsresult rv = AddRtpTransceiverToJsepSession(jsepTransceiver);
    804  if (NS_FAILED(rv)) {
    805    CSFLogError(LOGTAG, "%s: AddRtpTransceiverToJsepSession failed, res=%u",
    806                __FUNCTION__, static_cast<unsigned>(rv));
    807    aRv = rv;
    808    return nullptr;
    809  }
    810 
    811  auto& sendEncodings = init.mSendEncodings;
    812 
    813  // See https://www.w3.org/TR/webrtc/#dom-rtcrtpsender-setparameters step 11
    814  // Also see https://bugzilla.mozilla.org/show_bug.cgi?id=1968828
    815  auto getCapabilitiesResult = dom::Nullable<dom::RTCRtpCapabilities>();
    816  GetCapabilities(aKind, getCapabilitiesResult, sdp::Direction::kSend);
    817  MOZ_ASSERT(!getCapabilitiesResult.IsNull());
    818  if (NS_WARN_IF(getCapabilitiesResult.IsNull())) {
    819    aRv.Throw(NS_ERROR_FAILURE);
    820    return nullptr;
    821  }
    822  const auto& codecs = getCapabilitiesResult.Value().mCodecs;
    823  for (const auto& encoding : sendEncodings) {
    824    bool found = false;
    825    if (encoding.mCodec.WasPassed()) {
    826      for (const auto& codec : codecs) {
    827        if (DoesCodecParameterMatchCodec(encoding.mCodec.Value(), codec)) {
    828          found = true;
    829          break;
    830        }
    831      }
    832      if (!found) {
    833        const auto mime =
    834            NS_LossyConvertUTF16toASCII(encoding.mCodec.Value().mMimeType);
    835        std::stringstream ss;
    836        ss << "Codec " << mime
    837           << " does not match any codec "
    838              "in GetCapabilities";
    839        nsCString errorStr(ss.str().c_str());
    840        aRv.ThrowOperationError(errorStr);
    841        return nullptr;
    842      }
    843    }
    844  }
    845 
    846  // CheckAndRectifyEncodings covers these six:
    847  // If any encoding contains a rid member whose value does not conform to the
    848  // grammar requirements specified in Section 10 of [RFC8851], throw a
    849  // TypeError.
    850 
    851  // If some but not all encodings contain a rid member, throw a TypeError.
    852 
    853  // If any encoding contains a rid member whose value is the same as that of a
    854  // rid contained in another encoding in sendEncodings, throw a TypeError.
    855 
    856  // If kind is "audio", remove the scaleResolutionDownBy member from all
    857  // encodings that contain one.
    858 
    859  // If any encoding contains a scaleResolutionDownBy member whose value is
    860  // less than 1.0, throw a RangeError.
    861 
    862  // Verify that the value of each maxFramerate member in sendEncodings that is
    863  // defined is greater than 0.0. If one of the maxFramerate values does not
    864  // meet this requirement, throw a RangeError.
    865 
    866  RTCRtpSender::CheckAndRectifyEncodings(
    867      sendEncodings, *type == SdpMediaSection::kVideo,
    868      // No codecs until after negotiation
    869      Optional<Sequence<RTCRtpCodecParameters>>(), false, false,
    870      MatchGetCapabilities::NO, aRv);
    871  if (aRv.Failed()) {
    872    return nullptr;
    873  }
    874 
    875  // If any encoding contains a read-only parameter other than rid, throw an
    876  // InvalidAccessError.
    877  // NOTE: We don't support any additional read-only params right now. Also,
    878  // spec shoehorns this in between checks that setParameters also performs
    879  // (between the rid checks and the scaleResolutionDownBy checks).
    880 
    881  // If any encoding contains a scaleResolutionDownBy member, then for each
    882  // encoding without one, add a scaleResolutionDownBy member with the value
    883  // 1.0.
    884  for (const auto& constEncoding : sendEncodings) {
    885    if (constEncoding.mScaleResolutionDownBy.WasPassed()) {
    886      for (auto& encoding : sendEncodings) {
    887        if (!encoding.mScaleResolutionDownBy.WasPassed()) {
    888          encoding.mScaleResolutionDownBy.Construct(1.0f);
    889        }
    890      }
    891      break;
    892    }
    893  }
    894 
    895  // Let maxN be the maximum number of total simultaneous encodings the user
    896  // agent may support for this kind, at minimum 1.This should be an optimistic
    897  // number since the codec to be used is not known yet.
    898  size_t maxN =
    899      (*type == SdpMediaSection::kVideo) ? webrtc::kMaxSimulcastStreams : 1;
    900 
    901  // If the number of encodings stored in sendEncodings exceeds maxN, then trim
    902  // sendEncodings from the tail until its length is maxN.
    903  // NOTE: Spec has this after all validation steps; even if there are elements
    904  // that we will trim off, we still validate them.
    905  if (sendEncodings.Length() > maxN) {
    906    sendEncodings.TruncateLength(maxN);
    907  }
    908 
    909  // If kind is "video" and none of the encodings contain a
    910  // scaleResolutionDownBy member, then for each encoding, add a
    911  // scaleResolutionDownBy member with the value 2^(length of sendEncodings -
    912  // encoding index - 1). This results in smaller-to-larger resolutions where
    913  // the last encoding has no scaling applied to it, e.g. 4:2:1 if the length
    914  // is 3.
    915  // NOTE: The code above ensures that these are all set, or all unset, so we
    916  // can just check the first one.
    917  if (sendEncodings.Length() && *type == SdpMediaSection::kVideo &&
    918      !sendEncodings[0].mScaleResolutionDownBy.WasPassed()) {
    919    double scale = 1.0f;
    920    for (auto it = sendEncodings.rbegin(); it != sendEncodings.rend(); ++it) {
    921      it->mScaleResolutionDownBy.Construct(scale);
    922      scale *= 2;
    923    }
    924  }
    925 
    926  // If the number of encodings now stored in sendEncodings is 1, then remove
    927  // any rid member from the lone entry.
    928  if (sendEncodings.Length() == 1) {
    929    sendEncodings[0].mRid.Reset();
    930  }
    931 
    932  RefPtr<RTCRtpTransceiver> transceiver = CreateTransceiver(
    933      jsepTransceiver.GetUuid(),
    934      jsepTransceiver.GetMediaType() == SdpMediaSection::kVideo, init,
    935      aSendTrack, aAddTrackMagic, aRv);
    936 
    937  if (aRv.Failed()) {
    938    // Would be nice if we could peek at the rv without stealing it, so we
    939    // could log...
    940    CSFLogError(LOGTAG, "%s: failed", __FUNCTION__);
    941    return nullptr;
    942  }
    943 
    944  mTransceivers.AppendElement(transceiver);
    945  return transceiver.forget();
    946 }
    947 
    948 bool PeerConnectionImpl::CheckNegotiationNeeded() {
    949  MOZ_ASSERT(mSignalingState == RTCSignalingState::Stable);
    950  SyncToJsep();
    951  return !mLocalIceCredentialsToReplace.empty() ||
    952         mJsepSession->CheckNegotiationNeeded();
    953 }
    954 
    955 bool PeerConnectionImpl::CreatedSender(const dom::RTCRtpSender& aSender) const {
    956  return aSender.IsMyPc(this);
    957 }
    958 
    959 nsresult PeerConnectionImpl::MaybeInitializeDataChannel() {
    960  PC_AUTO_ENTER_API_CALL(false);
    961  CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
    962 
    963  uint32_t channels = 0;
    964  uint16_t localport = 0;
    965  uint16_t remoteport = 0;
    966  uint32_t remotemaxmessagesize = 0;
    967  std::string transportId;
    968  bool client = false;
    969  nsresult rv =
    970      GetDatachannelParameters(&channels, &localport, &remoteport,
    971                               &remotemaxmessagesize, &transportId, &client);
    972 
    973  if (NS_FAILED(rv)) {
    974    CSFLogDebug(LOGTAG, "%s: We did not negotiate datachannel", __FUNCTION__);
    975    return NS_OK;
    976  }
    977 
    978  if (channels > MAX_NUM_STREAMS) {
    979    channels = MAX_NUM_STREAMS;
    980  }
    981 
    982  rv = EnsureDataConnection(localport, channels);
    983  if (NS_SUCCEEDED(rv)) {
    984    mDataConnection->SetMaxMessageSize(remotemaxmessagesize);
    985    if (mDataConnection->ConnectToTransport(transportId, client, localport,
    986                                            remoteport)) {
    987      return NS_OK;
    988    }
    989    // If we inited the DataConnection, call Destroy() before releasing it
    990    mDataConnection->Destroy();
    991  }
    992  mDataConnection = nullptr;
    993  return NS_ERROR_FAILURE;
    994 }
    995 
    996 already_AddRefed<RTCDataChannel> PeerConnectionImpl::CreateDataChannel(
    997    const nsACString& aLabel, const nsACString& aProtocol, uint16_t aType,
    998    bool ordered, uint16_t aMaxTime, uint16_t aMaxNum, bool aExternalNegotiated,
    999    uint16_t aStream, ErrorResult& rv) {
   1000  RefPtr<RTCDataChannel> result;
   1001  rv = CreateDataChannel(aLabel, aProtocol, aType, ordered, aMaxTime, aMaxNum,
   1002                         aExternalNegotiated, aStream, getter_AddRefs(result));
   1003  return result.forget();
   1004 }
   1005 
   1006 NS_IMETHODIMP
   1007 PeerConnectionImpl::CreateDataChannel(
   1008    const nsACString& aLabel, const nsACString& aProtocol, uint16_t aType,
   1009    bool ordered, uint16_t aMaxTime, uint16_t aMaxNum, bool aExternalNegotiated,
   1010    uint16_t aStream, RTCDataChannel** aRetval) {
   1011  PC_AUTO_ENTER_API_CALL(false);
   1012  MOZ_ASSERT(aRetval);
   1013 
   1014  RefPtr<DataChannel> dataChannel;
   1015  DataChannelReliabilityPolicy prPolicy;
   1016  Nullable<uint16_t> maxLifeTime;
   1017  Nullable<uint16_t> maxRetransmits;
   1018  switch (aType) {
   1019    case IPeerConnection::kDataChannelReliable:
   1020      prPolicy = DataChannelReliabilityPolicy::Reliable;
   1021      break;
   1022    case IPeerConnection::kDataChannelPartialReliableRexmit:
   1023      prPolicy = DataChannelReliabilityPolicy::LimitedRetransmissions;
   1024      maxRetransmits.SetValue(aMaxNum);
   1025      break;
   1026    case IPeerConnection::kDataChannelPartialReliableTimed:
   1027      prPolicy = DataChannelReliabilityPolicy::LimitedLifetime;
   1028      maxLifeTime.SetValue(aMaxTime);
   1029      break;
   1030    default:
   1031      MOZ_ASSERT(false);
   1032      return NS_ERROR_FAILURE;
   1033  }
   1034 
   1035  uint16_t maxStreams = std::clamp(
   1036      Preferences::GetInt("media.peerconnection.sctp.default_max_streams",
   1037                          WEBRTC_DATACHANNEL_STREAMS_DEFAULT),
   1038      256, 2048);
   1039 
   1040  nsresult rv =
   1041      EnsureDataConnection(WEBRTC_DATACHANNEL_PORT_DEFAULT, maxStreams);
   1042  if (NS_FAILED(rv)) {
   1043    return rv;
   1044  }
   1045  dataChannel = mDataConnection->Open(
   1046      aLabel, aProtocol, prPolicy, ordered,
   1047      prPolicy == DataChannelReliabilityPolicy::LimitedRetransmissions
   1048          ? aMaxNum
   1049          : (prPolicy == DataChannelReliabilityPolicy::LimitedLifetime
   1050                 ? aMaxTime
   1051                 : 0),
   1052      aExternalNegotiated, aStream);
   1053  NS_ENSURE_TRUE(dataChannel, NS_ERROR_NOT_AVAILABLE);
   1054 
   1055  CSFLogDebug(LOGTAG, "%s: making DOMDataChannel", __FUNCTION__);
   1056 
   1057  Maybe<JsepTransceiver> dcTransceiver =
   1058      mJsepSession->FindTransceiver([](const JsepTransceiver& aTransceiver) {
   1059        return aTransceiver.GetMediaType() == SdpMediaSection::kApplication;
   1060      });
   1061 
   1062  if (dcTransceiver) {
   1063    dcTransceiver->RestartDatachannelTransceiver();
   1064    mJsepSession->SetTransceiver(*dcTransceiver);
   1065  } else {
   1066    mJsepSession->AddTransceiver(
   1067        JsepTransceiver(SdpMediaSection::MediaType::kApplication, *mUuidGen));
   1068  }
   1069 
   1070  RefPtr<RTCDataChannel> retval;
   1071  rv = NS_NewDOMDataChannel(dataChannel.forget(), aLabel, mOrigin, ordered,
   1072                            maxLifeTime, maxRetransmits, aProtocol,
   1073                            aExternalNegotiated, mWindow,
   1074                            getter_AddRefs(retval));
   1075  if (NS_FAILED(rv)) {
   1076    return rv;
   1077  }
   1078  retval.forget(aRetval);
   1079  return NS_OK;
   1080 }
   1081 
   1082 NS_IMPL_CYCLE_COLLECTION(PeerConnectionImpl::Operation, mPromise, mPc)
   1083 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PeerConnectionImpl::Operation)
   1084  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1085 NS_INTERFACE_MAP_END
   1086 NS_IMPL_CYCLE_COLLECTING_ADDREF(PeerConnectionImpl::Operation)
   1087 NS_IMPL_CYCLE_COLLECTING_RELEASE(PeerConnectionImpl::Operation)
   1088 
   1089 PeerConnectionImpl::Operation::Operation(PeerConnectionImpl* aPc,
   1090                                         ErrorResult& aError)
   1091    : mPromise(aPc->MakePromise(aError)), mPc(aPc) {}
   1092 
   1093 PeerConnectionImpl::Operation::~Operation() = default;
   1094 
   1095 void PeerConnectionImpl::Operation::Call(ErrorResult& aError) {
   1096  RefPtr<dom::Promise> opPromise = CallImpl(aError);
   1097  if (aError.Failed()) {
   1098    return;
   1099  }
   1100  // Upon fulfillment or rejection of the promise returned by the operation,
   1101  // run the following steps:
   1102  // (NOTE: mPromise is p from https://w3c.github.io/webrtc-pc/#dfn-chain,
   1103  // and CallImpl() is what returns the promise for the operation itself)
   1104  opPromise->AppendNativeHandler(this);
   1105 }
   1106 
   1107 void PeerConnectionImpl::Operation::ResolvedCallback(
   1108    JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
   1109  // If connection.[[IsClosed]] is true, abort these steps.
   1110  // (the spec wants p to never settle in this event)
   1111  if (!mPc->IsClosed()) {
   1112    // If the promise returned by operation was fulfilled with a
   1113    // value, fulfill p with that value.
   1114    mPromise->MaybeResolveWithClone(aCx, aValue);
   1115    // Upon fulfillment or rejection of p, execute the following
   1116    // steps:
   1117    // (Static analysis forces us to use a temporary)
   1118    RefPtr<PeerConnectionImpl> pc = mPc;
   1119    pc->RunNextOperation(aRv);
   1120  }
   1121 }
   1122 
   1123 void PeerConnectionImpl::Operation::RejectedCallback(
   1124    JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
   1125  // If connection.[[IsClosed]] is true, abort these steps.
   1126  // (the spec wants p to never settle in this event)
   1127  if (!mPc->IsClosed()) {
   1128    // If the promise returned by operation was rejected with a
   1129    // value, reject p with that value.
   1130    mPromise->MaybeRejectWithClone(aCx, aValue);
   1131    // Upon fulfillment or rejection of p, execute the following
   1132    // steps:
   1133    // (Static analysis forces us to use a temporary)
   1134    RefPtr<PeerConnectionImpl> pc = mPc;
   1135    pc->RunNextOperation(aRv);
   1136  }
   1137 }
   1138 
   1139 NS_IMPL_CYCLE_COLLECTION_INHERITED(PeerConnectionImpl::JSOperation,
   1140                                   PeerConnectionImpl::Operation, mOperation)
   1141 
   1142 NS_IMPL_ADDREF_INHERITED(PeerConnectionImpl::JSOperation,
   1143                         PeerConnectionImpl::Operation)
   1144 NS_IMPL_RELEASE_INHERITED(PeerConnectionImpl::JSOperation,
   1145                          PeerConnectionImpl::Operation)
   1146 
   1147 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PeerConnectionImpl::JSOperation)
   1148 NS_INTERFACE_MAP_END_INHERITING(PeerConnectionImpl::Operation)
   1149 
   1150 PeerConnectionImpl::JSOperation::JSOperation(PeerConnectionImpl* aPc,
   1151                                             dom::ChainedOperation& aOp,
   1152                                             ErrorResult& aError)
   1153    : Operation(aPc, aError), mOperation(&aOp) {}
   1154 
   1155 RefPtr<dom::Promise> PeerConnectionImpl::JSOperation::CallImpl(
   1156    ErrorResult& aError) {
   1157  // Static analysis will not let us call this without a temporary :(
   1158  RefPtr<dom::ChainedOperation> op = mOperation;
   1159  return op->Call(aError);
   1160 }
   1161 
   1162 already_AddRefed<dom::Promise> PeerConnectionImpl::Chain(
   1163    dom::ChainedOperation& aOperation, ErrorResult& aError) {
   1164  MOZ_RELEASE_ASSERT(!mChainingOperation);
   1165  mChainingOperation = true;
   1166  RefPtr<Operation> operation = new JSOperation(this, aOperation, aError);
   1167  if (aError.Failed()) {
   1168    return nullptr;
   1169  }
   1170  RefPtr<Promise> promise = Chain(operation, aError);
   1171  if (aError.Failed()) {
   1172    return nullptr;
   1173  }
   1174  mChainingOperation = false;
   1175  return promise.forget();
   1176 }
   1177 
   1178 // This is kinda complicated, but it is what the spec requires us to do. The
   1179 // core of what makes this complicated is the requirement that |aOperation| be
   1180 // run _immediately_ (without any Promise.Then!) if the operations chain is
   1181 // empty.
   1182 already_AddRefed<dom::Promise> PeerConnectionImpl::Chain(
   1183    const RefPtr<Operation>& aOperation, ErrorResult& aError) {
   1184  // If connection.[[IsClosed]] is true, return a promise rejected with a newly
   1185  // created InvalidStateError.
   1186  if (IsClosed()) {
   1187    CSFLogDebug(LOGTAG, "%s:%d: Peer connection is closed", __FILE__, __LINE__);
   1188    RefPtr<dom::Promise> error = MakePromise(aError);
   1189    if (aError.Failed()) {
   1190      return nullptr;
   1191    }
   1192    error->MaybeRejectWithInvalidStateError("Peer connection is closed");
   1193    return error.forget();
   1194  }
   1195 
   1196  // Append operation to [[Operations]].
   1197  mOperations.AppendElement(aOperation);
   1198 
   1199  // If the length of [[Operations]] is exactly 1, execute operation.
   1200  if (mOperations.Length() == 1) {
   1201    aOperation->Call(aError);
   1202    if (aError.Failed()) {
   1203      return nullptr;
   1204    }
   1205  }
   1206 
   1207  // This is the promise p from https://w3c.github.io/webrtc-pc/#dfn-chain
   1208  return do_AddRef(aOperation->GetPromise());
   1209 }
   1210 
   1211 void PeerConnectionImpl::RunNextOperation(ErrorResult& aError) {
   1212  // If connection.[[IsClosed]] is true, abort these steps.
   1213  if (IsClosed()) {
   1214    return;
   1215  }
   1216 
   1217  // Remove the first element of [[Operations]].
   1218  mOperations.RemoveElementAt(0);
   1219 
   1220  // If [[Operations]] is non-empty, execute the operation represented by the
   1221  // first element of [[Operations]], and abort these steps.
   1222  if (mOperations.Length()) {
   1223    // Cannot call without a temporary :(
   1224    RefPtr<Operation> op = mOperations[0];
   1225    op->Call(aError);
   1226    return;
   1227  }
   1228 
   1229  // If connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] is false, abort
   1230  // these steps.
   1231  if (!mUpdateNegotiationNeededFlagOnEmptyChain) {
   1232    return;
   1233  }
   1234 
   1235  // Set connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to false.
   1236  mUpdateNegotiationNeededFlagOnEmptyChain = false;
   1237  // Update the negotiation-needed flag for connection.
   1238  UpdateNegotiationNeeded();
   1239 }
   1240 
   1241 void PeerConnectionImpl::SyncToJsep() {
   1242  for (const auto& transceiver : mTransceivers) {
   1243    transceiver->SyncToJsep(*mJsepSession);
   1244  }
   1245 }
   1246 
   1247 void PeerConnectionImpl::SyncFromJsep() {
   1248  CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
   1249  mJsepSession->ForEachTransceiver(
   1250      [this, self = RefPtr<PeerConnectionImpl>(this)](
   1251          const JsepTransceiver& jsepTransceiver) {
   1252        if (jsepTransceiver.GetMediaType() ==
   1253            SdpMediaSection::MediaType::kApplication) {
   1254          return;
   1255        }
   1256 
   1257        CSFLogDebug(LOGTAG, "%s: Looking for match", __FUNCTION__);
   1258        RefPtr<RTCRtpTransceiver> transceiver;
   1259        for (auto& temp : mTransceivers) {
   1260          if (temp->GetJsepTransceiverId() == jsepTransceiver.GetUuid()) {
   1261            CSFLogDebug(LOGTAG, "%s: Found match", __FUNCTION__);
   1262            transceiver = temp;
   1263            break;
   1264          }
   1265        }
   1266 
   1267        if (!transceiver) {
   1268          if (jsepTransceiver.IsRemoved()) {
   1269            return;
   1270          }
   1271          CSFLogDebug(LOGTAG, "%s: No match, making new", __FUNCTION__);
   1272          dom::RTCRtpTransceiverInit init;
   1273          init.mDirection = RTCRtpTransceiverDirection::Recvonly;
   1274          IgnoredErrorResult rv;
   1275          transceiver = CreateTransceiver(
   1276              jsepTransceiver.GetUuid(),
   1277              jsepTransceiver.GetMediaType() == SdpMediaSection::kVideo, init,
   1278              nullptr, false, rv);
   1279          if (NS_WARN_IF(rv.Failed())) {
   1280            MOZ_ASSERT(false);
   1281            return;
   1282          }
   1283          mTransceivers.AppendElement(transceiver);
   1284        }
   1285 
   1286        CSFLogDebug(LOGTAG, "%s: Syncing transceiver", __FUNCTION__);
   1287        transceiver->SyncFromJsep(*mJsepSession);
   1288      });
   1289 }
   1290 
   1291 already_AddRefed<dom::Promise> PeerConnectionImpl::MakePromise(
   1292    ErrorResult& aError) const {
   1293  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
   1294  return dom::Promise::Create(global, aError);
   1295 }
   1296 
   1297 void PeerConnectionImpl::UpdateNegotiationNeeded() {
   1298  // If the length of connection.[[Operations]] is not 0, then set
   1299  // connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and abort
   1300  // these steps.
   1301  if (mOperations.Length() != 0) {
   1302    mUpdateNegotiationNeededFlagOnEmptyChain = true;
   1303    return;
   1304  }
   1305 
   1306  // Queue a task to run the following steps:
   1307  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1308      __func__, [this, self = RefPtr<PeerConnectionImpl>(this)] {
   1309        // If connection.[[IsClosed]] is true, abort these steps.
   1310        if (IsClosed()) {
   1311          return;
   1312        }
   1313        // If the length of connection.[[Operations]] is not 0, then set
   1314        // connection.[[UpdateNegotiationNeededFlagOnEmptyChain]] to true, and
   1315        // abort these steps.
   1316        if (mOperations.Length()) {
   1317          mUpdateNegotiationNeededFlagOnEmptyChain = true;
   1318          return;
   1319        }
   1320        // If connection's signaling state is not "stable", abort these steps.
   1321        if (mSignalingState != RTCSignalingState::Stable) {
   1322          return;
   1323        }
   1324        // If the result of checking if negotiation is needed is false, clear
   1325        // the negotiation-needed flag by setting
   1326        // connection.[[NegotiationNeeded]] to false, and abort these steps.
   1327        if (!CheckNegotiationNeeded()) {
   1328          mNegotiationNeeded = false;
   1329          return;
   1330        }
   1331 
   1332        // If connection.[[NegotiationNeeded]] is already true, abort these
   1333        // steps.
   1334        if (mNegotiationNeeded) {
   1335          return;
   1336        }
   1337 
   1338        // Set connection.[[NegotiationNeeded]] to true.
   1339        mNegotiationNeeded = true;
   1340 
   1341        // Fire an event named negotiationneeded at connection.
   1342        ErrorResult rv;
   1343        mPCObserver->FireNegotiationNeededEvent(rv);
   1344      }));
   1345 }
   1346 
   1347 RefPtr<dom::RTCRtpTransceiver> PeerConnectionImpl::GetTransceiver(
   1348    const std::string& aTransceiverId) {
   1349  for (const auto& transceiver : mTransceivers) {
   1350    if (transceiver->GetJsepTransceiverId() == aTransceiverId) {
   1351      return transceiver;
   1352    }
   1353  }
   1354  return nullptr;
   1355 }
   1356 
   1357 void PeerConnectionImpl::NotifyDataChannel(
   1358    already_AddRefed<DataChannel> aChannel, const nsACString& aLabel,
   1359    bool aOrdered, mozilla::dom::Nullable<uint16_t> aMaxLifeTime,
   1360    mozilla::dom::Nullable<uint16_t> aMaxRetransmits,
   1361    const nsACString& aProtocol, bool aNegotiated) {
   1362  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   1363 
   1364  RefPtr<DataChannel> channel(aChannel);
   1365  MOZ_ASSERT(channel);
   1366  CSFLogDebug(LOGTAG, "%s: channel: %p", __FUNCTION__, channel.get());
   1367 
   1368  RefPtr<RTCDataChannel> domchannel;
   1369  nsresult rv =
   1370      NS_NewDOMDataChannel(channel.forget(), aLabel, mOrigin, aOrdered,
   1371                           aMaxLifeTime, aMaxRetransmits, aProtocol,
   1372                           aNegotiated, mWindow, getter_AddRefs(domchannel));
   1373  NS_ENSURE_SUCCESS_VOID(rv);
   1374 
   1375  domchannel->SetReadyState(RTCDataChannelState::Open);
   1376 
   1377  JSErrorResult jrv;
   1378  mPCObserver->NotifyDataChannel(*domchannel, jrv);
   1379 }
   1380 
   1381 void PeerConnectionImpl::NotifyDataChannelOpen(DataChannel*) {
   1382  mDataChannelsOpened++;
   1383 }
   1384 
   1385 void PeerConnectionImpl::NotifyDataChannelClosed(DataChannel*) {
   1386  mDataChannelsClosed++;
   1387 }
   1388 
   1389 void PeerConnectionImpl::NotifySctpConnected() {
   1390  if (!mSctpTransport) {
   1391    MOZ_ASSERT(false);
   1392    return;
   1393  }
   1394 
   1395  mSctpTransport->UpdateState(RTCSctpTransportState::Connected);
   1396 }
   1397 
   1398 void PeerConnectionImpl::NotifySctpClosed() {
   1399  if (!mSctpTransport) {
   1400    MOZ_ASSERT(false);
   1401    return;
   1402  }
   1403 
   1404  mSctpTransport->UpdateState(RTCSctpTransportState::Closed);
   1405 }
   1406 
   1407 NS_IMETHODIMP
   1408 PeerConnectionImpl::CreateOffer(const RTCOfferOptions& aOptions) {
   1409  JsepOfferOptions options;
   1410  // convert the RTCOfferOptions to JsepOfferOptions
   1411  if (aOptions.mOfferToReceiveAudio.WasPassed()) {
   1412    options.mOfferToReceiveAudio =
   1413        mozilla::Some(size_t(aOptions.mOfferToReceiveAudio.Value()));
   1414  }
   1415 
   1416  if (aOptions.mOfferToReceiveVideo.WasPassed()) {
   1417    options.mOfferToReceiveVideo =
   1418        mozilla::Some(size_t(aOptions.mOfferToReceiveVideo.Value()));
   1419  }
   1420 
   1421  options.mIceRestart = mozilla::Some(aOptions.mIceRestart ||
   1422                                      !mLocalIceCredentialsToReplace.empty());
   1423 
   1424  return CreateOffer(options);
   1425 }
   1426 
   1427 static void DeferredCreateOffer(const std::string& aPcHandle,
   1428                                const JsepOfferOptions& aOptions) {
   1429  PeerConnectionWrapper wrapper(aPcHandle);
   1430 
   1431  if (wrapper.impl()) {
   1432    if (!PeerConnectionCtx::GetInstance()->isReady()) {
   1433      MOZ_CRASH(
   1434          "Why is DeferredCreateOffer being executed when the "
   1435          "PeerConnectionCtx isn't ready?");
   1436    }
   1437    wrapper.impl()->CreateOffer(aOptions);
   1438  }
   1439 }
   1440 
   1441 // Have to use unique_ptr because webidl enums are generated without a
   1442 // copy c'tor.
   1443 static std::unique_ptr<dom::PCErrorData> buildJSErrorData(
   1444    const JsepSession::Result& aResult, const std::string& aMessage) {
   1445  std::unique_ptr<dom::PCErrorData> result(new dom::PCErrorData);
   1446  result->mName = *aResult.mError;
   1447  result->mMessage = NS_ConvertASCIItoUTF16(aMessage.c_str());
   1448  return result;
   1449 }
   1450 
   1451 // Used by unit tests and the IDL CreateOffer.
   1452 NS_IMETHODIMP
   1453 PeerConnectionImpl::CreateOffer(const JsepOfferOptions& aOptions) {
   1454  PC_AUTO_ENTER_API_CALL(true);
   1455 
   1456  if (!PeerConnectionCtx::GetInstance()->isReady()) {
   1457    // Uh oh. We're not ready yet. Enqueue this operation.
   1458    PeerConnectionCtx::GetInstance()->queueJSEPOperation(
   1459        WrapRunnableNM(DeferredCreateOffer, mHandle, aOptions));
   1460    STAMP_TIMECARD(mTimeCard, "Deferring CreateOffer (not ready)");
   1461    return NS_OK;
   1462  }
   1463 
   1464  CSFLogDebug(LOGTAG, "CreateOffer()");
   1465  STAMP_TIMECARD(mTimeCard, "Create Offer");
   1466 
   1467  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1468      __func__, [this, self = RefPtr<PeerConnectionImpl>(this), aOptions] {
   1469        std::string offer;
   1470 
   1471        SyncToJsep();
   1472        UniquePtr<JsepSession> uncommittedJsepSession(mJsepSession->Clone());
   1473        JsepSession::Result result =
   1474            uncommittedJsepSession->CreateOffer(aOptions, &offer);
   1475        JSErrorResult rv;
   1476        if (result.mError.isSome()) {
   1477          std::string errorString = uncommittedJsepSession->GetLastError();
   1478 
   1479          CSFLogError(LOGTAG, "%s: pc = %s, error = %s", __FUNCTION__,
   1480                      mHandle.c_str(), errorString.c_str());
   1481 
   1482          mPCObserver->OnCreateOfferError(
   1483              *buildJSErrorData(result, errorString), rv);
   1484        } else {
   1485          mJsepSession = std::move(uncommittedJsepSession);
   1486          mPCObserver->OnCreateOfferSuccess(ObString(offer.c_str()), rv);
   1487        }
   1488      }));
   1489 
   1490  return NS_OK;
   1491 }
   1492 
   1493 NS_IMETHODIMP
   1494 PeerConnectionImpl::CreateAnswer() {
   1495  PC_AUTO_ENTER_API_CALL(true);
   1496 
   1497  CSFLogDebug(LOGTAG, "CreateAnswer()");
   1498 
   1499  STAMP_TIMECARD(mTimeCard, "Create Answer");
   1500  // TODO(bug 1098015): Once RTCAnswerOptions is standardized, we'll need to
   1501  // add it as a param to CreateAnswer, and convert it here.
   1502  JsepAnswerOptions options;
   1503 
   1504  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1505      __func__, [this, self = RefPtr<PeerConnectionImpl>(this), options] {
   1506        std::string answer;
   1507        SyncToJsep();
   1508        UniquePtr<JsepSession> uncommittedJsepSession(mJsepSession->Clone());
   1509        JsepSession::Result result =
   1510            uncommittedJsepSession->CreateAnswer(options, &answer);
   1511        JSErrorResult rv;
   1512        if (result.mError.isSome()) {
   1513          std::string errorString = uncommittedJsepSession->GetLastError();
   1514 
   1515          CSFLogError(LOGTAG, "%s: pc = %s, error = %s", __FUNCTION__,
   1516                      mHandle.c_str(), errorString.c_str());
   1517 
   1518          mPCObserver->OnCreateAnswerError(
   1519              *buildJSErrorData(result, errorString), rv);
   1520        } else {
   1521          mJsepSession = std::move(uncommittedJsepSession);
   1522          mPCObserver->OnCreateAnswerSuccess(ObString(answer.c_str()), rv);
   1523        }
   1524      }));
   1525 
   1526  return NS_OK;
   1527 }
   1528 
   1529 dom::RTCSdpType ToDomSdpType(JsepSdpType aType) {
   1530  switch (aType) {
   1531    case kJsepSdpOffer:
   1532      return dom::RTCSdpType::Offer;
   1533    case kJsepSdpAnswer:
   1534      return dom::RTCSdpType::Answer;
   1535    case kJsepSdpPranswer:
   1536      return dom::RTCSdpType::Pranswer;
   1537    case kJsepSdpRollback:
   1538      return dom::RTCSdpType::Rollback;
   1539  }
   1540 
   1541  MOZ_CRASH("Nonexistent JsepSdpType");
   1542 }
   1543 
   1544 JsepSdpType ToJsepSdpType(dom::RTCSdpType aType) {
   1545  switch (aType) {
   1546    case dom::RTCSdpType::Offer:
   1547      return kJsepSdpOffer;
   1548    case dom::RTCSdpType::Pranswer:
   1549      return kJsepSdpPranswer;
   1550    case dom::RTCSdpType::Answer:
   1551      return kJsepSdpAnswer;
   1552    case dom::RTCSdpType::Rollback:
   1553      return kJsepSdpRollback;
   1554  }
   1555 
   1556  MOZ_CRASH("Nonexistent dom::RTCSdpType");
   1557 }
   1558 
   1559 NS_IMETHODIMP
   1560 PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
   1561  PC_AUTO_ENTER_API_CALL(true);
   1562 
   1563  if (!aSDP) {
   1564    CSFLogError(LOGTAG, "%s - aSDP is NULL", __FUNCTION__);
   1565    return NS_ERROR_FAILURE;
   1566  }
   1567 
   1568  STAMP_TIMECARD(mTimeCard, "Set Local Description");
   1569 
   1570  if (AnyLocalTrackHasPeerIdentity()) {
   1571    mRequestedPrivacy = Some(PrincipalPrivacy::Private);
   1572  }
   1573 
   1574  mozilla::dom::RTCSdpHistoryEntryInternal sdpEntry;
   1575  sdpEntry.mIsLocal = true;
   1576  sdpEntry.mTimestamp = mTimestampMaker.GetNow().ToDom();
   1577  sdpEntry.mSdp = NS_ConvertASCIItoUTF16(aSDP);
   1578  auto appendHistory = [&]() {
   1579    if (!mSdpHistory.AppendElement(sdpEntry, fallible)) {
   1580      mozalloc_handle_oom(0);
   1581    }
   1582  };
   1583 
   1584  mLocalRequestedSDP = aSDP;
   1585 
   1586  SyncToJsep();
   1587 
   1588  bool wasRestartingIce = mJsepSession->IsIceRestarting();
   1589  JsepSdpType sdpType;
   1590  switch (aAction) {
   1591    case IPeerConnection::kActionOffer:
   1592      sdpType = mozilla::kJsepSdpOffer;
   1593      break;
   1594    case IPeerConnection::kActionAnswer:
   1595      sdpType = mozilla::kJsepSdpAnswer;
   1596      break;
   1597    case IPeerConnection::kActionPRAnswer:
   1598      sdpType = mozilla::kJsepSdpPranswer;
   1599      break;
   1600    case IPeerConnection::kActionRollback:
   1601      sdpType = mozilla::kJsepSdpRollback;
   1602      break;
   1603    default:
   1604      MOZ_ASSERT(false);
   1605      appendHistory();
   1606      return NS_ERROR_FAILURE;
   1607  }
   1608  MOZ_ASSERT(!mUncommittedJsepSession);
   1609  mUncommittedJsepSession.reset(mJsepSession->Clone());
   1610  JsepSession::Result result =
   1611      mUncommittedJsepSession->SetLocalDescription(sdpType, mLocalRequestedSDP);
   1612  JSErrorResult rv;
   1613  if (result.mError.isSome()) {
   1614    std::string errorString = mUncommittedJsepSession->GetLastError();
   1615    mUncommittedJsepSession = nullptr;
   1616    CSFLogError(LOGTAG, "%s: pc = %s, error = %s", __FUNCTION__,
   1617                mHandle.c_str(), errorString.c_str());
   1618    mPCObserver->OnSetDescriptionError(*buildJSErrorData(result, errorString),
   1619                                       rv);
   1620    sdpEntry.mErrors = GetLastSdpParsingErrors();
   1621  } else {
   1622    if (wasRestartingIce) {
   1623      RecordIceRestartStatistics(sdpType);
   1624    }
   1625 
   1626    mPCObserver->OnSetDescriptionSuccess(rv);
   1627  }
   1628 
   1629  appendHistory();
   1630 
   1631  if (rv.Failed()) {
   1632    return rv.StealNSResult();
   1633  }
   1634 
   1635  return NS_OK;
   1636 }
   1637 
   1638 static void DeferredSetRemote(const std::string& aPcHandle, int32_t aAction,
   1639                              const std::string& aSdp) {
   1640  PeerConnectionWrapper wrapper(aPcHandle);
   1641 
   1642  if (wrapper.impl()) {
   1643    if (!PeerConnectionCtx::GetInstance()->isReady()) {
   1644      MOZ_CRASH(
   1645          "Why is DeferredSetRemote being executed when the "
   1646          "PeerConnectionCtx isn't ready?");
   1647    }
   1648    wrapper.impl()->SetRemoteDescription(aAction, aSdp.c_str());
   1649  }
   1650 }
   1651 
   1652 NS_IMETHODIMP
   1653 PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) {
   1654  PC_AUTO_ENTER_API_CALL(true);
   1655 
   1656  if (!aSDP) {
   1657    CSFLogError(LOGTAG, "%s - aSDP is NULL", __FUNCTION__);
   1658    return NS_ERROR_FAILURE;
   1659  }
   1660 
   1661  if (action == IPeerConnection::kActionOffer) {
   1662    if (!PeerConnectionCtx::GetInstance()->isReady()) {
   1663      // Uh oh. We're not ready yet. Enqueue this operation. (This must be a
   1664      // remote offer, or else we would not have gotten this far)
   1665      PeerConnectionCtx::GetInstance()->queueJSEPOperation(WrapRunnableNM(
   1666          DeferredSetRemote, mHandle, action, std::string(aSDP)));
   1667      STAMP_TIMECARD(mTimeCard, "Deferring SetRemote (not ready)");
   1668      return NS_OK;
   1669    }
   1670  }
   1671 
   1672  STAMP_TIMECARD(mTimeCard, "Set Remote Description");
   1673 
   1674  mozilla::dom::RTCSdpHistoryEntryInternal sdpEntry;
   1675  sdpEntry.mIsLocal = false;
   1676  sdpEntry.mTimestamp = mTimestampMaker.GetNow().ToDom();
   1677  sdpEntry.mSdp = NS_ConvertASCIItoUTF16(aSDP);
   1678  auto appendHistory = [&]() {
   1679    if (!mSdpHistory.AppendElement(sdpEntry, fallible)) {
   1680      mozalloc_handle_oom(0);
   1681    }
   1682  };
   1683 
   1684  SyncToJsep();
   1685 
   1686  mRemoteRequestedSDP = aSDP;
   1687  bool wasRestartingIce = mJsepSession->IsIceRestarting();
   1688  JsepSdpType sdpType;
   1689  switch (action) {
   1690    case IPeerConnection::kActionOffer:
   1691      sdpType = mozilla::kJsepSdpOffer;
   1692      break;
   1693    case IPeerConnection::kActionAnswer:
   1694      sdpType = mozilla::kJsepSdpAnswer;
   1695      break;
   1696    case IPeerConnection::kActionPRAnswer:
   1697      sdpType = mozilla::kJsepSdpPranswer;
   1698      break;
   1699    case IPeerConnection::kActionRollback:
   1700      sdpType = mozilla::kJsepSdpRollback;
   1701      break;
   1702    default:
   1703      MOZ_ASSERT(false);
   1704      return NS_ERROR_FAILURE;
   1705  }
   1706 
   1707  MOZ_ASSERT(!mUncommittedJsepSession);
   1708  mUncommittedJsepSession.reset(mJsepSession->Clone());
   1709  JsepSession::Result result = mUncommittedJsepSession->SetRemoteDescription(
   1710      sdpType, mRemoteRequestedSDP);
   1711  JSErrorResult jrv;
   1712  if (result.mError.isSome()) {
   1713    std::string errorString = mUncommittedJsepSession->GetLastError();
   1714    mUncommittedJsepSession = nullptr;
   1715    sdpEntry.mErrors = GetLastSdpParsingErrors();
   1716    CSFLogError(LOGTAG, "%s: pc = %s, error = %s", __FUNCTION__,
   1717                mHandle.c_str(), errorString.c_str());
   1718    mPCObserver->OnSetDescriptionError(*buildJSErrorData(result, errorString),
   1719                                       jrv);
   1720  } else {
   1721    if (wasRestartingIce) {
   1722      RecordIceRestartStatistics(sdpType);
   1723    }
   1724 
   1725    mPCObserver->OnSetDescriptionSuccess(jrv);
   1726  }
   1727 
   1728  appendHistory();
   1729 
   1730  if (jrv.Failed()) {
   1731    return jrv.StealNSResult();
   1732  }
   1733 
   1734  return NS_OK;
   1735 }
   1736 
   1737 already_AddRefed<dom::Promise> PeerConnectionImpl::GetStats(
   1738    MediaStreamTrack* aSelector) {
   1739  if (!mWindow) {
   1740    MOZ_CRASH("Cannot create a promise without a window!");
   1741  }
   1742 
   1743  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
   1744  ErrorResult rv;
   1745  RefPtr<Promise> promise = Promise::Create(global, rv);
   1746  if (NS_WARN_IF(rv.Failed())) {
   1747    MOZ_CRASH("Failed to create a promise!");
   1748  }
   1749 
   1750  if (!IsClosed()) {
   1751    GetStats(aSelector, false)
   1752        ->Then(
   1753            GetMainThreadSerialEventTarget(), __func__,
   1754            [promise, window = mWindow](
   1755                UniquePtr<dom::RTCStatsReportInternal>&& aReport) {
   1756              RefPtr<RTCStatsReport> report(new RTCStatsReport(window));
   1757              report->Incorporate(*aReport);
   1758              promise->MaybeResolve(std::move(report));
   1759            },
   1760            [promise, window = mWindow](nsresult aError) {
   1761              RefPtr<RTCStatsReport> report(new RTCStatsReport(window));
   1762              promise->MaybeResolve(std::move(report));
   1763            });
   1764  } else {
   1765    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1766  }
   1767 
   1768  return promise.forget();
   1769 }
   1770 
   1771 void PeerConnectionImpl::GetRemoteStreams(
   1772    nsTArray<RefPtr<DOMMediaStream>>& aStreamsOut) const {
   1773  aStreamsOut = mReceiveStreams.Clone();
   1774 }
   1775 
   1776 NS_IMETHODIMP
   1777 PeerConnectionImpl::AddIceCandidate(
   1778    const char* aCandidate, const char* aMid, const char* aUfrag,
   1779    const dom::Nullable<unsigned short>& aLevel) {
   1780  PC_AUTO_ENTER_API_CALL(true);
   1781 
   1782  if (mForceIceTcp &&
   1783      std::string::npos != std::string(aCandidate).find(" UDP ")) {
   1784    CSFLogError(LOGTAG, "Blocking remote UDP candidate: %s", aCandidate);
   1785    return NS_OK;
   1786  }
   1787 
   1788  STAMP_TIMECARD(mTimeCard, "Add Ice Candidate");
   1789 
   1790  CSFLogDebug(LOGTAG, "AddIceCandidate: %s %s", aCandidate, aUfrag);
   1791 
   1792  std::string transportId;
   1793  Maybe<unsigned short> level;
   1794  if (!aLevel.IsNull()) {
   1795    level = Some(aLevel.Value());
   1796  }
   1797  MOZ_DIAGNOSTIC_ASSERT(
   1798      !mUncommittedJsepSession,
   1799      "AddIceCandidate is chained, which means it should never "
   1800      "run while an sRD/sLD is in progress");
   1801  JsepSession::Result result = mJsepSession->AddRemoteIceCandidate(
   1802      aCandidate, aMid, level, aUfrag, &transportId);
   1803 
   1804  if (!result.mError.isSome()) {
   1805    // We do not bother the MediaTransportHandler about this before
   1806    // offer/answer concludes.  Once offer/answer concludes, we will extract
   1807    // these candidates from the remote SDP.
   1808    if (mSignalingState == RTCSignalingState::Stable && !transportId.empty()) {
   1809      AddIceCandidate(aCandidate, transportId, aUfrag);
   1810      mRawTrickledCandidates.push_back(aCandidate);
   1811    }
   1812    // Spec says we queue a task for these updates
   1813    GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1814        __func__, [this, self = RefPtr<PeerConnectionImpl>(this)] {
   1815          if (IsClosed()) {
   1816            return;
   1817          }
   1818          mPendingRemoteDescription =
   1819              mJsepSession->GetRemoteDescription(kJsepDescriptionPending);
   1820          mCurrentRemoteDescription =
   1821              mJsepSession->GetRemoteDescription(kJsepDescriptionCurrent);
   1822          JSErrorResult rv;
   1823          mPCObserver->OnAddIceCandidateSuccess(rv);
   1824        }));
   1825  } else {
   1826    std::string errorString = mJsepSession->GetLastError();
   1827 
   1828    CSFLogError(LOGTAG,
   1829                "Failed to incorporate remote candidate into SDP:"
   1830                " res = %u, candidate = %s, level = %i, error = %s",
   1831                static_cast<unsigned>(*result.mError), aCandidate,
   1832                level.valueOr(-1), errorString.c_str());
   1833 
   1834    GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1835        __func__,
   1836        [this, self = RefPtr<PeerConnectionImpl>(this), errorString, result] {
   1837          if (IsClosed()) {
   1838            return;
   1839          }
   1840          JSErrorResult rv;
   1841          mPCObserver->OnAddIceCandidateError(
   1842              *buildJSErrorData(result, errorString), rv);
   1843        }));
   1844  }
   1845 
   1846  return NS_OK;
   1847 }
   1848 
   1849 NS_IMETHODIMP
   1850 PeerConnectionImpl::CloseStreams() {
   1851  PC_AUTO_ENTER_API_CALL(false);
   1852 
   1853  return NS_OK;
   1854 }
   1855 
   1856 NS_IMETHODIMP
   1857 PeerConnectionImpl::SetPeerIdentity(const nsAString& aPeerIdentity) {
   1858  PC_AUTO_ENTER_API_CALL(true);
   1859  MOZ_ASSERT(!aPeerIdentity.IsEmpty());
   1860 
   1861  // once set, this can't be changed
   1862  if (mPeerIdentity) {
   1863    if (!mPeerIdentity->Equals(aPeerIdentity)) {
   1864      return NS_ERROR_FAILURE;
   1865    }
   1866  } else {
   1867    mPeerIdentity = new PeerIdentity(aPeerIdentity);
   1868    Document* doc = mWindow->GetExtantDoc();
   1869    if (!doc) {
   1870      CSFLogInfo(LOGTAG, "Can't update principal on streams; document gone");
   1871      return NS_ERROR_FAILURE;
   1872    }
   1873    for (const auto& transceiver : mTransceivers) {
   1874      transceiver->Sender()->GetPipeline()->UpdateSinkIdentity(
   1875          doc->NodePrincipal(), mPeerIdentity);
   1876    }
   1877  }
   1878  return NS_OK;
   1879 }
   1880 
   1881 nsresult PeerConnectionImpl::OnAlpnNegotiated(const std::string& aAlpn,
   1882                                              bool aPrivacyRequested) {
   1883  PC_AUTO_ENTER_API_CALL(false);
   1884  MOZ_DIAGNOSTIC_ASSERT(!mRequestedPrivacy ||
   1885                        (*mRequestedPrivacy == PrincipalPrivacy::Private) ==
   1886                            aPrivacyRequested);
   1887  (void)aAlpn;
   1888 
   1889  mRequestedPrivacy = Some(aPrivacyRequested ? PrincipalPrivacy::Private
   1890                                             : PrincipalPrivacy::NonPrivate);
   1891  // This updates the MediaPipelines with a private PrincipalHandle. Note that
   1892  // MediaPipelineReceive has its own AlpnNegotiated handler so it can get
   1893  // signaled off-main to drop data until it receives the new PrincipalHandle
   1894  // from us.
   1895  UpdateMediaPipelines();
   1896  return NS_OK;
   1897 }
   1898 
   1899 void PeerConnectionImpl::OnDtlsStateChange(const std::string& aTransportId,
   1900                                           TransportLayer::State aState) {
   1901  nsCString key(aTransportId.data(), aTransportId.size());
   1902  RefPtr<RTCDtlsTransport> dtlsTransport =
   1903      mTransportIdToRTCDtlsTransport.Get(key);
   1904  if (!dtlsTransport) {
   1905    return;
   1906  }
   1907 
   1908  dtlsTransport->UpdateState(aState);
   1909  // Whenever the state of an RTCDtlsTransport changes or when the [[IsClosed]]
   1910  // slot turns true, the user agent MUST update the connection state by
   1911  // queueing a task that runs the following steps:
   1912  // NOTE: The business about [[IsClosed]] here is probably a bug, because the
   1913  // rest of the spec makes it very clear that events should never fire when
   1914  // [[IsClosed]] is true. See https://github.com/w3c/webrtc-pc/issues/2865
   1915  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   1916      __func__, [this, self = RefPtr<PeerConnectionImpl>(this)] {
   1917        // Let connection be this RTCPeerConnection object.
   1918        // Let newState be the value of deriving a new state value as described
   1919        // by the RTCPeerConnectionState enum.
   1920        // If connection.[[ConnectionState]] is equal to newState, abort these
   1921        // steps.
   1922        // Set connection.[[ConnectionState]] to newState.
   1923        if (UpdateConnectionState()) {
   1924          // Fire an event named connectionstatechange at connection.
   1925          JSErrorResult jrv;
   1926          mPCObserver->OnStateChange(PCObserverStateType::ConnectionState, jrv);
   1927        }
   1928      }));
   1929 }
   1930 
   1931 RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const {
   1932  // closed 	The RTCPeerConnection object's [[IsClosed]] slot is true.
   1933  if (IsClosed()) {
   1934    return RTCPeerConnectionState::Closed;
   1935  }
   1936 
   1937  // Would use a bitset, but that requires lots of static_cast<size_t>
   1938  // Oh well.
   1939  std::set<RTCDtlsTransportState> statesFound;
   1940  std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports());
   1941  for (const auto& transport : transports) {
   1942    statesFound.insert(transport->State());
   1943  }
   1944 
   1945  // failed 	The previous state doesn't apply, and either
   1946  // [[IceConnectionState]] is "failed" or any RTCDtlsTransports are in the
   1947  // "failed" state.
   1948  if (mIceConnectionState == RTCIceConnectionState::Failed ||
   1949      statesFound.count(RTCDtlsTransportState::Failed)) {
   1950    return RTCPeerConnectionState::Failed;
   1951  }
   1952 
   1953  // disconnected 	None of the previous states apply, and
   1954  // [[IceConnectionState]] is "disconnected".
   1955  if (mIceConnectionState == RTCIceConnectionState::Disconnected) {
   1956    return RTCPeerConnectionState::Disconnected;
   1957  }
   1958 
   1959  // new 	None of the previous states apply, and either
   1960  // [[IceConnectionState]] is "new", and all RTCDtlsTransports are in the
   1961  // "new" or "closed" state...
   1962  if (mIceConnectionState == RTCIceConnectionState::New &&
   1963      !statesFound.count(RTCDtlsTransportState::Connecting) &&
   1964      !statesFound.count(RTCDtlsTransportState::Connected) &&
   1965      !statesFound.count(RTCDtlsTransportState::Failed)) {
   1966    return RTCPeerConnectionState::New;
   1967  }
   1968 
   1969  // ...or there are no transports.
   1970  if (statesFound.empty()) {
   1971    return RTCPeerConnectionState::New;
   1972  }
   1973 
   1974  // connected 	None of the previous states apply,
   1975  // [[IceConnectionState]] is "connected", and all RTCDtlsTransports are in
   1976  // the "connected" or "closed" state.
   1977  if (mIceConnectionState == RTCIceConnectionState::Connected &&
   1978      !statesFound.count(RTCDtlsTransportState::New) &&
   1979      !statesFound.count(RTCDtlsTransportState::Failed) &&
   1980      !statesFound.count(RTCDtlsTransportState::Connecting)) {
   1981    return RTCPeerConnectionState::Connected;
   1982  }
   1983 
   1984  // connecting 	None of the previous states apply.
   1985  return RTCPeerConnectionState::Connecting;
   1986 }
   1987 
   1988 bool PeerConnectionImpl::UpdateConnectionState() {
   1989  auto newState = GetNewConnectionState();
   1990  if (newState != mConnectionState) {
   1991    CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__,
   1992               static_cast<int>(mConnectionState), static_cast<int>(newState),
   1993               this);
   1994    mConnectionState = newState;
   1995    if (mConnectionState != RTCPeerConnectionState::Closed) {
   1996      return true;
   1997    }
   1998  }
   1999 
   2000  return false;
   2001 }
   2002 
   2003 void PeerConnectionImpl::OnMediaError(const std::string& aError) {
   2004  CSFLogError(LOGTAG, "Encountered media error! %s", aError.c_str());
   2005  // TODO: Let content know about this somehow.
   2006 }
   2007 
   2008 void PeerConnectionImpl::DumpPacket_m(size_t level, dom::mozPacketDumpType type,
   2009                                      bool sending,
   2010                                      UniquePtr<uint8_t[]>& packet,
   2011                                      size_t size) {
   2012  if (IsClosed()) {
   2013    return;
   2014  }
   2015 
   2016  // TODO: Is this efficient? Should we try grabbing our JS ctx from somewhere
   2017  // else?
   2018  AutoJSAPI jsapi;
   2019  if (!jsapi.Init(mWindow)) {
   2020    return;
   2021  }
   2022 
   2023  UniquePtr<void, JS::FreePolicy> packetPtr{packet.release()};
   2024  JS::Rooted<JSObject*> jsobj(
   2025      jsapi.cx(),
   2026      JS::NewArrayBufferWithContents(jsapi.cx(), size, std::move(packetPtr)));
   2027 
   2028  RootedSpiderMonkeyInterface<ArrayBuffer> arrayBuffer(jsapi.cx());
   2029  if (!arrayBuffer.Init(jsobj)) {
   2030    return;
   2031  }
   2032 
   2033  JSErrorResult jrv;
   2034  mPCObserver->OnPacket(level, type, sending, arrayBuffer, jrv);
   2035 }
   2036 
   2037 nsresult PeerConnectionImpl::EnablePacketDump(unsigned long level,
   2038                                              dom::mozPacketDumpType type,
   2039                                              bool sending) {
   2040  return GetPacketDumper()->EnablePacketDump(level, type, sending);
   2041 }
   2042 
   2043 nsresult PeerConnectionImpl::DisablePacketDump(unsigned long level,
   2044                                               dom::mozPacketDumpType type,
   2045                                               bool sending) {
   2046  return GetPacketDumper()->DisablePacketDump(level, type, sending);
   2047 }
   2048 
   2049 void PeerConnectionImpl::StampTimecard(const char* aEvent) {
   2050  MOZ_ASSERT(NS_IsMainThread());
   2051  STAMP_TIMECARD(mTimeCard, aEvent);
   2052 }
   2053 
   2054 void PeerConnectionImpl::SendWarningToConsole(const nsCString& aWarning) {
   2055  nsAutoString msg = NS_ConvertASCIItoUTF16(aWarning);
   2056  nsContentUtils::ReportToConsoleByWindowID(msg, nsIScriptError::warningFlag,
   2057                                            "WebRTC"_ns, mWindow->WindowID());
   2058 }
   2059 
   2060 void PeerConnectionImpl::GetDefaultVideoCodecs(
   2061    std::vector<UniquePtr<JsepCodecDescription>>& aSupportedCodecs,
   2062    const OverrideRtxPreference aOverrideRtxPreference) {
   2063  const auto prefs = GetDefaultCodecPreferences(aOverrideRtxPreference);
   2064  // Supported video codecs.
   2065  // Note: order here implies priority for building offers!
   2066  aSupportedCodecs.emplace_back(
   2067      JsepVideoCodecDescription::CreateDefaultVP8(prefs));
   2068  aSupportedCodecs.emplace_back(
   2069      JsepVideoCodecDescription::CreateDefaultVP9(prefs));
   2070  aSupportedCodecs.emplace_back(
   2071      JsepVideoCodecDescription::CreateDefaultH264_1(prefs));
   2072  aSupportedCodecs.emplace_back(
   2073      JsepVideoCodecDescription::CreateDefaultH264_0(prefs));
   2074  aSupportedCodecs.emplace_back(
   2075      JsepVideoCodecDescription::CreateDefaultH264Baseline_1(prefs));
   2076  aSupportedCodecs.emplace_back(
   2077      JsepVideoCodecDescription::CreateDefaultH264Baseline_0(prefs));
   2078  aSupportedCodecs.emplace_back(
   2079      JsepVideoCodecDescription::CreateDefaultAV1(prefs));
   2080 
   2081  aSupportedCodecs.emplace_back(
   2082      JsepVideoCodecDescription::CreateDefaultUlpFec(prefs));
   2083  aSupportedCodecs.emplace_back(
   2084      JsepApplicationCodecDescription::CreateDefault());
   2085  aSupportedCodecs.emplace_back(
   2086      JsepVideoCodecDescription::CreateDefaultRed(prefs));
   2087 
   2088  CompareCodecPriority comparator;
   2089  std::stable_sort(aSupportedCodecs.begin(), aSupportedCodecs.end(),
   2090                   comparator);
   2091 }
   2092 
   2093 void PeerConnectionImpl::GetDefaultAudioCodecs(
   2094    std::vector<UniquePtr<JsepCodecDescription>>& aSupportedCodecs) {
   2095  const auto prefs = GetDefaultCodecPreferences();
   2096  aSupportedCodecs.emplace_back(
   2097      JsepAudioCodecDescription::CreateDefaultOpus(prefs));
   2098  aSupportedCodecs.emplace_back(JsepAudioCodecDescription::CreateDefaultG722());
   2099  aSupportedCodecs.emplace_back(JsepAudioCodecDescription::CreateDefaultPCMU());
   2100  aSupportedCodecs.emplace_back(JsepAudioCodecDescription::CreateDefaultPCMA());
   2101  aSupportedCodecs.emplace_back(
   2102      JsepAudioCodecDescription::CreateDefaultTelephoneEvent());
   2103 }
   2104 
   2105 void PeerConnectionImpl::GetDefaultRtpExtensions(
   2106    std::vector<RtpExtensionHeader>& aRtpExtensions) {
   2107  RtpExtensionHeader audioLevel = {JsepMediaType::kAudio,
   2108                                   SdpDirectionAttribute::Direction::kSendrecv,
   2109                                   webrtc::RtpExtension::kAudioLevelUri};
   2110  aRtpExtensions.push_back(audioLevel);
   2111 
   2112  RtpExtensionHeader csrcAudioLevels = {
   2113      JsepMediaType::kAudio, SdpDirectionAttribute::Direction::kRecvonly,
   2114      webrtc::RtpExtension::kCsrcAudioLevelsUri};
   2115  aRtpExtensions.push_back(csrcAudioLevels);
   2116 
   2117  RtpExtensionHeader mid = {JsepMediaType::kAudioVideo,
   2118                            SdpDirectionAttribute::Direction::kSendrecv,
   2119                            webrtc::RtpExtension::kMidUri};
   2120  aRtpExtensions.push_back(mid);
   2121 
   2122  RtpExtensionHeader absSendTime = {JsepMediaType::kVideo,
   2123                                    SdpDirectionAttribute::Direction::kSendrecv,
   2124                                    webrtc::RtpExtension::kAbsSendTimeUri};
   2125  aRtpExtensions.push_back(absSendTime);
   2126 
   2127  RtpExtensionHeader timestampOffset = {
   2128      JsepMediaType::kVideo, SdpDirectionAttribute::Direction::kSendrecv,
   2129      webrtc::RtpExtension::kTimestampOffsetUri};
   2130  aRtpExtensions.push_back(timestampOffset);
   2131 
   2132  RtpExtensionHeader playoutDelay = {
   2133      JsepMediaType::kVideo, SdpDirectionAttribute::Direction::kRecvonly,
   2134      webrtc::RtpExtension::kPlayoutDelayUri};
   2135  aRtpExtensions.push_back(playoutDelay);
   2136 
   2137  RtpExtensionHeader transportSequenceNumber = {
   2138      JsepMediaType::kVideo, SdpDirectionAttribute::Direction::kSendrecv,
   2139      webrtc::RtpExtension::kTransportSequenceNumberUri};
   2140  aRtpExtensions.push_back(transportSequenceNumber);
   2141 }
   2142 
   2143 void PeerConnectionImpl::GetCapabilities(
   2144    const nsAString& aKind, dom::Nullable<dom::RTCRtpCapabilities>& aResult,
   2145    sdp::Direction aDirection) {
   2146  std::vector<UniquePtr<JsepCodecDescription>> codecs;
   2147  std::vector<RtpExtensionHeader> headers;
   2148  auto mediaType = JsepMediaType::kNone;
   2149 
   2150  if (aKind.EqualsASCII("video")) {
   2151    // Note to reviewers, this forced RTX to true.
   2152    // RTX is supported by default, so I am not sure if that was necessary.
   2153    // When it has been explicitly disabled by pref, is there a point in
   2154    // forcing it here?
   2155    GetDefaultVideoCodecs(codecs, OverrideRtxPreference::NoOverride);
   2156    mediaType = JsepMediaType::kVideo;
   2157  } else if (aKind.EqualsASCII("audio")) {
   2158    GetDefaultAudioCodecs(codecs);
   2159    mediaType = JsepMediaType::kAudio;
   2160  } else {
   2161    return;
   2162  }
   2163 
   2164  GetDefaultRtpExtensions(headers);
   2165 
   2166  bool haveAddedRtx = false;
   2167 
   2168  // Use the codecs for kind to fill out the RTCRtpCodec
   2169  for (const auto& codec : codecs) {
   2170    // To avoid misleading information on codec capabilities skip:
   2171    // - Any disabled by pref
   2172    // - Recvonly codecs for send capabilities -- we have no sendonly codecs
   2173    // - Those not signaled for audio/video (webrtc-datachannel)
   2174    if (!codec->mEnabled || !codec->DirectionSupported(aDirection) ||
   2175        codec->mName == "webrtc-datachannel") {
   2176      continue;
   2177    }
   2178 
   2179    dom::RTCRtpCodec capability;
   2180    RTCRtpTransceiver::ToDomRtpCodec(*codec, &capability);
   2181 
   2182    if (!aResult.SetValue().mCodecs.AppendElement(capability, fallible)) {
   2183      mozalloc_handle_oom(0);
   2184    }
   2185 
   2186    // We need to manually add rtx for video.
   2187    // Spec says: There will only be a single entry in codecs for
   2188    // retransmission via RTX, with sdpFmtpLine not present.
   2189    if (mediaType == JsepMediaType::kVideo && !haveAddedRtx) {
   2190      const JsepVideoCodecDescription& videoCodec =
   2191          static_cast<JsepVideoCodecDescription&>(*codec);
   2192      if (videoCodec.mRtxEnabled) {
   2193        dom::RTCRtpCodec rtx;
   2194        RTCRtpTransceiver::ToDomRtpCodecRtx(videoCodec, &rtx);
   2195        rtx.mSdpFmtpLine.Reset();
   2196        if (!aResult.SetValue().mCodecs.AppendElement(rtx, fallible)) {
   2197          mozalloc_handle_oom(0);
   2198        }
   2199        haveAddedRtx = true;
   2200      }
   2201    }
   2202  }
   2203 
   2204  // Add headers that match the direction and media type requested.
   2205  for (const auto& header : headers) {
   2206    if ((header.direction & aDirection) && (header.mMediaType & mediaType)) {
   2207      dom::RTCRtpHeaderExtensionCapability rtpHeader;
   2208      rtpHeader.mUri.AssignASCII(header.extensionname);
   2209      if (!aResult.SetValue().mHeaderExtensions.AppendElement(rtpHeader,
   2210                                                              fallible)) {
   2211        mozalloc_handle_oom(0);
   2212      }
   2213    }
   2214  }
   2215 }
   2216 
   2217 void PeerConnectionImpl::SetupPreferredCodecs(
   2218    std::vector<UniquePtr<JsepCodecDescription>>& aPreferredCodecs) {
   2219  GetDefaultVideoCodecs(aPreferredCodecs, OverrideRtxPreference::NoOverride);
   2220  GetDefaultAudioCodecs(aPreferredCodecs);
   2221 }
   2222 
   2223 void PeerConnectionImpl::SetupPreferredRtpExtensions(
   2224    std::vector<RtpExtensionHeader>& aPreferredheaders) {
   2225  GetDefaultRtpExtensions(aPreferredheaders);
   2226 
   2227  if (!Preferences::GetBool("media.navigator.video.use_transport_cc", false)) {
   2228    aPreferredheaders.erase(
   2229        std::remove_if(
   2230            aPreferredheaders.begin(), aPreferredheaders.end(),
   2231            [&](const RtpExtensionHeader& header) {
   2232              return header.extensionname ==
   2233                     webrtc::RtpExtension::kTransportSequenceNumberUri;
   2234            }),
   2235        aPreferredheaders.end());
   2236  }
   2237 }
   2238 
   2239 nsresult PeerConnectionImpl::CalculateFingerprint(
   2240    const nsACString& algorithm, std::vector<uint8_t>* fingerprint) const {
   2241  DtlsDigest digest(algorithm);
   2242 
   2243  MOZ_ASSERT(fingerprint);
   2244  const UniqueCERTCertificate& cert = mCertificate->Certificate();
   2245  nsresult rv = DtlsIdentity::ComputeFingerprint(cert, &digest);
   2246  if (NS_FAILED(rv)) {
   2247    CSFLogError(LOGTAG, "Unable to calculate certificate fingerprint, rv=%u",
   2248                static_cast<unsigned>(rv));
   2249    return rv;
   2250  }
   2251  *fingerprint = digest.value_;
   2252  return NS_OK;
   2253 }
   2254 
   2255 NS_IMETHODIMP
   2256 PeerConnectionImpl::GetFingerprint(char** fingerprint) {
   2257  MOZ_ASSERT(fingerprint);
   2258  MOZ_ASSERT(mCertificate);
   2259  std::vector<uint8_t> fp;
   2260  nsresult rv = CalculateFingerprint(DtlsIdentity::DEFAULT_HASH_ALGORITHM, &fp);
   2261  NS_ENSURE_SUCCESS(rv, rv);
   2262  std::ostringstream os;
   2263  os << DtlsIdentity::DEFAULT_HASH_ALGORITHM << ' '
   2264     << SdpFingerprintAttributeList::FormatFingerprint(fp);
   2265  std::string fpStr = os.str();
   2266 
   2267  char* tmp = new char[fpStr.size() + 1];
   2268  std::copy(fpStr.begin(), fpStr.end(), tmp);
   2269  tmp[fpStr.size()] = '\0';
   2270 
   2271  *fingerprint = tmp;
   2272  return NS_OK;
   2273 }
   2274 
   2275 void PeerConnectionImpl::GetCurrentLocalDescription(nsAString& aSDP) const {
   2276  aSDP = NS_ConvertASCIItoUTF16(mCurrentLocalDescription.c_str());
   2277 }
   2278 
   2279 void PeerConnectionImpl::GetPendingLocalDescription(nsAString& aSDP) const {
   2280  aSDP = NS_ConvertASCIItoUTF16(mPendingLocalDescription.c_str());
   2281 }
   2282 
   2283 void PeerConnectionImpl::GetCurrentRemoteDescription(nsAString& aSDP) const {
   2284  aSDP = NS_ConvertASCIItoUTF16(mCurrentRemoteDescription.c_str());
   2285 }
   2286 
   2287 void PeerConnectionImpl::GetPendingRemoteDescription(nsAString& aSDP) const {
   2288  aSDP = NS_ConvertASCIItoUTF16(mPendingRemoteDescription.c_str());
   2289 }
   2290 
   2291 dom::Nullable<bool> PeerConnectionImpl::GetCurrentOfferer() const {
   2292  dom::Nullable<bool> result;
   2293  if (mCurrentOfferer.isSome()) {
   2294    result.SetValue(*mCurrentOfferer);
   2295  }
   2296  return result;
   2297 }
   2298 
   2299 dom::Nullable<bool> PeerConnectionImpl::GetPendingOfferer() const {
   2300  dom::Nullable<bool> result;
   2301  if (mPendingOfferer.isSome()) {
   2302    result.SetValue(*mPendingOfferer);
   2303  }
   2304  return result;
   2305 }
   2306 
   2307 NS_IMETHODIMP
   2308 PeerConnectionImpl::SignalingState(RTCSignalingState* aState) {
   2309  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2310  MOZ_ASSERT(aState);
   2311 
   2312  *aState = mSignalingState;
   2313  return NS_OK;
   2314 }
   2315 
   2316 NS_IMETHODIMP
   2317 PeerConnectionImpl::IceConnectionState(RTCIceConnectionState* aState) {
   2318  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2319  MOZ_ASSERT(aState);
   2320 
   2321  *aState = mIceConnectionState;
   2322  return NS_OK;
   2323 }
   2324 
   2325 NS_IMETHODIMP
   2326 PeerConnectionImpl::IceGatheringState(RTCIceGatheringState* aState) {
   2327  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2328  MOZ_ASSERT(aState);
   2329 
   2330  *aState = mIceGatheringState;
   2331  return NS_OK;
   2332 }
   2333 
   2334 NS_IMETHODIMP
   2335 PeerConnectionImpl::ConnectionState(RTCPeerConnectionState* aState) {
   2336  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2337  MOZ_ASSERT(aState);
   2338 
   2339  *aState = mConnectionState;
   2340  return NS_OK;
   2341 }
   2342 
   2343 nsresult PeerConnectionImpl::CheckApiState(bool assert_ice_ready) const {
   2344  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2345  MOZ_ASSERT(mTrickle || !assert_ice_ready ||
   2346             (mIceGatheringState == RTCIceGatheringState::Complete));
   2347 
   2348  if (IsClosed()) {
   2349    CSFLogError(LOGTAG, "%s: called API while closed", __FUNCTION__);
   2350    return NS_ERROR_FAILURE;
   2351  }
   2352  return NS_OK;
   2353 }
   2354 
   2355 void PeerConnectionImpl::StoreFinalStats(
   2356    UniquePtr<RTCStatsReportInternal>&& report) {
   2357  using namespace Telemetry;
   2358 
   2359  report->mClosed = true;
   2360 
   2361  for (const auto& inboundRtpStats : report->mInboundRtpStreamStats) {
   2362    bool isVideo = (inboundRtpStats.mId.Value().Find(u"video") != -1);
   2363    if (!isVideo) {
   2364      continue;
   2365    }
   2366    if (inboundRtpStats.mDiscardedPackets.WasPassed() &&
   2367        report->mCallDurationMs.WasPassed()) {
   2368      double mins = report->mCallDurationMs.Value() / (1000 * 60);
   2369      if (mins > 0) {
   2370        glean::webrtc::video_decoder_discarded_packets_per_call_ppm
   2371            .AccumulateSingleSample(uint32_t(
   2372                double(inboundRtpStats.mDiscardedPackets.Value()) / mins));
   2373      }
   2374    }
   2375  }
   2376 
   2377  // Finally, store the stats
   2378  mFinalStats = std::move(report);
   2379 }
   2380 
   2381 NS_IMETHODIMP
   2382 PeerConnectionImpl::Close() {
   2383  CSFLogDebug(LOGTAG, "%s: for %s", __FUNCTION__, mHandle.c_str());
   2384  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   2385 
   2386  if (IsClosed()) {
   2387    return NS_OK;
   2388  }
   2389 
   2390  STAMP_TIMECARD(mTimeCard, "Close");
   2391 
   2392  // When ICE completes, we record some telemetry. We do this at the end of the
   2393  // call because we want to make sure we've waited for all trickle ICE
   2394  // candidates to come in; this can happen well after we've transitioned to
   2395  // connected. As a bonus, this allows us to detect race conditions where a
   2396  // stats dispatch happens right as the PC closes.
   2397  RecordEndOfCallTelemetry();
   2398 
   2399  CSFLogInfo(LOGTAG,
   2400             "%s: Closing PeerConnectionImpl %s; "
   2401             "ending call",
   2402             __FUNCTION__, mHandle.c_str());
   2403  if (mJsepSession) {
   2404    mJsepSession->Close();
   2405  }
   2406  if (mDataConnection) {
   2407    CSFLogInfo(LOGTAG, "%s: Destroying DataChannelConnection %p for %s",
   2408               __FUNCTION__, (void*)mDataConnection.get(), mHandle.c_str());
   2409    mDataConnection->Destroy();
   2410    mDataConnection =
   2411        nullptr;  // it may not go away until the runnables are dead
   2412  }
   2413 
   2414  if (mStunAddrsRequest) {
   2415    for (const auto& hostname : mRegisteredMDNSHostnames) {
   2416      mStunAddrsRequest->SendUnregisterMDNSHostname(
   2417          nsCString(hostname.c_str()));
   2418    }
   2419    mRegisteredMDNSHostnames.clear();
   2420    mStunAddrsRequest->Cancel();
   2421    mStunAddrsRequest = nullptr;
   2422  }
   2423 
   2424  for (auto& transceiver : mTransceivers) {
   2425    transceiver->Close();
   2426  }
   2427 
   2428  mTransportIdToRTCDtlsTransport.Clear();
   2429 
   2430  mQueuedIceCtxOperations.clear();
   2431 
   2432  mOperations.Clear();
   2433 
   2434  // Uncount this connection as active on the inner window upon close.
   2435  if (mWindow && mActiveOnWindow) {
   2436    mWindow->RemovePeerConnection();
   2437    mActiveOnWindow = false;
   2438  }
   2439 
   2440  mSignalingState = RTCSignalingState::Closed;
   2441  mConnectionState = RTCPeerConnectionState::Closed;
   2442 
   2443  if (!mTransportHandler) {
   2444    // We were never initialized, apparently.
   2445    return NS_OK;
   2446  }
   2447 
   2448  mGatheringStateChangeListener.DisconnectIfExists();
   2449  mConnectionStateChangeListener.DisconnectIfExists();
   2450  mCandidateListener.DisconnectIfExists();
   2451  mAlpnNegotiatedListener.DisconnectIfExists();
   2452  mStateChangeListener.DisconnectIfExists();
   2453  mRtcpStateChangeListener.DisconnectIfExists();
   2454 
   2455  // Clear any resources held by libwebrtc through our Call instance.
   2456  RefPtr<GenericPromise> callDestroyPromise;
   2457  if (mCall) {
   2458    // Make sure the compiler does not get confused and try to acquire a
   2459    // reference to this thread _after_ we null out mCall.
   2460    auto callThread = mCall->mCallThread;
   2461    callDestroyPromise =
   2462        InvokeAsync(callThread, __func__, [call = std::move(mCall)]() {
   2463          call->Destroy();
   2464          return GenericPromise::CreateAndResolve(
   2465              true, "PCImpl->WebRtcCallWrapper::Destroy");
   2466        });
   2467  } else {
   2468    callDestroyPromise = GenericPromise::CreateAndResolve(true, __func__);
   2469  }
   2470 
   2471  mFinalStatsQuery =
   2472      GetStats(nullptr, true)
   2473          ->Then(
   2474              GetMainThreadSerialEventTarget(), __func__,
   2475              [this, self = RefPtr<PeerConnectionImpl>(this)](
   2476                  UniquePtr<dom::RTCStatsReportInternal>&& aReport) mutable {
   2477                StoreFinalStats(std::move(aReport));
   2478                return GenericNonExclusivePromise::CreateAndResolve(true,
   2479                                                                    __func__);
   2480              },
   2481              [](nsresult aError) {
   2482                return GenericNonExclusivePromise::CreateAndResolve(true,
   2483                                                                    __func__);
   2484              });
   2485 
   2486  // 1. Allow final stats query to complete.
   2487  // 2. Tear down call, if necessary. We do this before we shut down the
   2488  //    transport handler, so RTCP BYE can be sent.
   2489  // 3. Tear down the transport handler, and deregister from PeerConnectionCtx.
   2490  //    When we deregister from PeerConnectionCtx, our final stats (if any)
   2491  //    will be stored.
   2492  mFinalStatsQuery
   2493      ->Then(GetMainThreadSerialEventTarget(), __func__,
   2494             [callDestroyPromise]() mutable { return callDestroyPromise; })
   2495      ->Then(
   2496          GetMainThreadSerialEventTarget(), __func__,
   2497          [this, self = RefPtr<PeerConnectionImpl>(this)]() mutable {
   2498            CSFLogDebug(LOGTAG, "PCImpl->mTransportHandler::RemoveTransports");
   2499            mTransportHandler->RemoveTransportsExcept(std::set<std::string>());
   2500            if (mPrivateWindow) {
   2501              mTransportHandler->ExitPrivateMode();
   2502            }
   2503            mTransportHandler = nullptr;
   2504            if (PeerConnectionCtx::isActive()) {
   2505              // If we're shutting down xpcom, this Instance will be unset
   2506              // before calling Close() on all remaining PCs, to avoid
   2507              // reentrancy.
   2508              PeerConnectionCtx::GetInstance()->RemovePeerConnection(mHandle);
   2509            }
   2510          });
   2511 
   2512  return NS_OK;
   2513 }
   2514 
   2515 void PeerConnectionImpl::BreakCycles() {
   2516  for (auto& transceiver : mTransceivers) {
   2517    transceiver->BreakCycles();
   2518  }
   2519  mTransceivers.Clear();
   2520 }
   2521 
   2522 bool PeerConnectionImpl::HasPendingSetParameters() const {
   2523  for (const auto& transceiver : mTransceivers) {
   2524    if (transceiver->Sender()->HasPendingSetParameters()) {
   2525      return true;
   2526    }
   2527  }
   2528  return false;
   2529 }
   2530 
   2531 void PeerConnectionImpl::InvalidateLastReturnedParameters() {
   2532  for (const auto& transceiver : mTransceivers) {
   2533    transceiver->Sender()->InvalidateLastReturnedParameters();
   2534  }
   2535 }
   2536 
   2537 nsresult PeerConnectionImpl::SetConfiguration(
   2538    const RTCConfiguration& aConfiguration) {
   2539  nsresult rv = mTransportHandler->SetIceConfig(
   2540      aConfiguration.mIceServers, aConfiguration.mIceTransportPolicy);
   2541  if (NS_WARN_IF(NS_FAILED(rv))) {
   2542    return rv;
   2543  }
   2544 
   2545  JsepBundlePolicy bundlePolicy;
   2546  switch (aConfiguration.mBundlePolicy) {
   2547    case dom::RTCBundlePolicy::Balanced:
   2548      bundlePolicy = kBundleBalanced;
   2549      break;
   2550    case dom::RTCBundlePolicy::Max_compat:
   2551      bundlePolicy = kBundleMaxCompat;
   2552      break;
   2553    case dom::RTCBundlePolicy::Max_bundle:
   2554      bundlePolicy = kBundleMaxBundle;
   2555      break;
   2556    default:
   2557      MOZ_CRASH();
   2558  }
   2559 
   2560  // Ignore errors, since those ought to be handled earlier.
   2561  (void)mJsepSession->SetBundlePolicy(bundlePolicy);
   2562 
   2563  if (!aConfiguration.mPeerIdentity.IsEmpty()) {
   2564    mPeerIdentity = new PeerIdentity(aConfiguration.mPeerIdentity);
   2565    mRequestedPrivacy = Some(PrincipalPrivacy::Private);
   2566  }
   2567 
   2568  auto proxyConfig = GetProxyConfig();
   2569  if (proxyConfig) {
   2570    // Note that this could check if PrivacyRequested() is set on the PC and
   2571    // remove "webrtc" from the ALPN list.  But that would only work if the PC
   2572    // was constructed with a peerIdentity constraint, not when isolated
   2573    // streams are added.  If we ever need to signal to the proxy that the
   2574    // media is isolated, then we would need to restructure this code.
   2575    mTransportHandler->SetProxyConfig(std::move(*proxyConfig));
   2576  }
   2577 
   2578  // Store the configuration for about:webrtc
   2579  StoreConfigurationForAboutWebrtc(aConfiguration);
   2580 
   2581  return NS_OK;
   2582 }
   2583 
   2584 RTCSctpTransport* PeerConnectionImpl::GetSctp() const {
   2585  return mSctpTransport.get();
   2586 }
   2587 
   2588 void PeerConnectionImpl::RestartIce() {
   2589  RestartIceNoRenegotiationNeeded();
   2590  // Update the negotiation-needed flag for connection.
   2591  UpdateNegotiationNeeded();
   2592 }
   2593 
   2594 // webrtc-pc does not specify any situations where this is done, but the JSEP
   2595 // spec does, in some situations due to setConfiguration.
   2596 void PeerConnectionImpl::RestartIceNoRenegotiationNeeded() {
   2597  // Empty connection.[[LocalIceCredentialsToReplace]], and populate it with
   2598  // all ICE credentials (ice-ufrag and ice-pwd as defined in section 15.4 of
   2599  // [RFC5245]) found in connection.[[CurrentLocalDescription]], as well as all
   2600  // ICE credentials found in connection.[[PendingLocalDescription]].
   2601  mLocalIceCredentialsToReplace = mJsepSession->GetLocalIceCredentials();
   2602 }
   2603 
   2604 bool PeerConnectionImpl::PluginCrash(uint32_t aPluginID,
   2605                                     const nsAString& aPluginName) {
   2606  // fire an event to the DOM window if this is "ours"
   2607  if (!AnyCodecHasPluginID(aPluginID)) {
   2608    return false;
   2609  }
   2610 
   2611  CSFLogError(LOGTAG, "%s: Our plugin %llu crashed", __FUNCTION__,
   2612              static_cast<unsigned long long>(aPluginID));
   2613 
   2614  RefPtr<Document> doc = mWindow->GetExtantDoc();
   2615  if (!doc) {
   2616    NS_WARNING("Couldn't get document for PluginCrashed event!");
   2617    return true;
   2618  }
   2619 
   2620  PluginCrashedEventInit init;
   2621  init.mPluginID = aPluginID;
   2622  init.mPluginName = aPluginName;
   2623  init.mSubmittedCrashReport = false;
   2624  init.mGmpPlugin = true;
   2625  init.mBubbles = true;
   2626  init.mCancelable = true;
   2627 
   2628  RefPtr<PluginCrashedEvent> event =
   2629      PluginCrashedEvent::Constructor(doc, u"PluginCrashed"_ns, init);
   2630 
   2631  event->SetTrusted(true);
   2632  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   2633 
   2634  nsCOMPtr<nsPIDOMWindowInner> window = mWindow;
   2635  // MOZ_KnownLive due to bug 1506441
   2636  EventDispatcher::DispatchDOMEvent(
   2637      MOZ_KnownLive(nsGlobalWindowInner::Cast(window)), nullptr, event, nullptr,
   2638      nullptr);
   2639 
   2640  return true;
   2641 }
   2642 
   2643 void PeerConnectionImpl::RecordEndOfCallTelemetry() {
   2644  if (!mCallTelemStarted) {
   2645    return;
   2646  }
   2647  MOZ_RELEASE_ASSERT(!mCallTelemEnded, "Don't end telemetry twice");
   2648  MOZ_RELEASE_ASSERT(mJsepSession,
   2649                     "Call telemetry only starts after jsep session start");
   2650  MOZ_RELEASE_ASSERT(mJsepSession->GetNegotiations() > 0,
   2651                     "Call telemetry only starts after first connection");
   2652 
   2653  // Bitmask used for WEBRTC/LOOP_CALL_TYPE telemetry reporting
   2654  static const uint32_t kAudioTypeMask = 1;
   2655  static const uint32_t kVideoTypeMask = 2;
   2656  static const uint32_t kDataChannelTypeMask = 4;
   2657 
   2658  // Report end-of-call Telemetry
   2659  glean::webrtc::renegotiations.AccumulateSingleSample(
   2660      mJsepSession->GetNegotiations() - 1);
   2661  glean::webrtc::max_video_send_track.AccumulateSingleSample(
   2662      mMaxSending[SdpMediaSection::MediaType::kVideo]);
   2663  glean::webrtc::max_video_receive_track.AccumulateSingleSample(
   2664      mMaxReceiving[SdpMediaSection::MediaType::kVideo]);
   2665  glean::webrtc::max_audio_send_track.AccumulateSingleSample(
   2666      mMaxSending[SdpMediaSection::MediaType::kAudio]);
   2667  glean::webrtc::max_audio_receive_track.AccumulateSingleSample(
   2668      mMaxReceiving[SdpMediaSection::MediaType::kAudio]);
   2669  // DataChannels appear in both Sending and Receiving
   2670  glean::webrtc::datachannel_negotiated
   2671      .EnumGet(static_cast<glean::webrtc::DatachannelNegotiatedLabel>(
   2672          mMaxSending[SdpMediaSection::MediaType::kApplication]))
   2673      .Add();
   2674  // Enumerated/bitmask: 1 = Audio, 2 = Video, 4 = DataChannel
   2675  // A/V = 3, A/V/D = 7, etc
   2676  uint32_t type = 0;
   2677  if (mMaxSending[SdpMediaSection::MediaType::kAudio] ||
   2678      mMaxReceiving[SdpMediaSection::MediaType::kAudio]) {
   2679    type = kAudioTypeMask;
   2680  }
   2681  if (mMaxSending[SdpMediaSection::MediaType::kVideo] ||
   2682      mMaxReceiving[SdpMediaSection::MediaType::kVideo]) {
   2683    type |= kVideoTypeMask;
   2684  }
   2685  if (mMaxSending[SdpMediaSection::MediaType::kApplication]) {
   2686    type |= kDataChannelTypeMask;
   2687  }
   2688  glean::webrtc::call_type.AccumulateSingleSample(type);
   2689 
   2690  MOZ_RELEASE_ASSERT(mWindow);
   2691  auto found = sCallDurationTimers.find(mWindow->WindowID());
   2692  if (found != sCallDurationTimers.end()) {
   2693    found->second.UnregisterConnection((type & kAudioTypeMask) ||
   2694                                       (type & kVideoTypeMask));
   2695    if (found->second.IsStopped()) {
   2696      sCallDurationTimers.erase(found);
   2697    }
   2698  }
   2699  mCallTelemEnded = true;
   2700 }
   2701 
   2702 void PeerConnectionImpl::RecordSignalingTelemetry() const {
   2703  uint16_t recvonly[SdpMediaSection::kMediaTypes];
   2704  uint16_t sendonly[SdpMediaSection::kMediaTypes];
   2705  uint16_t sendrecv[SdpMediaSection::kMediaTypes];
   2706  mJsepSession->CountTransceivers(recvonly, sendonly, sendrecv);
   2707 
   2708  uint32_t numTransports = 0;
   2709  mJsepSession->ForEachTransceiver([&](const auto& aTransceiver) {
   2710    if (aTransceiver.HasOwnTransport()) {
   2711      ++numTransports;
   2712    }
   2713  });
   2714 
   2715  SdpNegotiatedExtra extra = {
   2716      .bundlePolicy = (mJsConfiguration.mBundlePolicy.WasPassed()
   2717                           ? Some(mJsConfiguration.mBundlePolicy.Value())
   2718                           : Nothing())
   2719                          .map([](RTCBundlePolicy aPolicy) {
   2720                            return GetEnumString(aPolicy);
   2721                          }),
   2722      .iceTransportPolicy =
   2723          (mJsConfiguration.mIceTransportPolicy.WasPassed()
   2724               ? Some(mJsConfiguration.mIceTransportPolicy.Value())
   2725               : Nothing())
   2726              .map([](RTCIceTransportPolicy aPolicy) {
   2727                return GetEnumString(aPolicy);
   2728              }),
   2729      .isRemoteIceLite = Some(mJsepSession->RemoteIsIceLite()),
   2730      .negotiationCount = Some(mJsepSession->GetNegotiations()),
   2731      .numMsectionsAudioRecvonly = Some(recvonly[SdpMediaSection::kAudio]),
   2732      .numMsectionsAudioSendonly = Some(sendonly[SdpMediaSection::kAudio]),
   2733      .numMsectionsAudioSendrecv = Some(sendrecv[SdpMediaSection::kAudio]),
   2734      .numMsectionsData = Some(sendrecv[SdpMediaSection::kApplication]),
   2735      .numMsectionsVideoRecvonly = Some(recvonly[SdpMediaSection::kVideo]),
   2736      .numMsectionsVideoSendonly = Some(sendonly[SdpMediaSection::kVideo]),
   2737      .numMsectionsVideoSendrecv = Some(sendrecv[SdpMediaSection::kVideo]),
   2738      .numTransports = Some(numTransports),
   2739      .pcId = Some(nsCString(mHandle.c_str())),
   2740  };
   2741  glean::webrtc_signaling::sdp_negotiated.Record(Some(std::move(extra)));
   2742 
   2743  mJsepSession->ForEachTransceiver([&](const JsepTransceiver& aTransceiver) {
   2744    if (const auto type = aTransceiver.GetMediaType();
   2745        type != SdpMediaSection::kAudio && type != SdpMediaSection::kVideo) {
   2746      return;
   2747    }
   2748    if (!aTransceiver.IsNegotiated()) {
   2749      return;
   2750    }
   2751    const bool sending = aTransceiver.mSendTrack.GetActive();
   2752    const bool receiving = aTransceiver.mRecvTrack.GetActive();
   2753    const nsFmtCString codecString =
   2754        ([](const JsepTrackNegotiatedDetails* aDetails) {
   2755          std::set<std::string> payload_names;
   2756          const size_t count = aDetails ? aDetails->GetEncodingCount() : 0;
   2757          for (size_t i = 0; i < count; ++i) {
   2758            const auto& encoding = aDetails->GetEncoding(i);
   2759            for (const auto& codec : encoding.GetCodecs()) {
   2760              if (codec->mEnabled) {
   2761                payload_names.insert(codec->mName);
   2762              }
   2763            }
   2764          }
   2765          return nsFmtCString{FMT_STRING("{}"), fmt::join(payload_names, ", ")};
   2766        })((sending ? aTransceiver.mSendTrack : aTransceiver.mRecvTrack)
   2767               .GetNegotiatedDetails());
   2768    const char* direction = ([&]() {
   2769      if (sending && receiving) {
   2770        return "sendrecv";
   2771      }
   2772      if (sending) {
   2773        return "sendonly";
   2774      }
   2775      if (receiving) {
   2776        return "recvonly";
   2777      }
   2778      return "inactive";
   2779    })();
   2780    const bool hasRtcpMux = aTransceiver.mTransport.mComponents == 1;
   2781    if (aTransceiver.GetMediaType() == SdpMediaSection::kVideo) {
   2782      VideoMsectionNegotiatedExtra extraVideo{
   2783          .codecs = Some(codecString),
   2784          .direction = Some(direction),
   2785          .hasRtcpMux = Some(hasRtcpMux),
   2786          .numSendSimulcastLayers =
   2787              sending ? Some(aTransceiver.mSendTrack.GetRids().size())
   2788                      : Nothing(),
   2789          .pcId = Some(nsCString(mHandle.c_str())),
   2790          .pcNegotiationCount = Some(mJsepSession->GetNegotiations()),
   2791          .preferredRecvCodec =
   2792              receiving ? Some(nsDependentCString(
   2793                              aTransceiver.mRecvTrack.GetVideoPreferredCodec()
   2794                                  .c_str()))
   2795                        : Nothing(),
   2796          .preferredSendCodec =
   2797              sending ? Some(nsDependentCString(
   2798                            aTransceiver.mSendTrack.GetVideoPreferredCodec()
   2799                                .c_str()))
   2800                      : Nothing(),
   2801      };
   2802      glean::webrtc_signaling::video_msection_negotiated.Record(
   2803          Some(extraVideo));
   2804    } else {
   2805      AudioMsectionNegotiatedExtra extraAudio{
   2806          .codecs = Some(codecString),
   2807          .direction = Some(direction),
   2808          .hasRtcpMux = Some(hasRtcpMux),
   2809          .pcId = Some(nsCString(mHandle.c_str())),
   2810          .pcNegotiationCount = Some(mJsepSession->GetNegotiations()),
   2811          .preferredRecvCodec =
   2812              receiving ? Some(nsDependentCString(
   2813                              aTransceiver.mRecvTrack.GetAudioPreferredCodec()
   2814                                  .c_str()))
   2815                        : Nothing(),
   2816          .preferredSendCodec =
   2817              sending ? Some(nsDependentCString(
   2818                            aTransceiver.mSendTrack.GetAudioPreferredCodec()
   2819                                .c_str()))
   2820                      : Nothing(),
   2821      };
   2822      glean::webrtc_signaling::audio_msection_negotiated.Record(
   2823          Some(extraAudio));
   2824    }
   2825  });
   2826 }
   2827 
   2828 DOMMediaStream* PeerConnectionImpl::GetReceiveStream(
   2829    const std::string& aId) const {
   2830  nsString wanted = NS_ConvertASCIItoUTF16(aId.c_str());
   2831  for (auto& stream : mReceiveStreams) {
   2832    nsString id;
   2833    stream->GetId(id);
   2834    if (id == wanted) {
   2835      return stream;
   2836    }
   2837  }
   2838  return nullptr;
   2839 }
   2840 
   2841 DOMMediaStream* PeerConnectionImpl::CreateReceiveStream(
   2842    const std::string& aId) {
   2843  mReceiveStreams.AppendElement(new DOMMediaStream(mWindow));
   2844  mReceiveStreams.LastElement()->AssignId(NS_ConvertASCIItoUTF16(aId.c_str()));
   2845  return mReceiveStreams.LastElement();
   2846 }
   2847 
   2848 already_AddRefed<dom::Promise> PeerConnectionImpl::OnSetDescriptionSuccess(
   2849    dom::RTCSdpType aSdpType, bool aRemote, ErrorResult& aError) {
   2850  CSFLogDebug(LOGTAG, __FUNCTION__);
   2851 
   2852  RefPtr<dom::Promise> p = MakePromise(aError);
   2853  if (aError.Failed()) {
   2854    return nullptr;
   2855  }
   2856 
   2857  DoSetDescriptionSuccessPostProcessing(aSdpType, aRemote, p);
   2858 
   2859  return p.forget();
   2860 }
   2861 
   2862 void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing(
   2863    dom::RTCSdpType aSdpType, bool aRemote, const RefPtr<dom::Promise>& aP) {
   2864  // Spec says we queue a task for all the stuff that ends up back in JS
   2865  GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   2866      __func__,
   2867      [this, self = RefPtr<PeerConnectionImpl>(this), aSdpType, aRemote, aP] {
   2868        if (IsClosed()) {
   2869          // Yes, we do not settle the promise here. Yes, this is what the spec
   2870          // wants.
   2871          return;
   2872        }
   2873 
   2874        MOZ_ASSERT(mUncommittedJsepSession);
   2875 
   2876        // sRD/sLD needs to be redone in certain circumstances
   2877        bool needsRedo = HasPendingSetParameters();
   2878        if (!needsRedo && aRemote && (aSdpType == dom::RTCSdpType::Offer)) {
   2879          for (auto& transceiver : mTransceivers) {
   2880            if (!mUncommittedJsepSession->GetTransceiver(
   2881                    transceiver->GetJsepTransceiverId())) {
   2882              needsRedo = true;
   2883              break;
   2884            }
   2885          }
   2886        }
   2887 
   2888        if (needsRedo) {
   2889          // Spec says to abort, and re-do the sRD!
   2890          // This happens either when there is a SetParameters call in
   2891          // flight (that will race against the [[SendEncodings]]
   2892          // modification caused by sRD(offer)), or when addTrack has been
   2893          // called while sRD(offer) was in progress.
   2894          mUncommittedJsepSession.reset(mJsepSession->Clone());
   2895          JsepSession::Result result;
   2896          if (aRemote) {
   2897            mUncommittedJsepSession->SetRemoteDescription(
   2898                ToJsepSdpType(aSdpType), mRemoteRequestedSDP);
   2899          } else {
   2900            mUncommittedJsepSession->SetLocalDescription(
   2901                ToJsepSdpType(aSdpType), mLocalRequestedSDP);
   2902          }
   2903          if (result.mError.isSome()) {
   2904            // wat
   2905            nsCString error(
   2906                "When redoing sRD/sLD because it raced against "
   2907                "addTrack or setParameters, we encountered a failure that "
   2908                "did not happen "
   2909                "the first time. This should never happen. The error was: ");
   2910            error += mUncommittedJsepSession->GetLastError().c_str();
   2911            aP->MaybeRejectWithOperationError(error);
   2912            MOZ_ASSERT(false);
   2913          } else {
   2914            DoSetDescriptionSuccessPostProcessing(aSdpType, aRemote, aP);
   2915          }
   2916          return;
   2917        }
   2918 
   2919        for (auto& transceiver : mTransceivers) {
   2920          if (!mUncommittedJsepSession->GetTransceiver(
   2921                  transceiver->GetJsepTransceiverId())) {
   2922            // sLD, or sRD(answer), just make sure the new transceiver is
   2923            // added, no need to re-do anything.
   2924            mUncommittedJsepSession->AddTransceiver(
   2925                transceiver->GetJsepTransceiver());
   2926          }
   2927        }
   2928 
   2929        auto oldIceCredentials = mJsepSession->GetLocalIceCredentials();
   2930        auto newIceCredentials =
   2931            mUncommittedJsepSession->GetLocalIceCredentials();
   2932 
   2933        bool iceRestartDetected =
   2934            (!oldIceCredentials.empty() && !newIceCredentials.empty() &&
   2935             (oldIceCredentials != newIceCredentials));
   2936 
   2937        mJsepSession = std::move(mUncommittedJsepSession);
   2938 
   2939        auto newSignalingState = GetSignalingState();
   2940        SyncFromJsep();
   2941        if (aRemote || aSdpType == dom::RTCSdpType::Pranswer ||
   2942            aSdpType == dom::RTCSdpType::Answer) {
   2943          InvalidateLastReturnedParameters();
   2944        }
   2945 
   2946        if (aSdpType == dom::RTCSdpType::Offer &&
   2947            mSignalingState == RTCSignalingState::Stable) {
   2948          // If description is of type "offer" and
   2949          // connection.[[SignalingState]] is "stable" then for each
   2950          // transceiver in connection's set of transceivers, run the following
   2951          // steps:
   2952          SaveStateForRollback();
   2953        }
   2954 
   2955        // Section 4.4.1.5 Set the RTCSessionDescription:
   2956        if (aSdpType == dom::RTCSdpType::Rollback) {
   2957          // - step 4.5.10, type is rollback
   2958          RestoreStateForRollback();
   2959        } else if (!(aRemote && aSdpType == dom::RTCSdpType::Offer)) {
   2960          // - step 4.5.9 type is not rollback
   2961          // - step 4.5.9.1 when remote is false
   2962          // - step 4.5.9.2.13 when remote is true, type answer or pranswer
   2963          // More simply: not rollback, and not for remote offers.
   2964          UpdateRTCDtlsTransports();
   2965        }
   2966 
   2967        // Did we just apply a local description?
   2968        if (!aRemote) {
   2969          // We'd like to handle this in PeerConnectionImpl::UpdateNetworkState.
   2970          // Unfortunately, if the WiFi switch happens quickly, we never see
   2971          // that state change.  We need to detect the ice restart here and
   2972          // reset the PeerConnectionImpl's stun addresses so they are
   2973          // regathered when PeerConnectionImpl::GatherIfReady is called.
   2974          if (iceRestartDetected || mJsepSession->IsIceRestarting()) {
   2975            ResetStunAddrsForIceRestart();
   2976          }
   2977          EnsureTransports(*mJsepSession);
   2978        }
   2979 
   2980        if (mJsepSession->GetState() == kJsepStateStable) {
   2981          // If we're rolling back a local offer, we might need to remove some
   2982          // transports, and stomp some MediaPipeline setup, but nothing further
   2983          // needs to be done.
   2984          UpdateTransports(*mJsepSession, mForceIceTcp);
   2985          if (NS_FAILED(UpdateMediaPipelines())) {
   2986            CSFLogError(LOGTAG, "Error Updating MediaPipelines");
   2987            NS_ASSERTION(
   2988                false,
   2989                "Error Updating MediaPipelines in OnSetDescriptionSuccess()");
   2990            aP->MaybeRejectWithOperationError("Error Updating MediaPipelines");
   2991          }
   2992 
   2993          if (aSdpType != dom::RTCSdpType::Rollback) {
   2994            StartIceChecks(*mJsepSession);
   2995          }
   2996 
   2997          // Telemetry: record info on the current state of
   2998          // streams/renegotiations/etc Note: this code gets run on rollbacks as
   2999          // well!
   3000 
   3001          // Update the max channels used with each direction for each type
   3002          uint16_t receiving[SdpMediaSection::kMediaTypes];
   3003          uint16_t sending[SdpMediaSection::kMediaTypes];
   3004          mJsepSession->CountTracksAndDatachannels(receiving, sending);
   3005          for (size_t i = 0; i < SdpMediaSection::kMediaTypes; i++) {
   3006            if (mMaxReceiving[i] < receiving[i]) {
   3007              mMaxReceiving[i] = receiving[i];
   3008            }
   3009            if (mMaxSending[i] < sending[i]) {
   3010              mMaxSending[i] = sending[i];
   3011            }
   3012          }
   3013        } else if (aSdpType == dom::RTCSdpType::Offer && !aRemote) {
   3014          // We do this to ensure the mediaPipelineFilter is ready to receive
   3015          // PTs in our offer. This is mainly used for when bundle is involved
   3016          // but for whatever reason mid or SSRC is not signaled.
   3017          for (const auto& transceiverImpl : mTransceivers) {
   3018            if ((transceiverImpl->Direction() ==
   3019                 RTCRtpTransceiverDirection::Sendrecv) ||
   3020                (transceiverImpl->Direction() ==
   3021                 RTCRtpTransceiverDirection::Recvonly)) {
   3022              transceiverImpl->Receiver()->UpdateTransport();
   3023            }
   3024          }
   3025        }
   3026 
   3027        mPendingRemoteDescription =
   3028            mJsepSession->GetRemoteDescription(kJsepDescriptionPending);
   3029        mCurrentRemoteDescription =
   3030            mJsepSession->GetRemoteDescription(kJsepDescriptionCurrent);
   3031        mPendingLocalDescription =
   3032            mJsepSession->GetLocalDescription(kJsepDescriptionPending);
   3033        mCurrentLocalDescription =
   3034            mJsepSession->GetLocalDescription(kJsepDescriptionCurrent);
   3035        mPendingOfferer = mJsepSession->IsPendingOfferer();
   3036        mCurrentOfferer = mJsepSession->IsCurrentOfferer();
   3037 
   3038        if (aSdpType == dom::RTCSdpType::Answer) {
   3039          std::set<std::pair<std::string, std::string>> iceCredentials =
   3040              mJsepSession->GetLocalIceCredentials();
   3041          std::vector<std::pair<std::string, std::string>>
   3042              iceCredentialsNotReplaced;
   3043          std::set_intersection(mLocalIceCredentialsToReplace.begin(),
   3044                                mLocalIceCredentialsToReplace.end(),
   3045                                iceCredentials.begin(), iceCredentials.end(),
   3046                                std::back_inserter(iceCredentialsNotReplaced));
   3047 
   3048          if (iceCredentialsNotReplaced.empty()) {
   3049            mLocalIceCredentialsToReplace.clear();
   3050          }
   3051        }
   3052 
   3053        if (newSignalingState == RTCSignalingState::Stable) {
   3054          mNegotiationNeeded = false;
   3055          UpdateNegotiationNeeded();
   3056        }
   3057 
   3058        bool signalingStateChanged = false;
   3059        if (newSignalingState != mSignalingState) {
   3060          mSignalingState = newSignalingState;
   3061          signalingStateChanged = true;
   3062        }
   3063 
   3064        // Spec does not actually tell us to do this, but that is probably a
   3065        // spec bug.
   3066        // https://github.com/w3c/webrtc-pc/issues/2817
   3067        bool gatheringStateChanged = UpdateIceGatheringState();
   3068 
   3069        bool iceConnectionStateChanged = UpdateIceConnectionState();
   3070 
   3071        bool connectionStateChanged = UpdateConnectionState();
   3072 
   3073        // This only gets populated for remote descriptions
   3074        dom::RTCRtpReceiver::StreamAssociationChanges changes;
   3075        if (aRemote) {
   3076          for (const auto& transceiver : mTransceivers) {
   3077            transceiver->Receiver()->UpdateStreams(&changes);
   3078          }
   3079        }
   3080 
   3081        // Make sure to wait until after we've calculated track changes before
   3082        // doing this.
   3083        for (size_t i = 0; i < mTransceivers.Length();) {
   3084          auto& transceiver = mTransceivers[i];
   3085          if (transceiver->ShouldRemove()) {
   3086            mTransceivers[i]->Close();
   3087            mTransceivers[i]->SetRemovedFromPc();
   3088            mTransceivers.RemoveElementAt(i);
   3089          } else {
   3090            ++i;
   3091          }
   3092        }
   3093 
   3094        // JS callbacks happen below. DO NOT TOUCH STATE AFTER THIS UNLESS SPEC
   3095        // EXPLICITLY SAYS TO, OTHERWISE STATES THAT ARE NOT SUPPOSED TO BE
   3096        // OBSERVABLE TO JS WILL BE!
   3097 
   3098        JSErrorResult jrv;
   3099        RefPtr<PeerConnectionObserver> pcObserver(mPCObserver);
   3100        if (signalingStateChanged) {
   3101          pcObserver->OnStateChange(PCObserverStateType::SignalingState, jrv);
   3102        }
   3103 
   3104        if (gatheringStateChanged) {
   3105          pcObserver->OnStateChange(PCObserverStateType::IceGatheringState,
   3106                                    jrv);
   3107        }
   3108 
   3109        if (iceConnectionStateChanged) {
   3110          pcObserver->OnStateChange(PCObserverStateType::IceConnectionState,
   3111                                    jrv);
   3112        }
   3113 
   3114        if (connectionStateChanged) {
   3115          pcObserver->OnStateChange(PCObserverStateType::ConnectionState, jrv);
   3116        }
   3117 
   3118        for (const auto& receiver : changes.mReceiversToMute) {
   3119          // This sets the muted state for the recv track and all its clones.
   3120          receiver->SetTrackMuteFromRemoteSdp();
   3121        }
   3122 
   3123        for (const auto& association : changes.mStreamAssociationsRemoved) {
   3124          RefPtr<DOMMediaStream> stream =
   3125              GetReceiveStream(association.mStreamId);
   3126          if (stream && stream->HasTrack(*association.mTrack)) {
   3127            stream->RemoveTrackInternal(association.mTrack);
   3128          }
   3129        }
   3130 
   3131        // TODO(Bug 1241291): For legacy event, remove eventually
   3132        std::vector<RefPtr<DOMMediaStream>> newStreams;
   3133 
   3134        for (const auto& association : changes.mStreamAssociationsAdded) {
   3135          RefPtr<DOMMediaStream> stream =
   3136              GetReceiveStream(association.mStreamId);
   3137          if (!stream) {
   3138            stream = CreateReceiveStream(association.mStreamId);
   3139            newStreams.push_back(stream);
   3140          }
   3141 
   3142          if (!stream->HasTrack(*association.mTrack)) {
   3143            stream->AddTrackInternal(association.mTrack);
   3144          }
   3145        }
   3146 
   3147        for (const auto& trackEvent : changes.mTrackEvents) {
   3148          dom::Sequence<OwningNonNull<DOMMediaStream>> streams;
   3149          for (const auto& id : trackEvent.mStreamIds) {
   3150            RefPtr<DOMMediaStream> stream = GetReceiveStream(id);
   3151            if (!stream) {
   3152              MOZ_ASSERT(false);
   3153              continue;
   3154            }
   3155            if (!streams.AppendElement(*stream, fallible)) {
   3156              // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which
   3157              // might involve multiple reallocations) and potentially
   3158              // crashing here, SetCapacity could be called outside the loop
   3159              // once.
   3160              mozalloc_handle_oom(0);
   3161            }
   3162          }
   3163          pcObserver->FireTrackEvent(*trackEvent.mReceiver, streams, jrv);
   3164        }
   3165 
   3166        // TODO(Bug 1241291): Legacy event, remove eventually
   3167        for (const auto& stream : newStreams) {
   3168          pcObserver->FireStreamEvent(*stream, jrv);
   3169        }
   3170 
   3171        if (signalingStateChanged &&
   3172            mSignalingState == dom::RTCSignalingState::Stable &&
   3173            aSdpType != RTCSdpType::Rollback) {
   3174          RecordSignalingTelemetry();
   3175        }
   3176 
   3177        aP->MaybeResolveWithUndefined();
   3178      }));
   3179 }
   3180 
   3181 void PeerConnectionImpl::OnSetDescriptionError() {
   3182  mUncommittedJsepSession = nullptr;
   3183 }
   3184 
   3185 RTCSignalingState PeerConnectionImpl::GetSignalingState() const {
   3186  switch (mJsepSession->GetState()) {
   3187    case kJsepStateStable:
   3188      return RTCSignalingState::Stable;
   3189      break;
   3190    case kJsepStateHaveLocalOffer:
   3191      return RTCSignalingState::Have_local_offer;
   3192      break;
   3193    case kJsepStateHaveRemoteOffer:
   3194      return RTCSignalingState::Have_remote_offer;
   3195      break;
   3196    case kJsepStateHaveLocalPranswer:
   3197      return RTCSignalingState::Have_local_pranswer;
   3198      break;
   3199    case kJsepStateHaveRemotePranswer:
   3200      return RTCSignalingState::Have_remote_pranswer;
   3201      break;
   3202    case kJsepStateClosed:
   3203      return RTCSignalingState::Closed;
   3204      break;
   3205  }
   3206  MOZ_CRASH("Invalid JSEP state");
   3207 }
   3208 
   3209 bool PeerConnectionImpl::IsClosed() const {
   3210  return mSignalingState == RTCSignalingState::Closed;
   3211 }
   3212 
   3213 PeerConnectionWrapper::PeerConnectionWrapper(const std::string& handle)
   3214    : impl_(nullptr) {
   3215  if (PeerConnectionCtx::isActive()) {
   3216    impl_ = PeerConnectionCtx::GetInstance()->GetPeerConnection(handle);
   3217  }
   3218 }
   3219 
   3220 const RefPtr<MediaTransportHandler> PeerConnectionImpl::GetTransportHandler()
   3221    const {
   3222  return mTransportHandler;
   3223 }
   3224 
   3225 const std::string& PeerConnectionImpl::GetHandle() { return mHandle; }
   3226 
   3227 const std::string& PeerConnectionImpl::GetName() {
   3228  PC_AUTO_ENTER_API_CALL_NO_CHECK();
   3229  return mName;
   3230 }
   3231 
   3232 void PeerConnectionImpl::CandidateReady(const std::string& candidate,
   3233                                        const std::string& transportId,
   3234                                        const std::string& ufrag) {
   3235  STAMP_TIMECARD(mTimeCard, "Ice Candidate gathered");
   3236  PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
   3237 
   3238  if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) {
   3239    CSFLogWarn(LOGTAG, "Blocking local UDP candidate: %s", candidate.c_str());
   3240    STAMP_TIMECARD(mTimeCard, "UDP Ice Candidate blocked");
   3241    return;
   3242  }
   3243 
   3244  // One of the very few places we still use level; required by the JSEP API
   3245  uint16_t level = 0;
   3246  std::string mid;
   3247  bool skipped = false;
   3248 
   3249  if (mUncommittedJsepSession) {
   3250    // An sLD or sRD is in progress, and while that is the case, we need to add
   3251    // the candidate to both the current JSEP engine, and the uncommitted JSEP
   3252    // engine. We ignore errors because the spec says to only take into account
   3253    // the current/pending local descriptions when determining whether to
   3254    // surface the candidate to content, which does not take into account any
   3255    // in-progress sRD/sLD.
   3256    (void)mUncommittedJsepSession->AddLocalIceCandidate(
   3257        candidate, transportId, ufrag, &level, &mid, &skipped);
   3258  }
   3259 
   3260  nsresult res = mJsepSession->AddLocalIceCandidate(
   3261      candidate, transportId, ufrag, &level, &mid, &skipped);
   3262 
   3263  if (NS_FAILED(res)) {
   3264    std::string errorString = mJsepSession->GetLastError();
   3265 
   3266    STAMP_TIMECARD(mTimeCard, "Local Ice Candidate invalid");
   3267    CSFLogError(LOGTAG,
   3268                "Failed to incorporate local candidate into SDP:"
   3269                " res = %u, candidate = %s, transport-id = %s,"
   3270                " error = %s",
   3271                static_cast<unsigned>(res), candidate.c_str(),
   3272                transportId.c_str(), errorString.c_str());
   3273    return;
   3274  }
   3275 
   3276  if (skipped) {
   3277    STAMP_TIMECARD(mTimeCard, "Local Ice Candidate skipped");
   3278    CSFLogInfo(LOGTAG,
   3279               "Skipped adding local candidate %s (transport-id %s) "
   3280               "to SDP, this typically happens because the m-section "
   3281               "is bundled, which means it doesn't make sense for it "
   3282               "to have its own transport-related attributes.",
   3283               candidate.c_str(), transportId.c_str());
   3284    return;
   3285  }
   3286 
   3287  mPendingLocalDescription =
   3288      mJsepSession->GetLocalDescription(kJsepDescriptionPending);
   3289  mCurrentLocalDescription =
   3290      mJsepSession->GetLocalDescription(kJsepDescriptionCurrent);
   3291  CSFLogInfo(LOGTAG, "Passing local candidate to content: %s",
   3292             candidate.c_str());
   3293  SendLocalIceCandidateToContent(level, mid, candidate, ufrag);
   3294 }
   3295 
   3296 void PeerConnectionImpl::SendLocalIceCandidateToContent(
   3297    uint16_t level, const std::string& mid, const std::string& candidate,
   3298    const std::string& ufrag) {
   3299  STAMP_TIMECARD(mTimeCard, "Send Ice Candidate to content");
   3300  JSErrorResult rv;
   3301  mPCObserver->OnIceCandidate(level, ObString(mid.c_str()),
   3302                              ObString(candidate.c_str()),
   3303                              ObString(ufrag.c_str()), rv);
   3304 }
   3305 
   3306 void PeerConnectionImpl::IceConnectionStateChange(
   3307    const std::string& aTransportId, dom::RTCIceTransportState domState) {
   3308  // If connection.[[IsClosed]] is true, abort these steps.
   3309  PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
   3310 
   3311  CSFLogDebug(LOGTAG, "IceConnectionStateChange: %s %d (%p)",
   3312              aTransportId.c_str(), static_cast<int>(domState), this);
   3313 
   3314  // Let transport be the RTCIceTransport whose state is changing.
   3315  nsCString key(aTransportId.data(), aTransportId.size());
   3316  RefPtr<RTCDtlsTransport> dtlsTransport =
   3317      mTransportIdToRTCDtlsTransport.Get(key);
   3318  if (!dtlsTransport) {
   3319    return;
   3320  }
   3321  RefPtr<RTCIceTransport> transport = dtlsTransport->IceTransport();
   3322 
   3323  if (domState == RTCIceTransportState::Closed) {
   3324    mTransportIdToRTCDtlsTransport.Remove(key);
   3325  }
   3326 
   3327  // Let selectedCandidatePairChanged be false.
   3328  // TODO(bug 1307994)
   3329 
   3330  // Let transportIceConnectionStateChanged be false.
   3331  bool transportIceConnectionStateChanged = false;
   3332 
   3333  // Let connectionIceConnectionStateChanged be false.
   3334  bool connectionIceConnectionStateChanged = false;
   3335 
   3336  // Let connectionStateChanged be false.
   3337  bool connectionStateChanged = false;
   3338 
   3339  if (transport->State() == domState) {
   3340    return;
   3341  }
   3342 
   3343  // If transport's RTCIceTransportState was changed, run the following steps:
   3344 
   3345  // Set transport.[[IceTransportState]] to the new indicated
   3346  // RTCIceTransportState.
   3347  transport->SetState(domState);
   3348 
   3349  // Set transportIceConnectionStateChanged to true.
   3350  transportIceConnectionStateChanged = true;
   3351 
   3352  // Set connection.[[IceConnectionState]] to the value of deriving a new state
   3353  // value as described by the RTCIceConnectionState enum.
   3354  if (UpdateIceConnectionState()) {
   3355    // If connection.[[IceConnectionState]] changed in the previous step, set
   3356    // connectionIceConnectionStateChanged to true.
   3357    connectionIceConnectionStateChanged = true;
   3358  }
   3359 
   3360  // Set connection.[[ConnectionState]] to the value of deriving a new state
   3361  // value as described by the RTCPeerConnectionState enum.
   3362  if (UpdateConnectionState()) {
   3363    // If connection.[[ConnectionState]] changed in the previous step, set
   3364    // connectionStateChanged to true.
   3365    connectionStateChanged = true;
   3366  }
   3367 
   3368  // If selectedCandidatePairChanged is true, fire an event named
   3369  // selectedcandidatepairchange at transport.
   3370  // TODO(bug 1307994)
   3371 
   3372  // If transportIceConnectionStateChanged is true, fire an event named
   3373  // statechange at transport.
   3374  if (transportIceConnectionStateChanged) {
   3375    transport->FireStateChangeEvent();
   3376  }
   3377 
   3378  WrappableJSErrorResult rv;
   3379  RefPtr<PeerConnectionObserver> pcObserver(mPCObserver);
   3380 
   3381  // If connectionIceConnectionStateChanged is true, fire an event named
   3382  // iceconnectionstatechange at connection.
   3383  if (connectionIceConnectionStateChanged) {
   3384    pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv);
   3385  }
   3386 
   3387  // If connectionStateChanged is true, fire an event named
   3388  // connectionstatechange at connection.
   3389  if (connectionStateChanged) {
   3390    pcObserver->OnStateChange(PCObserverStateType::ConnectionState, rv);
   3391  }
   3392 }
   3393 
   3394 RTCIceConnectionState PeerConnectionImpl::GetNewIceConnectionState() const {
   3395  // closed 	The RTCPeerConnection object's [[IsClosed]] slot is true.
   3396  if (IsClosed()) {
   3397    return RTCIceConnectionState::Closed;
   3398  }
   3399 
   3400  // Would use a bitset, but that requires lots of static_cast<size_t>
   3401  // Oh well.
   3402  std::set<RTCIceTransportState> statesFound;
   3403  std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports());
   3404  for (const auto& transport : transports) {
   3405    RefPtr<dom::RTCIceTransport> iceTransport = transport->IceTransport();
   3406    CSFLogWarn(LOGTAG, "GetNewIceConnectionState: %p %d", iceTransport.get(),
   3407               static_cast<int>(iceTransport->State()));
   3408    statesFound.insert(iceTransport->State());
   3409  }
   3410 
   3411  // failed 	 None of the previous states apply and any RTCIceTransports are
   3412  // in the "failed" state.
   3413  if (statesFound.count(RTCIceTransportState::Failed)) {
   3414    return RTCIceConnectionState::Failed;
   3415  }
   3416 
   3417  // disconnected 	 None of the previous states apply and any
   3418  // RTCIceTransports are in the "disconnected" state.
   3419  if (statesFound.count(RTCIceTransportState::Disconnected)) {
   3420    return RTCIceConnectionState::Disconnected;
   3421  }
   3422 
   3423  // new 	 None of the previous states apply and all RTCIceTransports are
   3424  // in the "new" or "closed" state, or there are no transports.
   3425  if (!statesFound.count(RTCIceTransportState::Checking) &&
   3426      !statesFound.count(RTCIceTransportState::Completed) &&
   3427      !statesFound.count(RTCIceTransportState::Connected)) {
   3428    return RTCIceConnectionState::New;
   3429  }
   3430 
   3431  // checking   None of the previous states apply and any RTCIceTransports are
   3432  // in the "new" or "checking" state.
   3433  if (statesFound.count(RTCIceTransportState::New) ||
   3434      statesFound.count(RTCIceTransportState::Checking)) {
   3435    return RTCIceConnectionState::Checking;
   3436  }
   3437 
   3438  // completed  None of the previous states apply and all RTCIceTransports are
   3439  // in the "completed" or "closed" state.
   3440  if (!statesFound.count(RTCIceTransportState::Connected)) {
   3441    return RTCIceConnectionState::Completed;
   3442  }
   3443 
   3444  // connected 	None of the previous states apply.
   3445  return RTCIceConnectionState::Connected;
   3446 }
   3447 
   3448 bool PeerConnectionImpl::UpdateIceConnectionState() {
   3449  auto newState = GetNewIceConnectionState();
   3450  if (newState != mIceConnectionState) {
   3451    CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__,
   3452               static_cast<int>(mIceConnectionState),
   3453               static_cast<int>(newState), this);
   3454    mIceConnectionState = newState;
   3455    // Start call telemtry logging on connected.
   3456    if (mIceConnectionState == RTCIceConnectionState::Connected) {
   3457      StartCallTelem();
   3458    }
   3459    if (mIceConnectionState != RTCIceConnectionState::Closed) {
   3460      return true;
   3461    }
   3462  }
   3463 
   3464  return false;
   3465 }
   3466 
   3467 void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId,
   3468                                          const CandidateInfo& aCandidateInfo) {
   3469  if (mStunAddrsRequest && !aCandidateInfo.mMDNSAddress.empty()) {
   3470    MOZ_ASSERT(!aCandidateInfo.mActualAddress.empty());
   3471 
   3472    if (mCanRegisterMDNSHostnamesDirectly) {
   3473      auto itor = mRegisteredMDNSHostnames.find(aCandidateInfo.mMDNSAddress);
   3474 
   3475      // We'll see the address twice if we're generating both UDP and TCP
   3476      // candidates.
   3477      if (itor == mRegisteredMDNSHostnames.end()) {
   3478        mRegisteredMDNSHostnames.insert(aCandidateInfo.mMDNSAddress);
   3479        mStunAddrsRequest->SendRegisterMDNSHostname(
   3480            nsCString(aCandidateInfo.mMDNSAddress.c_str()),
   3481            nsCString(aCandidateInfo.mActualAddress.c_str()));
   3482      }
   3483    } else {
   3484      mMDNSHostnamesToRegister.emplace(aCandidateInfo.mMDNSAddress,
   3485                                       aCandidateInfo.mActualAddress);
   3486    }
   3487  }
   3488 
   3489  if (!aCandidateInfo.mDefaultHostRtp.empty()) {
   3490    UpdateDefaultCandidate(aCandidateInfo.mDefaultHostRtp,
   3491                           aCandidateInfo.mDefaultPortRtp,
   3492                           aCandidateInfo.mDefaultHostRtcp,
   3493                           aCandidateInfo.mDefaultPortRtcp, aTransportId);
   3494  }
   3495  CandidateReady(aCandidateInfo.mCandidate, aTransportId,
   3496                 aCandidateInfo.mUfrag);
   3497 }
   3498 
   3499 void PeerConnectionImpl::IceGatheringStateChange(
   3500    const std::string& aTransportId, dom::RTCIceGathererState state) {
   3501  // If connection.[[IsClosed]] is true, abort these steps.
   3502  PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
   3503 
   3504  CSFLogWarn(LOGTAG, "IceGatheringStateChange: %s %d (%p)",
   3505             aTransportId.c_str(), static_cast<int>(state), this);
   3506 
   3507  // Let transport be the RTCIceTransport for which candidate gathering
   3508  // began/finished.
   3509  nsCString key(aTransportId.data(), aTransportId.size());
   3510  RefPtr<RTCDtlsTransport> dtlsTransport =
   3511      mTransportIdToRTCDtlsTransport.Get(key);
   3512  if (!dtlsTransport) {
   3513    return;
   3514  }
   3515  RefPtr<RTCIceTransport> transport = dtlsTransport->IceTransport();
   3516 
   3517  if (transport->GatheringState() == state) {
   3518    return;
   3519  }
   3520 
   3521  // Set transport.[[IceGathererState]] to gathering.
   3522  // or
   3523  // Set transport.[[IceGathererState]] to complete.
   3524  transport->SetGatheringState(state);
   3525 
   3526  // Set connection.[[IceGatheringState]] to the value of deriving a new state
   3527  // value as described by the RTCIceGatheringState enum.
   3528  //
   3529  // Let connectionIceGatheringStateChanged be true if
   3530  // connection.[[IceGatheringState]] changed in the previous step, otherwise
   3531  // false.
   3532  bool gatheringStateChanged = UpdateIceGatheringState();
   3533 
   3534  // Do not read or modify state beyond this point.
   3535 
   3536  // Fire an event named gatheringstatechange at transport.
   3537  transport->FireGatheringStateChangeEvent();
   3538 
   3539  // If connectionIceGatheringStateChanged is true, fire an event named
   3540  // icegatheringstatechange at connection.
   3541  if (gatheringStateChanged) {
   3542    // NOTE: If we're in the "complete" case, our JS code will fire a null
   3543    // icecandidate event after firing the icegatheringstatechange event.
   3544    // Fire an event named icecandidate using the RTCPeerConnectionIceEvent
   3545    // interface with the candidate attribute set to null at connection.
   3546    JSErrorResult rv;
   3547    mPCObserver->OnStateChange(PCObserverStateType::IceGatheringState, rv);
   3548  }
   3549 }
   3550 
   3551 bool PeerConnectionImpl::UpdateIceGatheringState() {
   3552  // If connection.[[IsClosed]] is true, abort these steps.
   3553  if (IsClosed()) {
   3554    return false;
   3555  }
   3556 
   3557  // Let newState be the value of deriving a new state value as
   3558  // described by the RTCIceGatheringState enum.
   3559  auto newState = GetNewIceGatheringState();
   3560 
   3561  // If connection.[[IceGatheringState]] is equal to newState, abort
   3562  // these steps.
   3563  if (newState == mIceGatheringState) {
   3564    return false;
   3565  }
   3566 
   3567  CSFLogInfo(LOGTAG, "UpdateIceGatheringState: %d -> %d (%p)",
   3568             static_cast<int>(mIceGatheringState), static_cast<int>(newState),
   3569             this);
   3570  // Set connection.[[IceGatheringState]] to newState.
   3571  mIceGatheringState = newState;
   3572 
   3573  // Would be nice if we had a means of converting one of these dom
   3574  // enums to a string that wasn't almost as much text as this switch
   3575  // statement...
   3576  switch (mIceGatheringState) {
   3577    case RTCIceGatheringState::New:
   3578      STAMP_TIMECARD(mTimeCard, "Ice gathering state: new");
   3579      break;
   3580    case RTCIceGatheringState::Gathering:
   3581      STAMP_TIMECARD(mTimeCard, "Ice gathering state: gathering");
   3582      break;
   3583    case RTCIceGatheringState::Complete:
   3584      STAMP_TIMECARD(mTimeCard, "Ice gathering state: complete");
   3585      break;
   3586    default:
   3587      MOZ_ASSERT_UNREACHABLE("Unexpected mIceGatheringState!");
   3588  }
   3589 
   3590  return true;
   3591 }
   3592 
   3593 RTCIceGatheringState PeerConnectionImpl::GetNewIceGatheringState() const {
   3594  // new 	Any of the RTCIceTransports are in the "new" gathering state
   3595  // and none of the transports are in the "gathering" state, or there are no
   3596  // transports.
   3597 
   3598  // NOTE! This derives the RTCIce**Gathering**State from the individual
   3599  // RTCIce**Gatherer**State of the transports. These are different enums.
   3600  // But they have exactly the same values, in the same order.
   3601  // ¯\_(ツ)_/¯
   3602  bool foundComplete = false;
   3603  std::set<RefPtr<RTCDtlsTransport>> transports(GetActiveTransports());
   3604  for (const auto& transport : transports) {
   3605    RefPtr<dom::RTCIceTransport> iceTransport = transport->IceTransport();
   3606    switch (iceTransport->GatheringState()) {
   3607      case RTCIceGathererState::New:
   3608        break;
   3609      case RTCIceGathererState::Gathering:
   3610        // gathering 	Any of the RTCIceTransports are in the "gathering"
   3611        // state.
   3612        return RTCIceGatheringState::Gathering;
   3613      case RTCIceGathererState::Complete:
   3614        foundComplete = true;
   3615        break;
   3616    }
   3617  }
   3618 
   3619  if (!foundComplete) {
   3620    return RTCIceGatheringState::New;
   3621  }
   3622 
   3623  // This could change depending on the outcome in
   3624  // https://github.com/w3c/webrtc-pc/issues/2914
   3625  return RTCIceGatheringState::Complete;
   3626 }
   3627 
   3628 void PeerConnectionImpl::UpdateDefaultCandidate(
   3629    const std::string& defaultAddr, uint16_t defaultPort,
   3630    const std::string& defaultRtcpAddr, uint16_t defaultRtcpPort,
   3631    const std::string& transportId) {
   3632  CSFLogDebug(LOGTAG, "%s", __FUNCTION__);
   3633  mJsepSession->UpdateDefaultCandidate(
   3634      defaultAddr, defaultPort, defaultRtcpAddr, defaultRtcpPort, transportId);
   3635  if (mUncommittedJsepSession) {
   3636    mUncommittedJsepSession->UpdateDefaultCandidate(
   3637        defaultAddr, defaultPort, defaultRtcpAddr, defaultRtcpPort,
   3638        transportId);
   3639  }
   3640 }
   3641 
   3642 RefPtr<dom::RTCStatsPromise> PeerConnectionImpl::GetDataChannelStats(
   3643    const DOMHighResTimeStamp aTimestamp) {
   3644  MOZ_ASSERT(NS_IsMainThread());
   3645  if (mDataConnection) {
   3646    return mDataConnection->GetStats(aTimestamp)
   3647        ->Then(GetMainThreadSerialEventTarget(), __func__,
   3648               [](DataChannelConnection::StatsPromise::ResolveOrRejectValue&&
   3649                      aResult) {
   3650                 UniquePtr<dom::RTCStatsCollection> report(
   3651                     new dom::RTCStatsCollection);
   3652                 if (aResult.IsResolve()) {
   3653                   if (!report->mDataChannelStats.AppendElements(
   3654                           aResult.ResolveValue(), fallible)) {
   3655                     mozalloc_handle_oom(0);
   3656                   }
   3657                 }
   3658                 return RTCStatsPromise::CreateAndResolve(std::move(report),
   3659                                                          __func__);
   3660               });
   3661  }
   3662  return nullptr;
   3663 }
   3664 
   3665 void PeerConnectionImpl::CollectConduitTelemetryData() {
   3666  MOZ_ASSERT(NS_IsMainThread());
   3667 
   3668  nsTArray<RefPtr<VideoSessionConduit>> conduits;
   3669  for (const auto& transceiver : mTransceivers) {
   3670    if (RefPtr<MediaSessionConduit> conduit = transceiver->GetConduit()) {
   3671      conduit->AsVideoSessionConduit().apply(
   3672          [&](const auto& aVideo) { conduits.AppendElement(aVideo); });
   3673    }
   3674  }
   3675 
   3676  if (!conduits.IsEmpty() && mCall) {
   3677    mCall->mCallThread->Dispatch(
   3678        NS_NewRunnableFunction(__func__, [conduits = std::move(conduits)] {
   3679          for (const auto& conduit : conduits) {
   3680            conduit->CollectTelemetryData();
   3681          }
   3682        }));
   3683  }
   3684 }
   3685 
   3686 nsTArray<dom::RTCCodecStats> PeerConnectionImpl::GetCodecStats(
   3687    DOMHighResTimeStamp aNow) {
   3688  MOZ_ASSERT(NS_IsMainThread());
   3689  nsTArray<dom::RTCCodecStats> result;
   3690 
   3691  struct CodecComparator {
   3692    bool operator()(const JsepCodecDescription* aA,
   3693                    const JsepCodecDescription* aB) const {
   3694      return aA->StatsId() < aB->StatsId();
   3695    }
   3696  };
   3697 
   3698  // transportId -> codec; per direction (whether the codecType
   3699  // shall be "encode", "decode" or absent (if a codec exists in both maps for a
   3700  // transport)). These do the bookkeeping to ensure codec stats get coalesced
   3701  // to transport level.
   3702  std::map<std::string, std::set<JsepCodecDescription*, CodecComparator>>
   3703      sendCodecMap;
   3704  std::map<std::string, std::set<JsepCodecDescription*, CodecComparator>>
   3705      recvCodecMap;
   3706 
   3707  // Find all JsepCodecDescription instances we want to turn into codec stats.
   3708  for (const auto& transceiver : mTransceivers) {
   3709    // TODO: Grab these from the JSEP transceivers instead
   3710    auto sendCodecs = transceiver->GetNegotiatedSendCodecs();
   3711    auto recvCodecs = transceiver->GetNegotiatedRecvCodecs();
   3712 
   3713    const std::string transportId = transceiver->GetTransportId();
   3714    // This ensures both codec maps have the same size.
   3715    auto& sendMap = sendCodecMap[transportId];
   3716    auto& recvMap = recvCodecMap[transportId];
   3717 
   3718    sendCodecs.apply([&](const auto& aCodecs) {
   3719      for (const auto& codec : aCodecs) {
   3720        sendMap.insert(codec.get());
   3721      }
   3722    });
   3723    recvCodecs.apply([&](const auto& aCodecs) {
   3724      for (const auto& codec : aCodecs) {
   3725        recvMap.insert(codec.get());
   3726      }
   3727    });
   3728  }
   3729 
   3730  auto createCodecStat = [&](const JsepCodecDescription* aCodec,
   3731                             const nsString& aTransportId,
   3732                             Maybe<RTCCodecType> aCodecType) {
   3733    uint16_t pt;
   3734    {
   3735      DebugOnly<bool> rv = aCodec->GetPtAsInt(&pt);
   3736      MOZ_ASSERT(rv);
   3737    }
   3738    nsString mimeType;
   3739    mimeType.AppendPrintf(
   3740        "%s/%s", aCodec->Type() == SdpMediaSection::kVideo ? "video" : "audio",
   3741        aCodec->mName.c_str());
   3742    nsString id = aTransportId;
   3743    id.Append(u"_");
   3744    id.Append(aCodec->StatsId());
   3745 
   3746    dom::RTCCodecStats codec;
   3747    codec.mId.Construct(std::move(id));
   3748    codec.mTimestamp.Construct(aNow);
   3749    codec.mType.Construct(RTCStatsType::Codec);
   3750    codec.mPayloadType = pt;
   3751    if (aCodecType) {
   3752      codec.mCodecType.Construct(*aCodecType);
   3753    }
   3754    codec.mTransportId = aTransportId;
   3755    codec.mMimeType = std::move(mimeType);
   3756    codec.mClockRate.Construct(aCodec->mClock);
   3757    if (aCodec->Type() == SdpMediaSection::MediaType::kAudio) {
   3758      codec.mChannels.Construct(aCodec->mChannels);
   3759    }
   3760    if (aCodec->mSdpFmtpLine) {
   3761      codec.mSdpFmtpLine.Construct(
   3762          NS_ConvertUTF8toUTF16(aCodec->mSdpFmtpLine->c_str()));
   3763    }
   3764 
   3765    result.AppendElement(std::move(codec));
   3766  };
   3767 
   3768  // Create codec stats for the gathered codec descriptions, sorted primarily
   3769  // by transportId, secondarily by payload type (from StatsId()).
   3770  for (const auto& [transportId, sendCodecs] : sendCodecMap) {
   3771    const auto& recvCodecs = recvCodecMap[transportId];
   3772    const nsString tid = NS_ConvertASCIItoUTF16(transportId);
   3773    AutoTArray<JsepCodecDescription*, 16> bidirectionalCodecs;
   3774    AutoTArray<JsepCodecDescription*, 16> unidirectionalCodecs;
   3775    std::set_intersection(sendCodecs.cbegin(), sendCodecs.cend(),
   3776                          recvCodecs.cbegin(), recvCodecs.cend(),
   3777                          MakeBackInserter(bidirectionalCodecs),
   3778                          CodecComparator());
   3779    std::set_symmetric_difference(sendCodecs.cbegin(), sendCodecs.cend(),
   3780                                  recvCodecs.cbegin(), recvCodecs.cend(),
   3781                                  MakeBackInserter(unidirectionalCodecs),
   3782                                  CodecComparator());
   3783    for (const auto* codec : bidirectionalCodecs) {
   3784      createCodecStat(codec, tid, Nothing());
   3785    }
   3786    for (const auto* codec : unidirectionalCodecs) {
   3787      createCodecStat(
   3788          codec, tid,
   3789          Some(codec->mDirection == sdp::kSend ? RTCCodecType::Encode
   3790                                               : RTCCodecType::Decode));
   3791    }
   3792  }
   3793 
   3794  return result;
   3795 }
   3796 
   3797 RefPtr<dom::RTCStatsReportPromise> PeerConnectionImpl::GetStats(
   3798    dom::MediaStreamTrack* aSelector, bool aInternalStats) {
   3799  MOZ_ASSERT(NS_IsMainThread());
   3800 
   3801  if (mFinalStatsQuery) {
   3802    // This case should be _extremely_ rare; this will basically only happen
   3803    // when WebrtcGlobalInformation tries to get our stats while we are tearing
   3804    // down.
   3805    return mFinalStatsQuery->Then(
   3806        GetMainThreadSerialEventTarget(), __func__,
   3807        [this, self = RefPtr<PeerConnectionImpl>(this)]() {
   3808          UniquePtr<dom::RTCStatsReportInternal> finalStats =
   3809              MakeUnique<dom::RTCStatsReportInternal>();
   3810          // Might not be set if this encountered some error.
   3811          if (mFinalStats) {
   3812            *finalStats = *mFinalStats;
   3813          }
   3814          return RTCStatsReportPromise::CreateAndResolve(std::move(finalStats),
   3815                                                         __func__);
   3816        });
   3817  }
   3818 
   3819  nsTArray<RefPtr<dom::RTCStatsPromise>> promises;
   3820  DOMHighResTimeStamp now = mTimestampMaker.GetNow().ToDom();
   3821 
   3822  nsTArray<dom::RTCCodecStats> codecStats = GetCodecStats(now);
   3823  std::set<std::string> transportIds;
   3824 
   3825  if (!aSelector) {
   3826    // There might not be any senders/receivers if we're DataChannel only, so we
   3827    // don't handle the null selector case in the loop below.
   3828    transportIds.insert("");
   3829  }
   3830 
   3831  nsTArray<
   3832      std::tuple<RTCRtpTransceiver*, RefPtr<RTCStatsPromise::AllPromiseType>>>
   3833      transceiverStatsPromises;
   3834  for (const auto& transceiver : mTransceivers) {
   3835    const bool sendSelected = transceiver->Sender()->HasTrack(aSelector) ||
   3836                              (!aSelector && transceiver->HasBeenUsedToSend());
   3837    const bool recvSelected = transceiver->Receiver()->HasTrack(aSelector);
   3838    if (!sendSelected && !recvSelected) {
   3839      continue;
   3840    }
   3841 
   3842    if (aSelector) {
   3843      transportIds.insert(transceiver->GetTransportId());
   3844    }
   3845 
   3846    nsTArray<RefPtr<RTCStatsPromise>> rtpStreamPromises;
   3847    // Get all rtp stream stats for the given selector. Then filter away any
   3848    // codec stat not related to the selector, and assign codec ids to the
   3849    // stream stats.
   3850    // Skips the ICE stats; we do our own queries based on |transportIds| to
   3851    // avoid duplicates
   3852    if (sendSelected) {
   3853      rtpStreamPromises.AppendElements(
   3854          transceiver->Sender()->GetStatsInternal(true));
   3855    }
   3856    if (recvSelected) {
   3857      rtpStreamPromises.AppendElements(
   3858          transceiver->Receiver()->GetStatsInternal(true));
   3859    }
   3860    transceiverStatsPromises.AppendElement(
   3861        std::make_tuple(transceiver.get(),
   3862                        RTCStatsPromise::All(GetMainThreadSerialEventTarget(),
   3863                                             rtpStreamPromises)));
   3864  }
   3865 
   3866  promises.AppendElement(RTCRtpTransceiver::ApplyCodecStats(
   3867      std::move(codecStats), std::move(transceiverStatsPromises)));
   3868 
   3869  for (const auto& transportId : transportIds) {
   3870    promises.AppendElement(mTransportHandler->GetIceStats(transportId, now));
   3871  }
   3872 
   3873  if (mDataConnection) {
   3874    promises.AppendElement(GetDataChannelStats(now));
   3875  }
   3876 
   3877  auto pcStatsCollection = MakeUnique<dom::RTCStatsCollection>();
   3878  RTCPeerConnectionStats pcStats;
   3879  pcStats.mTimestamp.Construct(now);
   3880  pcStats.mType.Construct(RTCStatsType::Peer_connection);
   3881  pcStats.mId.Construct(NS_ConvertUTF8toUTF16(mHandle.c_str()));
   3882  pcStats.mDataChannelsOpened.Construct(mDataChannelsOpened);
   3883  pcStats.mDataChannelsClosed.Construct(mDataChannelsClosed);
   3884  if (!pcStatsCollection->mPeerConnectionStats.AppendElement(std::move(pcStats),
   3885                                                             fallible)) {
   3886    mozalloc_handle_oom(0);
   3887  }
   3888  promises.AppendElement(RTCStatsPromise::CreateAndResolve(
   3889      std::move(pcStatsCollection), __func__));
   3890 
   3891  // This is what we're going to return; all the stuff in |promises| will be
   3892  // accumulated here.
   3893  UniquePtr<dom::RTCStatsReportInternal> report(
   3894      new dom::RTCStatsReportInternal);
   3895  report->mPcid = NS_ConvertASCIItoUTF16(mName.c_str());
   3896  if (mWindow && mWindow->GetBrowsingContext()) {
   3897    report->mBrowserId = mWindow->GetBrowsingContext()->BrowserId();
   3898  }
   3899  report->mConfiguration.Construct(mJsConfiguration);
   3900  // TODO(bug 1589416): We need to do better here.
   3901  if (!mIceStartTime.IsNull()) {
   3902    report->mCallDurationMs.Construct(
   3903        (TimeStamp::Now() - mIceStartTime).ToMilliseconds());
   3904  }
   3905  report->mIceRestarts = mIceRestartCount;
   3906  report->mIceRollbacks = mIceRollbackCount;
   3907  report->mClosed = false;
   3908  report->mTimestamp = now;
   3909 
   3910  if (aInternalStats && mJsepSession) {
   3911    for (const auto& candidate : mRawTrickledCandidates) {
   3912      if (!report->mRawRemoteCandidates.AppendElement(
   3913              NS_ConvertASCIItoUTF16(candidate.c_str()), fallible)) {
   3914        // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
   3915        // involve multiple reallocations) and potentially crashing here,
   3916        // SetCapacity could be called outside the loop once.
   3917        mozalloc_handle_oom(0);
   3918      }
   3919    }
   3920 
   3921    if (mJsepSession) {
   3922      // TODO we probably should report Current and Pending SDPs here
   3923      // separately. Plus the raw SDP we got from JS (mLocalRequestedSDP).
   3924      // And if it's the offer or answer would also be nice.
   3925      std::string localDescription =
   3926          mJsepSession->GetLocalDescription(kJsepDescriptionPendingOrCurrent);
   3927      std::string remoteDescription =
   3928          mJsepSession->GetRemoteDescription(kJsepDescriptionPendingOrCurrent);
   3929      if (!report->mSdpHistory.AppendElements(mSdpHistory, fallible)) {
   3930        mozalloc_handle_oom(0);
   3931      }
   3932      if (mJsepSession->IsPendingOfferer().isSome()) {
   3933        report->mOfferer.Construct(*mJsepSession->IsPendingOfferer());
   3934      } else if (mJsepSession->IsCurrentOfferer().isSome()) {
   3935        report->mOfferer.Construct(*mJsepSession->IsCurrentOfferer());
   3936      } else {
   3937        // Silly.
   3938        report->mOfferer.Construct(false);
   3939      }
   3940    }
   3941  }
   3942 
   3943  return dom::RTCStatsPromise::All(GetMainThreadSerialEventTarget(), promises)
   3944      ->Then(
   3945          GetMainThreadSerialEventTarget(), __func__,
   3946          [report = std::move(report), idGen = mIdGenerator](
   3947              nsTArray<UniquePtr<dom::RTCStatsCollection>> aStats) mutable {
   3948            idGen->RewriteIds(std::move(aStats), report.get());
   3949            return dom::RTCStatsReportPromise::CreateAndResolve(
   3950                std::move(report), __func__);
   3951          },
   3952          [](nsresult rv) {
   3953            return dom::RTCStatsReportPromise::CreateAndReject(rv, __func__);
   3954          });
   3955 }
   3956 
   3957 void PeerConnectionImpl::RecordIceRestartStatistics(JsepSdpType type) {
   3958  switch (type) {
   3959    case mozilla::kJsepSdpOffer:
   3960    case mozilla::kJsepSdpPranswer:
   3961      break;
   3962    case mozilla::kJsepSdpAnswer:
   3963      ++mIceRestartCount;
   3964      break;
   3965    case mozilla::kJsepSdpRollback:
   3966      ++mIceRollbackCount;
   3967      break;
   3968  }
   3969 }
   3970 
   3971 void PeerConnectionImpl::StoreConfigurationForAboutWebrtc(
   3972    const dom::RTCConfiguration& aConfig) {
   3973  // This will only be called once, when the PeerConnection is initially
   3974  // configured, at least until setConfiguration is implemented
   3975  // see https://bugzilla.mozilla.org/show_bug.cgi?id=1253706
   3976  // @TODO bug 1739451 call this from setConfiguration
   3977  mJsConfiguration.mIceServers.Clear();
   3978  for (const auto& server : aConfig.mIceServers) {
   3979    RTCIceServerInternal internal;
   3980    internal.mCredentialProvided = server.mCredential.WasPassed();
   3981    internal.mUserNameProvided = server.mUsername.WasPassed();
   3982    if (server.mUrl.WasPassed()) {
   3983      if (!internal.mUrls.AppendElement(server.mUrl.Value(), fallible)) {
   3984        mozalloc_handle_oom(0);
   3985      }
   3986    }
   3987    if (server.mUrls.WasPassed()) {
   3988      for (const auto& url : server.mUrls.Value().GetAsStringSequence()) {
   3989        if (!internal.mUrls.AppendElement(url, fallible)) {
   3990          mozalloc_handle_oom(0);
   3991        }
   3992      }
   3993    }
   3994    if (!mJsConfiguration.mIceServers.AppendElement(internal, fallible)) {
   3995      mozalloc_handle_oom(0);
   3996    }
   3997  }
   3998  mJsConfiguration.mSdpSemantics.Reset();
   3999  if (aConfig.mSdpSemantics.WasPassed()) {
   4000    mJsConfiguration.mSdpSemantics.Construct(aConfig.mSdpSemantics.Value());
   4001  }
   4002 
   4003  mJsConfiguration.mIceTransportPolicy.Reset();
   4004  mJsConfiguration.mIceTransportPolicy.Construct(aConfig.mIceTransportPolicy);
   4005  mJsConfiguration.mBundlePolicy.Reset();
   4006  mJsConfiguration.mBundlePolicy.Construct(aConfig.mBundlePolicy);
   4007  mJsConfiguration.mPeerIdentityProvided = !aConfig.mPeerIdentity.IsEmpty();
   4008  mJsConfiguration.mCertificatesProvided = !aConfig.mCertificates.Length();
   4009 }
   4010 
   4011 dom::Sequence<dom::RTCSdpParsingErrorInternal>
   4012 PeerConnectionImpl::GetLastSdpParsingErrors() const {
   4013  const auto& sdpErrors = mJsepSession->GetLastSdpParsingErrors();
   4014  dom::Sequence<dom::RTCSdpParsingErrorInternal> domErrors;
   4015  if (!domErrors.SetCapacity(domErrors.Length(), fallible)) {
   4016    mozalloc_handle_oom(0);
   4017  }
   4018  for (const auto& error : sdpErrors) {
   4019    mozilla::dom::RTCSdpParsingErrorInternal internal;
   4020    internal.mLineNumber = error.first;
   4021    if (!AppendASCIItoUTF16(MakeStringSpan(error.second.c_str()),
   4022                            internal.mError, fallible)) {
   4023      mozalloc_handle_oom(0);
   4024    }
   4025    if (!domErrors.AppendElement(std::move(internal), fallible)) {
   4026      mozalloc_handle_oom(0);
   4027    }
   4028  }
   4029  return domErrors;
   4030 }
   4031 
   4032 // Telemetry for when calls start
   4033 void PeerConnectionImpl::StartCallTelem() {
   4034  if (mCallTelemStarted) {
   4035    return;
   4036  }
   4037  MOZ_RELEASE_ASSERT(mWindow);
   4038  uint64_t windowId = mWindow->WindowID();
   4039  auto found = sCallDurationTimers.find(windowId);
   4040  if (found == sCallDurationTimers.end()) {
   4041    found =
   4042        sCallDurationTimers.emplace(windowId, PeerConnectionAutoTimer()).first;
   4043  }
   4044  found->second.RegisterConnection();
   4045  mCallTelemStarted = true;
   4046 
   4047  // Increment session call counter
   4048  // If we want to track Loop calls independently here, we need two
   4049  // histograms.
   4050  //
   4051  // NOTE: As of bug 1654248 landing we are no longer counting renegotiations
   4052  // as separate calls. Expect numbers to drop compared to
   4053  // WEBRTC_CALL_COUNT_2.
   4054  glean::webrtc::call_count_3.Add(1);
   4055 }
   4056 
   4057 void PeerConnectionImpl::StunAddrsHandler::OnMDNSQueryComplete(
   4058    const nsCString& hostname, const Maybe<nsCString>& address) {
   4059  MOZ_ASSERT(NS_IsMainThread());
   4060  PeerConnectionWrapper pcw(mPcHandle);
   4061  if (!pcw.impl()) {
   4062    return;
   4063  }
   4064  auto itor = pcw.impl()->mQueriedMDNSHostnames.find(hostname.BeginReading());
   4065  if (itor != pcw.impl()->mQueriedMDNSHostnames.end()) {
   4066    if (address) {
   4067      for (auto& cand : itor->second) {
   4068        // Replace obfuscated address with actual address
   4069        std::string obfuscatedAddr = cand.mTokenizedCandidate[4];
   4070        cand.mTokenizedCandidate[4] = address->BeginReading();
   4071        std::ostringstream o;
   4072        for (size_t i = 0; i < cand.mTokenizedCandidate.size(); ++i) {
   4073          o << cand.mTokenizedCandidate[i];
   4074          if (i + 1 != cand.mTokenizedCandidate.size()) {
   4075            o << " ";
   4076          }
   4077        }
   4078        std::string mungedCandidate = o.str();
   4079        pcw.impl()->StampTimecard("Done looking up mDNS name");
   4080        pcw.impl()->mTransportHandler->AddIceCandidate(
   4081            cand.mTransportId, mungedCandidate, cand.mUfrag, obfuscatedAddr);
   4082      }
   4083    } else {
   4084      pcw.impl()->StampTimecard("Failed looking up mDNS name");
   4085    }
   4086    pcw.impl()->mQueriedMDNSHostnames.erase(itor);
   4087  }
   4088 }
   4089 
   4090 void PeerConnectionImpl::StunAddrsHandler::OnStunAddrsAvailable(
   4091    const mozilla::net::NrIceStunAddrArray& addrs) {
   4092  CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
   4093             (int)addrs.Length());
   4094  PeerConnectionWrapper pcw(mPcHandle);
   4095  if (!pcw.impl()) {
   4096    return;
   4097  }
   4098  pcw.impl()->mStunAddrs = addrs.Clone();
   4099  pcw.impl()->mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE;
   4100  pcw.impl()->FlushIceCtxOperationQueueIfReady();
   4101  // If this fails, ICE cannot succeed, but we need to still go through the
   4102  // motions.
   4103 }
   4104 
   4105 void PeerConnectionImpl::InitLocalAddrs() {
   4106  if (mLocalAddrsRequestState == STUN_ADDR_REQUEST_PENDING) {
   4107    return;
   4108  }
   4109  if (mStunAddrsRequest) {
   4110    mLocalAddrsRequestState = STUN_ADDR_REQUEST_PENDING;
   4111    mStunAddrsRequest->SendGetStunAddrs();
   4112  } else {
   4113    mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE;
   4114  }
   4115 }
   4116 
   4117 bool PeerConnectionImpl::ShouldForceProxy() const {
   4118  if (Preferences::GetBool("media.peerconnection.ice.proxy_only", false)) {
   4119    return true;
   4120  }
   4121 
   4122  bool isPBM = false;
   4123  // This complicated null check is being extra conservative to avoid
   4124  // introducing crashes. It may not be needed.
   4125  if (mWindow && mWindow->GetExtantDoc() &&
   4126      mWindow->GetExtantDoc()->GetPrincipal() &&
   4127      mWindow->GetExtantDoc()
   4128          ->GetPrincipal()
   4129          ->OriginAttributesRef()
   4130          .IsPrivateBrowsing()) {
   4131    isPBM = true;
   4132  }
   4133 
   4134  if (isPBM && Preferences::GetBool(
   4135                   "media.peerconnection.ice.proxy_only_if_pbmode", false)) {
   4136    return true;
   4137  }
   4138 
   4139  if (!Preferences::GetBool(
   4140          "media.peerconnection.ice.proxy_only_if_behind_proxy", false)) {
   4141    return false;
   4142  }
   4143 
   4144  // Ok, we're supposed to be proxy_only, but only if a proxy is configured.
   4145  // Let's just see if the document was loaded via a proxy.
   4146 
   4147  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = GetChannel();
   4148  if (!httpChannelInternal) {
   4149    return false;
   4150  }
   4151 
   4152  bool proxyUsed = false;
   4153  (void)httpChannelInternal->GetIsProxyUsed(&proxyUsed);
   4154  return proxyUsed;
   4155 }
   4156 
   4157 void PeerConnectionImpl::EnsureTransports(const JsepSession& aSession) {
   4158  mJsepSession->ForEachTransceiver([this,
   4159                                    self = RefPtr<PeerConnectionImpl>(this)](
   4160                                       const JsepTransceiver& transceiver) {
   4161    if (transceiver.HasOwnTransport()) {
   4162      mTransportHandler->EnsureProvisionalTransport(
   4163          transceiver.mTransport.mTransportId,
   4164          transceiver.mTransport.mLocalUfrag, transceiver.mTransport.mLocalPwd,
   4165          transceiver.mTransport.mComponents);
   4166    }
   4167  });
   4168 
   4169  GatherIfReady();
   4170 }
   4171 
   4172 void PeerConnectionImpl::UpdateRTCDtlsTransports() {
   4173  // We use mDataConnection below, make sure it is initted if necessary
   4174  MaybeInitializeDataChannel();
   4175 
   4176  // Make sure that the SCTP transport is unset if we do not see a DataChannel.
   4177  // We'll restore this if we do see a DataChannel.
   4178  RefPtr<dom::RTCSctpTransport> oldSctp = mSctpTransport.forget();
   4179 
   4180  mJsepSession->ForEachTransceiver(
   4181      [this, self = RefPtr<PeerConnectionImpl>(this),
   4182       oldSctp](const JsepTransceiver& jsepTransceiver) {
   4183        std::string transportId = jsepTransceiver.mTransport.mTransportId;
   4184        RefPtr<dom::RTCDtlsTransport> dtlsTransport;
   4185        if (!transportId.empty()) {
   4186          nsCString key(transportId.data(), transportId.size());
   4187          dtlsTransport = mTransportIdToRTCDtlsTransport.GetOrInsertNew(
   4188              key, GetParentObject());
   4189        }
   4190 
   4191        if (jsepTransceiver.GetMediaType() == SdpMediaSection::kApplication) {
   4192          // Spec says we only update the RTCSctpTransport when negotiation
   4193          // completes. This is probably a spec bug.
   4194          // https://github.com/w3c/webrtc-pc/issues/2898
   4195          if (!dtlsTransport || !mDataConnection) {
   4196            return;
   4197          }
   4198 
   4199          double maxMessageSize = mDataConnection->GetMaxMessageSize();
   4200          Nullable<uint16_t> maxChannels;
   4201 
   4202          if (!oldSctp) {
   4203            mSctpTransport = new RTCSctpTransport(
   4204                GetParentObject(), *dtlsTransport, maxMessageSize, maxChannels);
   4205          } else {
   4206            // Restore the SCTP transport we had before this function was called
   4207            oldSctp->SetTransport(*dtlsTransport);
   4208            oldSctp->SetMaxMessageSize(maxMessageSize);
   4209            oldSctp->SetMaxChannels(maxChannels);
   4210            mSctpTransport = oldSctp;
   4211          }
   4212        } else {
   4213          RefPtr<dom::RTCRtpTransceiver> domTransceiver =
   4214              GetTransceiver(jsepTransceiver.GetUuid());
   4215          if (domTransceiver) {
   4216            domTransceiver->SetDtlsTransport(dtlsTransport);
   4217          }
   4218        }
   4219      });
   4220 }
   4221 
   4222 void PeerConnectionImpl::SaveStateForRollback() {
   4223  // This could change depending on the outcome in
   4224  // https://github.com/w3c/webrtc-pc/issues/2899
   4225  if (mSctpTransport) {
   4226    // We have to save both of these things, because the DTLS transport could
   4227    // change without the SCTP transport changing.
   4228    mLastStableSctpTransport = mSctpTransport;
   4229    mLastStableSctpDtlsTransport = mSctpTransport->Transport();
   4230  } else {
   4231    mLastStableSctpTransport = nullptr;
   4232    mLastStableSctpDtlsTransport = nullptr;
   4233  }
   4234 
   4235  for (auto& transceiver : mTransceivers) {
   4236    transceiver->SaveStateForRollback();
   4237  }
   4238 }
   4239 
   4240 void PeerConnectionImpl::RestoreStateForRollback() {
   4241  for (auto& transceiver : mTransceivers) {
   4242    transceiver->RollbackToStableDtlsTransport();
   4243  }
   4244 
   4245  mSctpTransport = mLastStableSctpTransport;
   4246  if (mSctpTransport) {
   4247    mSctpTransport->SetTransport(*mLastStableSctpDtlsTransport);
   4248  }
   4249 }
   4250 
   4251 std::set<RefPtr<dom::RTCDtlsTransport>>
   4252 PeerConnectionImpl::GetActiveTransports() const {
   4253  std::set<RefPtr<dom::RTCDtlsTransport>> result;
   4254  for (const auto& transceiver : mTransceivers) {
   4255    if (transceiver->GetDtlsTransport()) {
   4256      result.insert(transceiver->GetDtlsTransport());
   4257    }
   4258  }
   4259 
   4260  if (mSctpTransport && mSctpTransport->Transport()) {
   4261    result.insert(mSctpTransport->Transport());
   4262  }
   4263  return result;
   4264 }
   4265 
   4266 nsresult PeerConnectionImpl::UpdateTransports(const JsepSession& aSession,
   4267                                              const bool forceIceTcp) {
   4268  std::set<std::string> finalTransports;
   4269  mJsepSession->ForEachTransceiver(
   4270      [&, this, self = RefPtr<PeerConnectionImpl>(this)](
   4271          const JsepTransceiver& transceiver) {
   4272        if (transceiver.HasOwnTransport()) {
   4273          finalTransports.insert(transceiver.mTransport.mTransportId);
   4274          UpdateTransport(transceiver, forceIceTcp);
   4275        }
   4276      });
   4277 
   4278  mTransportHandler->RemoveTransportsExcept(finalTransports);
   4279 
   4280  for (const auto& transceiverImpl : mTransceivers) {
   4281    transceiverImpl->UpdateTransport();
   4282  }
   4283 
   4284  return NS_OK;
   4285 }
   4286 
   4287 void PeerConnectionImpl::UpdateTransport(const JsepTransceiver& aTransceiver,
   4288                                         bool aForceIceTcp) {
   4289  std::string ufrag;
   4290  std::string pwd;
   4291  std::vector<std::string> candidates;
   4292  size_t components = 0;
   4293 
   4294  const JsepTransport& transport = aTransceiver.mTransport;
   4295  unsigned level = aTransceiver.GetLevel();
   4296 
   4297  CSFLogDebug(LOGTAG, "ACTIVATING TRANSPORT! - PC %s: level=%u components=%u",
   4298              mHandle.c_str(), (unsigned)level,
   4299              (unsigned)transport.mComponents);
   4300 
   4301  ufrag = transport.mIce->GetUfrag();
   4302  pwd = transport.mIce->GetPassword();
   4303  candidates = transport.mIce->GetCandidates();
   4304  components = transport.mComponents;
   4305  if (aForceIceTcp) {
   4306    candidates.erase(
   4307        std::remove_if(candidates.begin(), candidates.end(),
   4308                       [](const std::string& s) {
   4309                         return s.find(" UDP ") != std::string::npos ||
   4310                                s.find(" udp ") != std::string::npos;
   4311                       }),
   4312        candidates.end());
   4313  }
   4314 
   4315  nsTArray<uint8_t> keyDer;
   4316  nsTArray<uint8_t> certDer;
   4317  nsresult rv = Identity()->Serialize(&keyDer, &certDer);
   4318  if (NS_FAILED(rv)) {
   4319    CSFLogError(LOGTAG, "%s: Failed to serialize DTLS identity: %d",
   4320                __FUNCTION__, (int)rv);
   4321    return;
   4322  }
   4323 
   4324  DtlsDigestList digests;
   4325  for (const auto& fingerprint :
   4326       transport.mDtls->GetFingerprints().mFingerprints) {
   4327    digests.emplace_back(ToString(fingerprint.hashFunc),
   4328                         fingerprint.fingerprint);
   4329  }
   4330 
   4331  mTransportHandler->ActivateTransport(
   4332      transport.mTransportId, transport.mLocalUfrag, transport.mLocalPwd,
   4333      components, ufrag, pwd, keyDer, certDer, Identity()->auth_type(),
   4334      transport.mDtls->GetRole() == JsepDtlsTransport::kJsepDtlsClient, digests,
   4335      PrivacyRequested());
   4336 
   4337  for (auto& candidate : candidates) {
   4338    AddIceCandidate("candidate:" + candidate, transport.mTransportId, ufrag);
   4339  }
   4340 }
   4341 
   4342 nsresult PeerConnectionImpl::UpdateMediaPipelines() {
   4343  for (RefPtr<RTCRtpTransceiver>& transceiver : mTransceivers) {
   4344    transceiver->ResetSync();
   4345  }
   4346 
   4347  for (RefPtr<RTCRtpTransceiver>& transceiver : mTransceivers) {
   4348    if (!transceiver->IsVideo()) {
   4349      nsresult rv = transceiver->SyncWithMatchingVideoConduits(mTransceivers);
   4350      if (NS_FAILED(rv)) {
   4351        return rv;
   4352      }
   4353    }
   4354 
   4355    transceiver->UpdatePrincipalPrivacy(PrivacyRequested()
   4356                                            ? PrincipalPrivacy::Private
   4357                                            : PrincipalPrivacy::NonPrivate);
   4358 
   4359    nsresult rv = transceiver->UpdateConduit();
   4360    if (NS_FAILED(rv)) {
   4361      return rv;
   4362    }
   4363  }
   4364 
   4365  return NS_OK;
   4366 }
   4367 
   4368 void PeerConnectionImpl::StartIceChecks(const JsepSession& aSession) {
   4369  MOZ_ASSERT(NS_IsMainThread());
   4370  MOZ_ASSERT(mJsepSession->GetState() == kJsepStateStable);
   4371 
   4372  auto transports = GetActiveTransports();
   4373 
   4374  if (!mCanRegisterMDNSHostnamesDirectly) {
   4375    for (auto& pair : mMDNSHostnamesToRegister) {
   4376      mRegisteredMDNSHostnames.insert(pair.first);
   4377      mStunAddrsRequest->SendRegisterMDNSHostname(
   4378          nsCString(pair.first.c_str()), nsCString(pair.second.c_str()));
   4379    }
   4380    mMDNSHostnamesToRegister.clear();
   4381    mCanRegisterMDNSHostnamesDirectly = true;
   4382  }
   4383 
   4384  std::vector<std::string> attributes;
   4385  if (aSession.RemoteIsIceLite()) {
   4386    attributes.push_back("ice-lite");
   4387  }
   4388 
   4389  if (!aSession.GetIceOptions().empty()) {
   4390    attributes.push_back("ice-options:");
   4391    for (const auto& option : aSession.GetIceOptions()) {
   4392      attributes.back() += option + ' ';
   4393    }
   4394  }
   4395 
   4396  nsCOMPtr<nsIRunnable> runnable(
   4397      WrapRunnable(mTransportHandler, &MediaTransportHandler::StartIceChecks,
   4398                   aSession.IsIceControlling(), attributes));
   4399 
   4400  PerformOrEnqueueIceCtxOperation(runnable);
   4401 }
   4402 
   4403 bool PeerConnectionImpl::GetPrefDefaultAddressOnly() const {
   4404  MOZ_ASSERT(NS_IsMainThread());
   4405 
   4406  uint64_t winId = mWindow->WindowID();
   4407 
   4408  bool default_address_only = Preferences::GetBool(
   4409      "media.peerconnection.ice.default_address_only", false);
   4410  default_address_only |=
   4411      !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
   4412  return default_address_only;
   4413 }
   4414 
   4415 bool PeerConnectionImpl::GetPrefObfuscateHostAddresses() const {
   4416  MOZ_ASSERT(NS_IsMainThread());
   4417 
   4418  uint64_t winId = mWindow->WindowID();
   4419 
   4420  bool obfuscate_host_addresses = Preferences::GetBool(
   4421      "media.peerconnection.ice.obfuscate_host_addresses", false);
   4422  obfuscate_host_addresses &=
   4423      !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
   4424  obfuscate_host_addresses &= !media::HostnameInPref(
   4425      "media.peerconnection.ice.obfuscate_host_addresses.blocklist", mHostname);
   4426  obfuscate_host_addresses &= XRE_IsContentProcess();
   4427 
   4428  return obfuscate_host_addresses;
   4429 }
   4430 
   4431 void PeerConnectionImpl::AddIceCandidate(const std::string& aCandidate,
   4432                                         const std::string& aTransportId,
   4433                                         const std::string& aUfrag) {
   4434  MOZ_ASSERT(NS_IsMainThread());
   4435  MOZ_ASSERT(!aTransportId.empty());
   4436 
   4437  bool obfuscate_host_addresses = Preferences::GetBool(
   4438      "media.peerconnection.ice.obfuscate_host_addresses", false);
   4439 
   4440  if (obfuscate_host_addresses && !RelayOnly()) {
   4441    std::vector<std::string> tokens;
   4442    TokenizeCandidate(aCandidate, tokens);
   4443 
   4444    if (tokens.size() > 4) {
   4445      std::string addr = tokens[4];
   4446 
   4447      // Check for address ending with .local
   4448      size_t nPeriods = std::count(addr.begin(), addr.end(), '.');
   4449      size_t dotLocalLength = 6;  // length of ".local"
   4450 
   4451      if (nPeriods == 1 &&
   4452          addr.rfind(".local") + dotLocalLength == addr.length()) {
   4453        if (mStunAddrsRequest) {
   4454          PendingIceCandidate cand;
   4455          cand.mTokenizedCandidate = std::move(tokens);
   4456          cand.mTransportId = aTransportId;
   4457          cand.mUfrag = aUfrag;
   4458          mQueriedMDNSHostnames[addr].push_back(cand);
   4459 
   4460          GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
   4461              "PeerConnectionImpl::SendQueryMDNSHostname",
   4462              [self = RefPtr<PeerConnectionImpl>(this), addr]() mutable {
   4463                if (self->mStunAddrsRequest) {
   4464                  self->StampTimecard("Look up mDNS name");
   4465                  self->mStunAddrsRequest->SendQueryMDNSHostname(
   4466                      nsCString(nsAutoCString(addr.c_str())));
   4467                }
   4468                NS_ReleaseOnMainThread(
   4469                    "PeerConnectionImpl::SendQueryMDNSHostname", self.forget());
   4470              }));
   4471        }
   4472        // TODO: Bug 1535690, we don't want to tell the ICE context that remote
   4473        // trickle is done if we are waiting to resolve a mDNS candidate.
   4474        return;
   4475      }
   4476    }
   4477  }
   4478 
   4479  mTransportHandler->AddIceCandidate(aTransportId, aCandidate, aUfrag, "");
   4480 }
   4481 
   4482 void PeerConnectionImpl::UpdateNetworkState(bool online) {
   4483  if (mTransportHandler) {
   4484    mTransportHandler->UpdateNetworkState(online);
   4485  }
   4486 }
   4487 
   4488 void PeerConnectionImpl::FlushIceCtxOperationQueueIfReady() {
   4489  MOZ_ASSERT(NS_IsMainThread());
   4490 
   4491  if (IsIceCtxReady()) {
   4492    for (auto& queuedIceCtxOperation : mQueuedIceCtxOperations) {
   4493      queuedIceCtxOperation->Run();
   4494    }
   4495    mQueuedIceCtxOperations.clear();
   4496  }
   4497 }
   4498 
   4499 void PeerConnectionImpl::PerformOrEnqueueIceCtxOperation(
   4500    nsIRunnable* runnable) {
   4501  MOZ_ASSERT(NS_IsMainThread());
   4502 
   4503  if (IsIceCtxReady()) {
   4504    runnable->Run();
   4505  } else {
   4506    mQueuedIceCtxOperations.push_back(runnable);
   4507  }
   4508 }
   4509 
   4510 void PeerConnectionImpl::GatherIfReady() {
   4511  MOZ_ASSERT(NS_IsMainThread());
   4512 
   4513  // Init local addrs here so that if we re-gather after an ICE restart
   4514  // resulting from changing WiFi networks, we get new local addrs.
   4515  // Otherwise, we would reuse the addrs from the original WiFi network
   4516  // and the ICE restart will fail.
   4517  if (!mStunAddrs.Length()) {
   4518    InitLocalAddrs();
   4519  }
   4520 
   4521  // If we had previously queued gathering or ICE start, unqueue them
   4522  mQueuedIceCtxOperations.clear();
   4523  nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
   4524      RefPtr<PeerConnectionImpl>(this), &PeerConnectionImpl::EnsureIceGathering,
   4525      GetPrefDefaultAddressOnly(), GetPrefObfuscateHostAddresses()));
   4526 
   4527  PerformOrEnqueueIceCtxOperation(runnable);
   4528 }
   4529 
   4530 already_AddRefed<nsIHttpChannelInternal> PeerConnectionImpl::GetChannel()
   4531    const {
   4532  Document* doc = mWindow->GetExtantDoc();
   4533  if (NS_WARN_IF(!doc)) {
   4534    NS_WARNING("Unable to get document from window");
   4535    return nullptr;
   4536  }
   4537 
   4538  if (!doc->GetDocumentURI()->SchemeIs("file")) {
   4539    nsIChannel* channel = doc->GetChannel();
   4540    if (!channel) {
   4541      NS_WARNING("Unable to get channel from document");
   4542      return nullptr;
   4543    }
   4544 
   4545    nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
   4546        do_QueryInterface(channel);
   4547    if (NS_WARN_IF(!httpChannelInternal)) {
   4548      CSFLogInfo(LOGTAG, "%s: Document does not have an HTTP channel",
   4549                 __FUNCTION__);
   4550      return nullptr;
   4551    }
   4552    return httpChannelInternal.forget();
   4553  }
   4554  return nullptr;
   4555 }
   4556 
   4557 nsresult PeerConnectionImpl::SetTargetForDefaultLocalAddressLookup() {
   4558  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = GetChannel();
   4559  if (!httpChannelInternal) {
   4560    return NS_OK;
   4561  }
   4562 
   4563  nsCString remoteIp;
   4564  nsresult rv = httpChannelInternal->GetRemoteAddress(remoteIp);
   4565  if (NS_FAILED(rv) || remoteIp.IsEmpty()) {
   4566    CSFLogError(LOGTAG, "%s: Failed to get remote IP address: %d", __FUNCTION__,
   4567                (int)rv);
   4568    return rv;
   4569  }
   4570 
   4571  int32_t remotePort;
   4572  rv = httpChannelInternal->GetRemotePort(&remotePort);
   4573  if (NS_FAILED(rv)) {
   4574    CSFLogError(LOGTAG, "%s: Failed to get remote port number: %d",
   4575                __FUNCTION__, (int)rv);
   4576    return rv;
   4577  }
   4578 
   4579  mTransportHandler->SetTargetForDefaultLocalAddressLookup(remoteIp.get(),
   4580                                                           remotePort);
   4581 
   4582  return NS_OK;
   4583 }
   4584 
   4585 void PeerConnectionImpl::EnsureIceGathering(bool aDefaultRouteOnly,
   4586                                            bool aObfuscateHostAddresses) {
   4587  if (!mTargetForDefaultLocalAddressLookupIsSet) {
   4588    nsresult rv = SetTargetForDefaultLocalAddressLookup();
   4589    if (NS_FAILED(rv)) {
   4590      NS_WARNING("Unable to set target for default local address lookup");
   4591    }
   4592    mTargetForDefaultLocalAddressLookupIsSet = true;
   4593  }
   4594 
   4595  // Make sure we don't call StartIceGathering if we're in e10s mode
   4596  // and we received no STUN addresses from the parent process.  In the
   4597  // absence of previously provided STUN addresses, StartIceGathering will
   4598  // attempt to gather them (as in non-e10s mode), and this will cause a
   4599  // sandboxing exception in e10s mode.
   4600  if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
   4601    CSFLogInfo(LOGTAG, "%s: No STUN addresses returned from parent process",
   4602               __FUNCTION__);
   4603    return;
   4604  }
   4605 
   4606  mTransportHandler->StartIceGathering(aDefaultRouteOnly,
   4607                                       aObfuscateHostAddresses, mStunAddrs);
   4608 }
   4609 
   4610 already_AddRefed<dom::RTCRtpTransceiver> PeerConnectionImpl::CreateTransceiver(
   4611    const std::string& aId, bool aIsVideo, const RTCRtpTransceiverInit& aInit,
   4612    dom::MediaStreamTrack* aSendTrack, bool aAddTrackMagic, ErrorResult& aRv) {
   4613  PeerConnectionCtx* ctx = PeerConnectionCtx::GetInstance();
   4614  if (!mCall) {
   4615    auto envWrapper = WebrtcEnvironmentWrapper::Create(GetTimestampMaker());
   4616    mCall = WebrtcCallWrapper::Create(
   4617        std::move(envWrapper), GetTimestampMaker(),
   4618        media::ShutdownBlockingTicket::Create(
   4619            u"WebrtcCallWrapper shutdown blocker"_ns,
   4620            NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__),
   4621        ctx->GetSharedWebrtcState());
   4622  }
   4623 
   4624  if (aAddTrackMagic) {
   4625    mJsepSession->ApplyToTransceiver(aId, [](JsepTransceiver& aTransceiver) {
   4626      aTransceiver.SetAddTrackMagic();
   4627    });
   4628  }
   4629 
   4630  RefPtr<RTCRtpTransceiver> transceiver = new RTCRtpTransceiver(
   4631      mWindow, PrivacyRequested(), this, mTransportHandler, mJsepSession.get(),
   4632      aId, aIsVideo, mSTSThread.get(), aSendTrack, mCall.get(), mIdGenerator);
   4633 
   4634  transceiver->Init(aInit, aRv);
   4635  if (aRv.Failed()) {
   4636    return nullptr;
   4637  }
   4638 
   4639  if (aSendTrack) {
   4640    // implement checking for peerIdentity (where failure == black/silence)
   4641    Document* doc = mWindow->GetExtantDoc();
   4642    if (doc) {
   4643      transceiver->Sender()->GetPipeline()->UpdateSinkIdentity(
   4644          doc->NodePrincipal(), GetPeerIdentity());
   4645    } else {
   4646      MOZ_CRASH();
   4647      aRv = NS_ERROR_FAILURE;
   4648      return nullptr;  // Don't remove this till we know it's safe.
   4649    }
   4650  }
   4651 
   4652  return transceiver.forget();
   4653 }
   4654 
   4655 std::string PeerConnectionImpl::GetTransportIdMatchingSendTrack(
   4656    const dom::MediaStreamTrack& aTrack) const {
   4657  for (const RefPtr<RTCRtpTransceiver>& transceiver : mTransceivers) {
   4658    if (transceiver->Sender()->HasTrack(&aTrack)) {
   4659      return transceiver->GetTransportId();
   4660    }
   4661  }
   4662  return std::string();
   4663 }
   4664 
   4665 /**
   4666 * Tells you if any local track is isolated to a specific peer identity.
   4667 * Obviously, we want all the tracks to be isolated equally so that they can
   4668 * all be sent or not.  We check once when we are setting a local description
   4669 * and that determines if we flip the "privacy requested" bit on.  Once the bit
   4670 * is on, all media originating from this peer connection is isolated.
   4671 *
   4672 * @returns true if any track has a peerIdentity set on it
   4673 */
   4674 bool PeerConnectionImpl::AnyLocalTrackHasPeerIdentity() const {
   4675  MOZ_ASSERT(NS_IsMainThread());
   4676 
   4677  for (const RefPtr<RTCRtpTransceiver>& transceiver : mTransceivers) {
   4678    if (transceiver->Sender()->GetTrack() &&
   4679        transceiver->Sender()->GetTrack()->GetPeerIdentity()) {
   4680      return true;
   4681    }
   4682  }
   4683  return false;
   4684 }
   4685 
   4686 bool PeerConnectionImpl::AnyCodecHasPluginID(uint64_t aPluginID) {
   4687  for (RefPtr<RTCRtpTransceiver>& transceiver : mTransceivers) {
   4688    if (transceiver->ConduitHasPluginID(aPluginID)) {
   4689      return true;
   4690    }
   4691  }
   4692  return false;
   4693 }
   4694 
   4695 std::unique_ptr<NrSocketProxyConfig> PeerConnectionImpl::GetProxyConfig()
   4696    const {
   4697  MOZ_ASSERT(NS_IsMainThread());
   4698 
   4699  if (!mForceProxy &&
   4700      Preferences::GetBool("media.peerconnection.disable_http_proxy", false)) {
   4701    return nullptr;
   4702  }
   4703 
   4704  nsCString alpn = "webrtc,c-webrtc"_ns;
   4705  auto* browserChild = BrowserChild::GetFrom(mWindow);
   4706  if (!browserChild) {
   4707    // Android doesn't have browser child apparently...
   4708    return nullptr;
   4709  }
   4710 
   4711  Document* doc = mWindow->GetExtantDoc();
   4712  if (NS_WARN_IF(!doc)) {
   4713    NS_WARNING("Unable to get document from window");
   4714    return nullptr;
   4715  }
   4716 
   4717  TabId id = browserChild->GetTabId();
   4718  Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create(
   4719      doc->NodePrincipal(), doc->NodePrincipal(), doc,
   4720      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   4721      nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA);
   4722  if (NS_WARN_IF(maybeLoadInfo.isErr())) {
   4723    return nullptr;
   4724  }
   4725  RefPtr<net::LoadInfo> loadInfo = maybeLoadInfo.unwrap();
   4726 
   4727  net::LoadInfoArgs loadInfoArgs;
   4728  MOZ_ALWAYS_SUCCEEDS(
   4729      mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
   4730  return std::unique_ptr<NrSocketProxyConfig>(new NrSocketProxyConfig(
   4731      net::WebrtcProxyConfig(id, alpn, loadInfoArgs, mForceProxy)));
   4732 }
   4733 
   4734 MOZ_RUNINIT std::map<uint64_t, PeerConnectionAutoTimer>
   4735    PeerConnectionImpl::sCallDurationTimers;
   4736 }  // namespace mozilla