From 40a355a42d4a9444dc753c04c6608dade2f06a23 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:13:27 +0200 Subject: Adding upstream version 125.0.1. Signed-off-by: Daniel Baumann --- dom/media/webrtc/jsapi/PeerConnectionImpl.cpp | 562 +++++++++++++++++++------- 1 file changed, 412 insertions(+), 150 deletions(-) (limited to 'dom/media/webrtc/jsapi/PeerConnectionImpl.cpp') diff --git a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp index 985100153a..9afa7e5dd2 100644 --- a/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp +++ b/dom/media/webrtc/jsapi/PeerConnectionImpl.cpp @@ -85,6 +85,7 @@ #include "mozilla/dom/RTCCertificate.h" #include "mozilla/dom/RTCSctpTransportBinding.h" // RTCSctpTransportState #include "mozilla/dom/RTCDtlsTransportBinding.h" // RTCDtlsTransportState +#include "mozilla/dom/RTCIceTransportBinding.h" // RTCIceTransportState #include "mozilla/dom/RTCRtpReceiverBinding.h" #include "mozilla/dom/RTCRtpSenderBinding.h" #include "mozilla/dom/RTCStatsReportBinding.h" @@ -249,19 +250,41 @@ void PeerConnectionAutoTimer::UnregisterConnection(bool aContainedAV) { bool PeerConnectionAutoTimer::IsStopped() { return mRefCnt == 0; } +// There is not presently an implementation of these for nsTHashMap :( +inline void ImplCycleCollectionUnlink( + PeerConnectionImpl::RTCDtlsTransportMap& aMap) { + for (auto& tableEntry : aMap) { + ImplCycleCollectionUnlink(*tableEntry.GetModifiableData()); + } + aMap.Clear(); +} + +inline void ImplCycleCollectionTraverse( + nsCycleCollectionTraversalCallback& aCallback, + PeerConnectionImpl::RTCDtlsTransportMap& aMap, const char* aName, + uint32_t aFlags = 0) { + for (auto& tableEntry : aMap) { + ImplCycleCollectionTraverse(aCallback, *tableEntry.GetModifiableData(), + aName, aFlags); + } +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(PeerConnectionImpl) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PeerConnectionImpl) tmp->Close(); tmp->BreakCycles(); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mPCObserver, mWindow, mCertificate, - mSTSThread, mReceiveStreams, mOperations, - mSctpTransport, mKungFuDeathGrip) + NS_IMPL_CYCLE_COLLECTION_UNLINK( + mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams, + mOperations, mTransportIdToRTCDtlsTransport, mSctpTransport, + mLastStableSctpTransport, mLastStableSctpDtlsTransport, mKungFuDeathGrip) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PeerConnectionImpl) NS_IMPL_CYCLE_COLLECTION_TRAVERSE( mPCObserver, mWindow, mCertificate, mSTSThread, mReceiveStreams, - mOperations, mTransceivers, mSctpTransport, mKungFuDeathGrip) + mOperations, mTransceivers, mTransportIdToRTCDtlsTransport, + mSctpTransport, mLastStableSctpTransport, mLastStableSctpDtlsTransport, + mKungFuDeathGrip) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTING_ADDREF(PeerConnectionImpl) @@ -849,6 +872,7 @@ nsresult PeerConnectionImpl::GetDatachannelParameters( if (!datachannelTransceiver || !datachannelTransceiver->mTransport.mComponents || + !datachannelTransceiver->mTransport.mDtls || !datachannelTransceiver->mSendTrack.GetNegotiatedDetails()) { return NS_ERROR_FAILURE; } @@ -1061,7 +1085,7 @@ bool PeerConnectionImpl::CreatedSender(const dom::RTCRtpSender& aSender) const { return aSender.IsMyPc(this); } -nsresult PeerConnectionImpl::InitializeDataChannel() { +nsresult PeerConnectionImpl::MaybeInitializeDataChannel() { PC_AUTO_ENTER_API_CALL(false); CSFLogDebug(LOGTAG, "%s", __FUNCTION__); @@ -1440,6 +1464,16 @@ void PeerConnectionImpl::UpdateNegotiationNeeded() { })); } +RefPtr PeerConnectionImpl::GetTransceiver( + const std::string& aTransceiverId) { + for (const auto& transceiver : mTransceivers) { + if (transceiver->GetJsepTransceiverId() == aTransceiverId) { + return transceiver; + } + } + return nullptr; +} + void PeerConnectionImpl::NotifyDataChannel( already_AddRefed aChannel) { PC_AUTO_ENTER_API_CALL_NO_CHECK(); @@ -1637,7 +1671,6 @@ JsepSdpType ToJsepSdpType(dom::RTCSdpType aType) { return kJsepSdpAnswer; case dom::RTCSdpType::Rollback: return kJsepSdpRollback; - case dom::RTCSdpType::EndGuard_:; } MOZ_CRASH("Nonexistent dom::RTCSdpType"); @@ -1989,10 +2022,14 @@ nsresult PeerConnectionImpl::OnAlpnNegotiated(bool aPrivacyRequested) { void PeerConnectionImpl::OnDtlsStateChange(const std::string& aTransportId, TransportLayer::State aState) { - auto it = mTransportIdToRTCDtlsTransport.find(aTransportId); - if (it != mTransportIdToRTCDtlsTransport.end()) { - it->second->UpdateState(aState); + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { + return; } + + dtlsTransport->UpdateState(aState); // Whenever the state of an RTCDtlsTransport changes or when the [[IsClosed]] // slot turns true, the user agent MUST update the connection state by // queueing a task that runs the following steps: @@ -2024,9 +2061,9 @@ RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const { // Would use a bitset, but that requires lots of static_cast // Oh well. std::set statesFound; - for (const auto& [id, dtlsTransport] : mTransportIdToRTCDtlsTransport) { - Unused << id; - statesFound.insert(dtlsTransport->State()); + std::set> transports(GetActiveTransports()); + for (const auto& transport : transports) { + statesFound.insert(transport->State()); } // failed The previous state doesn't apply, and either @@ -2075,9 +2112,9 @@ RTCPeerConnectionState PeerConnectionImpl::GetNewConnectionState() const { bool PeerConnectionImpl::UpdateConnectionState() { auto newState = GetNewConnectionState(); if (newState != mConnectionState) { - CSFLogDebug(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, - static_cast(mConnectionState), static_cast(newState), - this); + CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, + static_cast(mConnectionState), static_cast(newState), + this); mConnectionState = newState; if (mConnectionState != RTCPeerConnectionState::Closed) { return true; @@ -2569,7 +2606,7 @@ PeerConnectionImpl::Close() { transceiver->Close(); } - mTransportIdToRTCDtlsTransport.clear(); + mTransportIdToRTCDtlsTransport.Clear(); mQueuedIceCtxOperations.clear(); @@ -2964,18 +3001,25 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( InvalidateLastReturnedParameters(); } + if (aSdpType == dom::RTCSdpType::Offer && + mSignalingState == RTCSignalingState::Stable) { + // If description is of type "offer" and + // connection.[[SignalingState]] is "stable" then for each + // transceiver in connection's set of transceivers, run the following + // steps: + SaveStateForRollback(); + } + // Section 4.4.1.5 Set the RTCSessionDescription: if (aSdpType == dom::RTCSdpType::Rollback) { // - step 4.5.10, type is rollback - RollbackRTCDtlsTransports(); + RestoreStateForRollback(); } else if (!(aRemote && aSdpType == dom::RTCSdpType::Offer)) { // - step 4.5.9 type is not rollback // - step 4.5.9.1 when remote is false // - step 4.5.9.2.13 when remote is true, type answer or pranswer // More simply: not rollback, and not for remote offers. - bool markAsStable = aSdpType == dom::RTCSdpType::Offer && - mSignalingState == RTCSignalingState::Stable; - UpdateRTCDtlsTransports(markAsStable); + UpdateRTCDtlsTransports(); } // Did we just apply a local description? @@ -2992,11 +3036,6 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( } if (mJsepSession->GetState() == kJsepStateStable) { - if (aSdpType != dom::RTCSdpType::Rollback) { - // We need this initted for UpdateTransports - InitializeDataChannel(); - } - // If we're rolling back a local offer, we might need to remove some // transports, and stomp some MediaPipeline setup, but nothing further // needs to be done. @@ -3071,6 +3110,10 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( // Spec does not actually tell us to do this, but that is probably a // spec bug. // https://github.com/w3c/webrtc-pc/issues/2817 + bool gatheringStateChanged = UpdateIceGatheringState(); + + bool iceConnectionStateChanged = UpdateIceConnectionState(); + bool connectionStateChanged = UpdateConnectionState(); // This only gets populated for remote descriptions @@ -3104,6 +3147,16 @@ void PeerConnectionImpl::DoSetDescriptionSuccessPostProcessing( pcObserver->OnStateChange(PCObserverStateType::SignalingState, jrv); } + if (gatheringStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceGatheringState, + jrv); + } + + if (iceConnectionStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, + jrv); + } + if (connectionStateChanged) { pcObserver->OnStateChange(PCObserverStateType::ConnectionState, jrv); } @@ -3290,61 +3343,162 @@ void PeerConnectionImpl::SendLocalIceCandidateToContent( } void PeerConnectionImpl::IceConnectionStateChange( - dom::RTCIceConnectionState domState) { + const std::string& aTransportId, dom::RTCIceTransportState domState) { + // If connection.[[IsClosed]] is true, abort these steps. PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); - CSFLogDebug(LOGTAG, "%s: %d -> %d", __FUNCTION__, - static_cast(mIceConnectionState), - static_cast(domState)); + CSFLogDebug(LOGTAG, "IceConnectionStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast(domState), this); - if (domState == mIceConnectionState) { - // no work to be done since the states are the same. - // this can happen during ICE rollback situations. + // Let transport be the RTCIceTransport whose state is changing. + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { return; } + RefPtr transport = dtlsTransport->IceTransport(); - mIceConnectionState = domState; + if (domState == RTCIceTransportState::Closed) { + mTransportIdToRTCDtlsTransport.Remove(key); + } - // Would be nice if we had a means of converting one of these dom enums - // to a string that wasn't almost as much text as this switch statement... - switch (mIceConnectionState) { - case RTCIceConnectionState::New: - STAMP_TIMECARD(mTimeCard, "Ice state: new"); - break; - case RTCIceConnectionState::Checking: - // For telemetry - mIceStartTime = TimeStamp::Now(); - STAMP_TIMECARD(mTimeCard, "Ice state: checking"); - break; - case RTCIceConnectionState::Connected: - STAMP_TIMECARD(mTimeCard, "Ice state: connected"); - StartCallTelem(); - break; - case RTCIceConnectionState::Completed: - STAMP_TIMECARD(mTimeCard, "Ice state: completed"); - break; - case RTCIceConnectionState::Failed: - STAMP_TIMECARD(mTimeCard, "Ice state: failed"); - break; - case RTCIceConnectionState::Disconnected: - STAMP_TIMECARD(mTimeCard, "Ice state: disconnected"); - break; - case RTCIceConnectionState::Closed: - STAMP_TIMECARD(mTimeCard, "Ice state: closed"); - break; - default: - MOZ_ASSERT_UNREACHABLE("Unexpected mIceConnectionState!"); + // Let selectedCandidatePairChanged be false. + // TODO(bug 1307994) + + // Let transportIceConnectionStateChanged be false. + bool transportIceConnectionStateChanged = false; + + // Let connectionIceConnectionStateChanged be false. + bool connectionIceConnectionStateChanged = false; + + // Let connectionStateChanged be false. + bool connectionStateChanged = false; + + if (transport->State() == domState) { + return; + } + + // If transport's RTCIceTransportState was changed, run the following steps: + + // Set transport.[[IceTransportState]] to the new indicated + // RTCIceTransportState. + transport->SetState(domState); + + // Set transportIceConnectionStateChanged to true. + transportIceConnectionStateChanged = true; + + // Set connection.[[IceConnectionState]] to the value of deriving a new state + // value as described by the RTCIceConnectionState enum. + if (UpdateIceConnectionState()) { + // If connection.[[IceConnectionState]] changed in the previous step, set + // connectionIceConnectionStateChanged to true. + connectionIceConnectionStateChanged = true; + } + + // Set connection.[[ConnectionState]] to the value of deriving a new state + // value as described by the RTCPeerConnectionState enum. + if (UpdateConnectionState()) { + // If connection.[[ConnectionState]] changed in the previous step, set + // connectionStateChanged to true. + connectionStateChanged = true; + } + + // If selectedCandidatePairChanged is true, fire an event named + // selectedcandidatepairchange at transport. + // TODO(bug 1307994) + + // If transportIceConnectionStateChanged is true, fire an event named + // statechange at transport. + if (transportIceConnectionStateChanged) { + transport->FireStateChangeEvent(); } - bool connectionStateChanged = UpdateConnectionState(); WrappableJSErrorResult rv; RefPtr pcObserver(mPCObserver); - pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv); + + // If connectionIceConnectionStateChanged is true, fire an event named + // iceconnectionstatechange at connection. + if (connectionIceConnectionStateChanged) { + pcObserver->OnStateChange(PCObserverStateType::IceConnectionState, rv); + } + + // If connectionStateChanged is true, fire an event named + // connectionstatechange at connection. if (connectionStateChanged) { pcObserver->OnStateChange(PCObserverStateType::ConnectionState, rv); } } +RTCIceConnectionState PeerConnectionImpl::GetNewIceConnectionState() const { + // closed The RTCPeerConnection object's [[IsClosed]] slot is true. + if (IsClosed()) { + return RTCIceConnectionState::Closed; + } + + // Would use a bitset, but that requires lots of static_cast + // Oh well. + std::set statesFound; + std::set> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr iceTransport = transport->IceTransport(); + CSFLogWarn(LOGTAG, "GetNewIceConnectionState: %p %d", iceTransport.get(), + static_cast(iceTransport->State())); + statesFound.insert(iceTransport->State()); + } + + // failed None of the previous states apply and any RTCIceTransports are + // in the "failed" state. + if (statesFound.count(RTCIceTransportState::Failed)) { + return RTCIceConnectionState::Failed; + } + + // disconnected None of the previous states apply and any + // RTCIceTransports are in the "disconnected" state. + if (statesFound.count(RTCIceTransportState::Disconnected)) { + return RTCIceConnectionState::Disconnected; + } + + // new None of the previous states apply and all RTCIceTransports are + // in the "new" or "closed" state, or there are no transports. + if (!statesFound.count(RTCIceTransportState::Checking) && + !statesFound.count(RTCIceTransportState::Completed) && + !statesFound.count(RTCIceTransportState::Connected)) { + return RTCIceConnectionState::New; + } + + // checking None of the previous states apply and any RTCIceTransports are + // in the "new" or "checking" state. + if (statesFound.count(RTCIceTransportState::New) || + statesFound.count(RTCIceTransportState::Checking)) { + return RTCIceConnectionState::Checking; + } + + // completed None of the previous states apply and all RTCIceTransports are + // in the "completed" or "closed" state. + if (!statesFound.count(RTCIceTransportState::Connected)) { + return RTCIceConnectionState::Completed; + } + + // connected None of the previous states apply. + return RTCIceConnectionState::Connected; +} + +bool PeerConnectionImpl::UpdateIceConnectionState() { + auto newState = GetNewIceConnectionState(); + if (newState != mIceConnectionState) { + CSFLogInfo(LOGTAG, "%s: %d -> %d (%p)", __FUNCTION__, + static_cast(mIceConnectionState), + static_cast(newState), this); + mIceConnectionState = newState; + if (mIceConnectionState != RTCIceConnectionState::Closed) { + return true; + } + } + + return false; +} + void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId, const CandidateInfo& aCandidateInfo) { if (mStunAddrsRequest && !aCandidateInfo.mMDNSAddress.empty()) { @@ -3378,18 +3532,82 @@ void PeerConnectionImpl::OnCandidateFound(const std::string& aTransportId, } void PeerConnectionImpl::IceGatheringStateChange( - dom::RTCIceGatheringState state) { + const std::string& aTransportId, dom::RTCIceGathererState state) { + // If connection.[[IsClosed]] is true, abort these steps. PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); - CSFLogDebug(LOGTAG, "%s %d", __FUNCTION__, static_cast(state)); - if (mIceGatheringState == state) { + CSFLogWarn(LOGTAG, "IceGatheringStateChange: %s %d (%p)", + aTransportId.c_str(), static_cast(state), this); + + // Let transport be the RTCIceTransport for which candidate gathering + // began/finished. + nsCString key(aTransportId.data(), aTransportId.size()); + RefPtr dtlsTransport = + mTransportIdToRTCDtlsTransport.Get(key); + if (!dtlsTransport) { + return; + } + RefPtr transport = dtlsTransport->IceTransport(); + + if (transport->GatheringState() == state) { return; } - mIceGatheringState = state; + // Set transport.[[IceGathererState]] to gathering. + // or + // Set transport.[[IceGathererState]] to complete. + transport->SetGatheringState(state); + + // Set connection.[[IceGatheringState]] to the value of deriving a new state + // value as described by the RTCIceGatheringState enum. + // + // Let connectionIceGatheringStateChanged be true if + // connection.[[IceGatheringState]] changed in the previous step, otherwise + // false. + bool gatheringStateChanged = UpdateIceGatheringState(); + + // Do not read or modify state beyond this point. + + // Fire an event named gatheringstatechange at transport. + transport->FireGatheringStateChangeEvent(); + + // If connectionIceGatheringStateChanged is true, fire an event named + // icegatheringstatechange at connection. + if (gatheringStateChanged) { + // NOTE: If we're in the "complete" case, our JS code will fire a null + // icecandidate event after firing the icegatheringstatechange event. + // Fire an event named icecandidate using the RTCPeerConnectionIceEvent + // interface with the candidate attribute set to null at connection. + JSErrorResult rv; + mPCObserver->OnStateChange(PCObserverStateType::IceGatheringState, rv); + } +} + +bool PeerConnectionImpl::UpdateIceGatheringState() { + // If connection.[[IsClosed]] is true, abort these steps. + if (IsClosed()) { + return false; + } + + // Let newState be the value of deriving a new state value as + // described by the RTCIceGatheringState enum. + auto newState = GetNewIceGatheringState(); + + // If connection.[[IceGatheringState]] is equal to newState, abort + // these steps. + if (newState == mIceGatheringState) { + return false; + } + + CSFLogInfo(LOGTAG, "UpdateIceGatheringState: %d -> %d (%p)", + static_cast(mIceGatheringState), static_cast(newState), + this); + // Set connection.[[IceGatheringState]] to newState. + mIceGatheringState = newState; - // Would be nice if we had a means of converting one of these dom enums - // to a string that wasn't almost as much text as this switch statement... + // Would be nice if we had a means of converting one of these dom + // enums to a string that wasn't almost as much text as this switch + // statement... switch (mIceGatheringState) { case RTCIceGatheringState::New: STAMP_TIMECARD(mTimeCard, "Ice gathering state: new"); @@ -3404,8 +3622,42 @@ void PeerConnectionImpl::IceGatheringStateChange( MOZ_ASSERT_UNREACHABLE("Unexpected mIceGatheringState!"); } - JSErrorResult rv; - mPCObserver->OnStateChange(PCObserverStateType::IceGatheringState, rv); + return true; +} + +RTCIceGatheringState PeerConnectionImpl::GetNewIceGatheringState() const { + // new Any of the RTCIceTransports are in the "new" gathering state + // and none of the transports are in the "gathering" state, or there are no + // transports. + + // NOTE! This derives the RTCIce**Gathering**State from the individual + // RTCIce**Gatherer**State of the transports. These are different enums. + // But they have exactly the same values, in the same order. + // ¯\_(ツ)_/¯ + bool foundComplete = false; + std::set> transports(GetActiveTransports()); + for (const auto& transport : transports) { + RefPtr iceTransport = transport->IceTransport(); + switch (iceTransport->GatheringState()) { + case RTCIceGathererState::New: + break; + case RTCIceGathererState::Gathering: + // gathering Any of the RTCIceTransports are in the "gathering" + // state. + return RTCIceGatheringState::Gathering; + case RTCIceGathererState::Complete: + foundComplete = true; + break; + } + } + + if (!foundComplete) { + return RTCIceGatheringState::New; + } + + // This could change depending on the outcome in + // https://github.com/w3c/webrtc-pc/issues/2914 + return RTCIceGatheringState::Complete; } void PeerConnectionImpl::UpdateDefaultCandidate( @@ -3878,11 +4130,8 @@ void PeerConnectionImpl::StunAddrsHandler::OnStunAddrsAvailable( pcw.impl()->mStunAddrs = addrs.Clone(); pcw.impl()->mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE; pcw.impl()->FlushIceCtxOperationQueueIfReady(); - // If parent process returns 0 STUN addresses, change ICE connection - // state to failed. - if (!pcw.impl()->mStunAddrs.Length()) { - pcw.impl()->IceConnectionStateChange(dom::RTCIceConnectionState::Failed); - } + // If this fails, ICE cannot succeed, but we need to still go through the + // motions. } void PeerConnectionImpl::InitLocalAddrs() { @@ -3952,109 +4201,120 @@ void PeerConnectionImpl::EnsureTransports(const JsepSession& aSession) { GatherIfReady(); } -void PeerConnectionImpl::UpdateRTCDtlsTransports(bool aMarkAsStable) { +void PeerConnectionImpl::UpdateRTCDtlsTransports() { + // We use mDataConnection below, make sure it is initted if necessary + MaybeInitializeDataChannel(); + + // Make sure that the SCTP transport is unset if we do not see a DataChannel. + // We'll restore this if we do see a DataChannel. + RefPtr oldSctp = mSctpTransport.forget(); + mJsepSession->ForEachTransceiver( - [this, self = RefPtr(this)]( - const JsepTransceiver& jsepTransceiver) { + [this, self = RefPtr(this), + oldSctp](const JsepTransceiver& jsepTransceiver) { std::string transportId = jsepTransceiver.mTransport.mTransportId; - if (transportId.empty()) { - return; + RefPtr dtlsTransport; + if (!transportId.empty()) { + nsCString key(transportId.data(), transportId.size()); + dtlsTransport = mTransportIdToRTCDtlsTransport.GetOrInsertNew( + key, GetParentObject()); } - if (!mTransportIdToRTCDtlsTransport.count(transportId)) { - mTransportIdToRTCDtlsTransport.emplace( - transportId, new RTCDtlsTransport(GetParentObject())); + + if (jsepTransceiver.GetMediaType() == SdpMediaSection::kApplication) { + // Spec says we only update the RTCSctpTransport when negotiation + // completes. This is probably a spec bug. + // https://github.com/w3c/webrtc-pc/issues/2898 + if (!dtlsTransport || !mDataConnection) { + return; + } + + // Why on earth does the spec use a floating point for this? + double maxMessageSize = + static_cast(mDataConnection->GetMaxMessageSize()); + Nullable maxChannels; + + if (!oldSctp) { + mSctpTransport = new RTCSctpTransport( + GetParentObject(), *dtlsTransport, maxMessageSize, maxChannels); + } else { + // Restore the SCTP transport we had before this function was called + oldSctp->SetTransport(*dtlsTransport); + oldSctp->SetMaxMessageSize(maxMessageSize); + oldSctp->SetMaxChannels(maxChannels); + mSctpTransport = oldSctp; + } + } else { + RefPtr domTransceiver = + GetTransceiver(jsepTransceiver.GetUuid()); + if (domTransceiver) { + domTransceiver->SetDtlsTransport(dtlsTransport); + } } }); +} - for (auto& transceiver : mTransceivers) { - std::string transportId = transceiver->GetTransportId(); - if (transportId.empty()) { - continue; - } - if (mTransportIdToRTCDtlsTransport.count(transportId)) { - transceiver->SetDtlsTransport(mTransportIdToRTCDtlsTransport[transportId], - aMarkAsStable); - } +void PeerConnectionImpl::SaveStateForRollback() { + // This could change depending on the outcome in + // https://github.com/w3c/webrtc-pc/issues/2899 + if (mSctpTransport) { + // We have to save both of these things, because the DTLS transport could + // change without the SCTP transport changing. + mLastStableSctpTransport = mSctpTransport; + mLastStableSctpDtlsTransport = mSctpTransport->Transport(); + } else { + mLastStableSctpTransport = nullptr; + mLastStableSctpDtlsTransport = nullptr; } - // Spec says we only update the RTCSctpTransport when negotiation completes + for (auto& transceiver : mTransceivers) { + transceiver->SaveStateForRollback(); + } } -void PeerConnectionImpl::RollbackRTCDtlsTransports() { +void PeerConnectionImpl::RestoreStateForRollback() { for (auto& transceiver : mTransceivers) { transceiver->RollbackToStableDtlsTransport(); } + + mSctpTransport = mLastStableSctpTransport; + if (mSctpTransport) { + mSctpTransport->SetTransport(*mLastStableSctpDtlsTransport); + } } -void PeerConnectionImpl::RemoveRTCDtlsTransportsExcept( - const std::set& aTransportIds) { - for (auto iter = mTransportIdToRTCDtlsTransport.begin(); - iter != mTransportIdToRTCDtlsTransport.end();) { - if (!aTransportIds.count(iter->first)) { - iter = mTransportIdToRTCDtlsTransport.erase(iter); - } else { - ++iter; +std::set> +PeerConnectionImpl::GetActiveTransports() const { + std::set> result; + for (const auto& transceiver : mTransceivers) { + if (transceiver->GetDtlsTransport()) { + result.insert(transceiver->GetDtlsTransport()); } } + + if (mSctpTransport && mSctpTransport->Transport()) { + result.insert(mSctpTransport->Transport()); + } + return result; } nsresult PeerConnectionImpl::UpdateTransports(const JsepSession& aSession, const bool forceIceTcp) { std::set finalTransports; - Maybe sctpTransport; mJsepSession->ForEachTransceiver( [&, this, self = RefPtr(this)]( const JsepTransceiver& transceiver) { - if (transceiver.GetMediaType() == SdpMediaSection::kApplication && - transceiver.HasTransport()) { - sctpTransport = Some(transceiver.mTransport.mTransportId); - } - if (transceiver.HasOwnTransport()) { finalTransports.insert(transceiver.mTransport.mTransportId); UpdateTransport(transceiver, forceIceTcp); } }); - // clean up the unused RTCDtlsTransports - RemoveRTCDtlsTransportsExcept(finalTransports); - mTransportHandler->RemoveTransportsExcept(finalTransports); for (const auto& transceiverImpl : mTransceivers) { transceiverImpl->UpdateTransport(); } - if (sctpTransport.isSome()) { - auto it = mTransportIdToRTCDtlsTransport.find(*sctpTransport); - if (it == mTransportIdToRTCDtlsTransport.end()) { - // What? - MOZ_ASSERT(false); - return NS_ERROR_FAILURE; - } - if (!mDataConnection) { - // What? - MOZ_ASSERT(false); - return NS_ERROR_FAILURE; - } - RefPtr dtlsTransport = it->second; - // Why on earth does the spec use a floating point for this? - double maxMessageSize = - static_cast(mDataConnection->GetMaxMessageSize()); - Nullable maxChannels; - - if (!mSctpTransport) { - mSctpTransport = new RTCSctpTransport(GetParentObject(), *dtlsTransport, - maxMessageSize, maxChannels); - } else { - mSctpTransport->SetTransport(*dtlsTransport); - mSctpTransport->SetMaxMessageSize(maxMessageSize); - mSctpTransport->SetMaxChannels(maxChannels); - } - } else { - mSctpTransport = nullptr; - } - return NS_OK; } @@ -4141,6 +4401,9 @@ nsresult PeerConnectionImpl::UpdateMediaPipelines() { void PeerConnectionImpl::StartIceChecks(const JsepSession& aSession) { MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mJsepSession->GetState() == kJsepStateStable); + + auto transports = GetActiveTransports(); if (!mCanRegisterMDNSHostnamesDirectly) { for (auto& pair : mMDNSHostnamesToRegister) { @@ -4472,32 +4735,31 @@ std::string PeerConnectionImpl::GetTransportIdMatchingSendTrack( } void PeerConnectionImpl::SignalHandler::IceGatheringStateChange_s( - dom::RTCIceGatheringState aState) { + const std::string& aTransportId, dom::RTCIceGathererState aState) { ASSERT_ON_THREAD(mSTSThread); - GetMainThreadSerialEventTarget()->Dispatch( NS_NewRunnableFunction(__func__, - [handle = mHandle, aState] { + [handle = mHandle, aTransportId, aState] { PeerConnectionWrapper wrapper(handle); if (wrapper.impl()) { wrapper.impl()->IceGatheringStateChange( - aState); + aTransportId, aState); } }), NS_DISPATCH_NORMAL); } void PeerConnectionImpl::SignalHandler::IceConnectionStateChange_s( - dom::RTCIceConnectionState aState) { + const std::string& aTransportId, dom::RTCIceTransportState aState) { ASSERT_ON_THREAD(mSTSThread); GetMainThreadSerialEventTarget()->Dispatch( NS_NewRunnableFunction(__func__, - [handle = mHandle, aState] { + [handle = mHandle, aTransportId, aState] { PeerConnectionWrapper wrapper(handle); if (wrapper.impl()) { wrapper.impl()->IceConnectionStateChange( - aState); + aTransportId, aState); } }), NS_DISPATCH_NORMAL); -- cgit v1.2.3