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