summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/jsapi/PeerConnectionMedia.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/media/webrtc/jsapi/PeerConnectionMedia.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/webrtc/jsapi/PeerConnectionMedia.cpp')
-rw-r--r--dom/media/webrtc/jsapi/PeerConnectionMedia.cpp891
1 files changed, 891 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/PeerConnectionMedia.cpp b/dom/media/webrtc/jsapi/PeerConnectionMedia.cpp
new file mode 100644
index 0000000000..eb3b6df173
--- /dev/null
+++ b/dom/media/webrtc/jsapi/PeerConnectionMedia.cpp
@@ -0,0 +1,891 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "common/browser_logging/CSFLog.h"
+
+#include "transport/nr_socket_proxy_config.h"
+#include "transportbridge/MediaPipelineFilter.h"
+#include "transportbridge/MediaPipeline.h"
+#include "PeerConnectionImpl.h"
+#include "PeerConnectionMedia.h"
+#include "RTCDtlsTransport.h"
+#include "transport/runnable_utils.h"
+#include "jsep/JsepSession.h"
+#include "jsep/JsepTransport.h"
+
+#include "nsContentUtils.h"
+#include "nsIIDNService.h"
+#include "nsILoadInfo.h"
+#include "nsIProxyInfo.h"
+#include "nsIPrincipal.h"
+#include "mozilla/LoadInfo.h"
+#include "nsIProxiedChannel.h"
+
+#include "nsIScriptGlobalObject.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/WebrtcProxyConfig.h"
+#include "MediaManager.h"
+#include "libwebrtcglue/WebrtcGmpVideoCodec.h"
+
+namespace mozilla {
+using namespace dom;
+
+static const char* pcmLogTag = "PeerConnectionMedia";
+#ifdef LOGTAG
+# undef LOGTAG
+#endif
+#define LOGTAG pcmLogTag
+
+void PeerConnectionMedia::StunAddrsHandler::OnMDNSQueryComplete(
+ const nsCString& hostname, const Maybe<nsCString>& address) {
+ ASSERT_ON_THREAD(pcm_->mMainThread);
+ auto itor = pcm_->mQueriedMDNSHostnames.find(hostname.BeginReading());
+ if (itor != pcm_->mQueriedMDNSHostnames.end()) {
+ if (address) {
+ for (auto& cand : itor->second) {
+ // Replace obfuscated address with actual address
+ std::string obfuscatedAddr = cand.mTokenizedCandidate[4];
+ cand.mTokenizedCandidate[4] = address->BeginReading();
+ std::ostringstream o;
+ for (size_t i = 0; i < cand.mTokenizedCandidate.size(); ++i) {
+ o << cand.mTokenizedCandidate[i];
+ if (i + 1 != cand.mTokenizedCandidate.size()) {
+ o << " ";
+ }
+ }
+ std::string mungedCandidate = o.str();
+ pcm_->mParent->StampTimecard("Done looking up mDNS name");
+ pcm_->mTransportHandler->AddIceCandidate(
+ cand.mTransportId, mungedCandidate, cand.mUfrag, obfuscatedAddr);
+ }
+ } else {
+ pcm_->mParent->StampTimecard("Failed looking up mDNS name");
+ }
+ pcm_->mQueriedMDNSHostnames.erase(itor);
+ }
+}
+
+void PeerConnectionMedia::StunAddrsHandler::OnStunAddrsAvailable(
+ const mozilla::net::NrIceStunAddrArray& addrs) {
+ CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
+ (int)addrs.Length());
+ if (pcm_) {
+ pcm_->mStunAddrs = addrs.Clone();
+ pcm_->mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE;
+ pcm_->FlushIceCtxOperationQueueIfReady();
+ // If parent process returns 0 STUN addresses, change ICE connection
+ // state to failed.
+ if (!pcm_->mStunAddrs.Length()) {
+ pcm_->IceConnectionStateChange_m(dom::RTCIceConnectionState::Failed);
+ }
+ }
+}
+
+PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl* parent)
+ : mTransportHandler(parent->GetTransportHandler()),
+ mParent(parent),
+ mParentHandle(parent->GetHandle()),
+ mParentName(parent->GetName()),
+ mMainThread(mParent->GetMainThread()),
+ mSTSThread(mParent->GetSTSThread()),
+ mForceProxy(false),
+ mStunAddrsRequest(nullptr),
+ mLocalAddrsRequestState(STUN_ADDR_REQUEST_NONE),
+ mTargetForDefaultLocalAddressLookupIsSet(false),
+ mDestroyed(false) {
+ if (XRE_IsContentProcess()) {
+ nsCOMPtr<nsISerialEventTarget> target =
+ mParent->GetWindow()
+ ? mParent->GetWindow()->EventTargetFor(TaskCategory::Other)
+ : nullptr;
+
+ mStunAddrsRequest =
+ new net::StunAddrsRequestChild(new StunAddrsHandler(this), target);
+ }
+}
+
+PeerConnectionMedia::~PeerConnectionMedia() {
+ MOZ_RELEASE_ASSERT(!mMainThread);
+}
+
+void PeerConnectionMedia::InitLocalAddrs() {
+ if (mLocalAddrsRequestState == STUN_ADDR_REQUEST_PENDING) {
+ return;
+ }
+ if (mStunAddrsRequest) {
+ mLocalAddrsRequestState = STUN_ADDR_REQUEST_PENDING;
+ mStunAddrsRequest->SendGetStunAddrs();
+ } else {
+ mLocalAddrsRequestState = STUN_ADDR_REQUEST_COMPLETE;
+ }
+}
+
+bool PeerConnectionMedia::ShouldForceProxy() const {
+ if (Preferences::GetBool("media.peerconnection.ice.proxy_only", false)) {
+ return true;
+ }
+
+ if (!Preferences::GetBool(
+ "media.peerconnection.ice.proxy_only_if_behind_proxy", false)) {
+ return false;
+ }
+
+ // Ok, we're supposed to be proxy_only, but only if a proxy is configured.
+ // Let's just see if the document was loaded via a proxy.
+
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = GetChannel();
+ if (!httpChannelInternal) {
+ return false;
+ }
+
+ nsCOMPtr<nsIProxiedChannel> proxiedChannel =
+ do_QueryInterface(httpChannelInternal);
+ if (!proxiedChannel) {
+ return false;
+ }
+
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ proxiedChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
+ if (!proxyInfo) {
+ return false;
+ }
+
+ nsCString proxyType;
+ proxyInfo->GetType(proxyType);
+
+ return !proxyType.IsEmpty() && !proxyType.EqualsLiteral("direct");
+}
+
+nsresult PeerConnectionMedia::Init() {
+ mForceProxy = ShouldForceProxy();
+
+ // setup the stun local addresses IPC async call
+ InitLocalAddrs();
+
+ ConnectSignals();
+ return NS_OK;
+}
+
+void PeerConnectionMedia::EnsureTransports(const JsepSession& aSession) {
+ for (const auto& [id, transceiver] : aSession.GetTransceivers()) {
+ (void)id; // Lame, but no better way to do this right now.
+ if (transceiver->HasOwnTransport()) {
+ mTransportHandler->EnsureProvisionalTransport(
+ transceiver->mTransport.mTransportId,
+ transceiver->mTransport.mLocalUfrag,
+ transceiver->mTransport.mLocalPwd,
+ transceiver->mTransport.mComponents);
+ }
+ }
+
+ GatherIfReady();
+}
+
+void PeerConnectionMedia::UpdateRTCDtlsTransports(bool aMarkAsStable) {
+ for (auto& transceiver : mTransceivers) {
+ std::string transportId = transceiver->GetTransportId();
+ if (transportId.empty()) {
+ continue;
+ }
+ if (!mTransportIdToRTCDtlsTransport.count(transportId)) {
+ mTransportIdToRTCDtlsTransport.emplace(
+ transportId, new RTCDtlsTransport(transceiver->GetParentObject()));
+ }
+
+ transceiver->SetDtlsTransport(mTransportIdToRTCDtlsTransport[transportId],
+ aMarkAsStable);
+ }
+}
+
+void PeerConnectionMedia::RollbackRTCDtlsTransports() {
+ for (auto& transceiver : mTransceivers) {
+ transceiver->RollbackToStableDtlsTransport();
+ }
+}
+
+void PeerConnectionMedia::RemoveRTCDtlsTransportsExcept(
+ const std::set<std::string>& aTransportIds) {
+ for (auto iter = mTransportIdToRTCDtlsTransport.begin();
+ iter != mTransportIdToRTCDtlsTransport.end();) {
+ if (!aTransportIds.count(iter->first)) {
+ iter = mTransportIdToRTCDtlsTransport.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+nsresult PeerConnectionMedia::UpdateTransports(const JsepSession& aSession,
+ const bool forceIceTcp) {
+ std::set<std::string> finalTransports;
+ for (const auto& [id, transceiver] : aSession.GetTransceivers()) {
+ (void)id; // Lame, but no better way to do this right now.
+ 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();
+ }
+
+ return NS_OK;
+}
+
+void PeerConnectionMedia::UpdateTransport(const JsepTransceiver& aTransceiver,
+ bool aForceIceTcp) {
+ std::string ufrag;
+ std::string pwd;
+ std::vector<std::string> candidates;
+ size_t components = 0;
+
+ const JsepTransport& transport = aTransceiver.mTransport;
+ unsigned level = aTransceiver.GetLevel();
+
+ CSFLogDebug(LOGTAG, "ACTIVATING TRANSPORT! - PC %s: level=%u components=%u",
+ mParentHandle.c_str(), (unsigned)level,
+ (unsigned)transport.mComponents);
+
+ ufrag = transport.mIce->GetUfrag();
+ pwd = transport.mIce->GetPassword();
+ candidates = transport.mIce->GetCandidates();
+ components = transport.mComponents;
+ if (aForceIceTcp) {
+ candidates.erase(
+ std::remove_if(candidates.begin(), candidates.end(),
+ [](const std::string& s) {
+ return s.find(" UDP ") != std::string::npos ||
+ s.find(" udp ") != std::string::npos;
+ }),
+ candidates.end());
+ }
+
+ nsTArray<uint8_t> keyDer;
+ nsTArray<uint8_t> certDer;
+ nsresult rv = mParent->Identity()->Serialize(&keyDer, &certDer);
+ if (NS_FAILED(rv)) {
+ CSFLogError(LOGTAG, "%s: Failed to serialize DTLS identity: %d",
+ __FUNCTION__, (int)rv);
+ return;
+ }
+
+ DtlsDigestList digests;
+ for (const auto& fingerprint :
+ transport.mDtls->GetFingerprints().mFingerprints) {
+ std::ostringstream ss;
+ ss << fingerprint.hashFunc;
+ digests.emplace_back(ss.str(), fingerprint.fingerprint);
+ }
+
+ mTransportHandler->ActivateTransport(
+ transport.mTransportId, transport.mLocalUfrag, transport.mLocalPwd,
+ components, ufrag, pwd, keyDer, certDer, mParent->Identity()->auth_type(),
+ transport.mDtls->GetRole() == JsepDtlsTransport::kJsepDtlsClient, digests,
+ mParent->PrivacyRequested());
+
+ for (auto& candidate : candidates) {
+ AddIceCandidate("candidate:" + candidate, transport.mTransportId, ufrag);
+ }
+}
+
+nsresult PeerConnectionMedia::UpdateMediaPipelines() {
+ // The GMP code is all the way on the other side of webrtc.org, and it is not
+ // feasible to plumb error information all the way back. So, we set up a
+ // handle to the PC (for the duration of this call) in a global variable.
+ // This allows the GMP code to report errors to the PC.
+ WebrtcGmpPCHandleSetter setter(mParentHandle);
+
+ for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ transceiver->ResetSync();
+ }
+
+ for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ if (!transceiver->IsVideo()) {
+ nsresult rv = transceiver->SyncWithMatchingVideoConduits(mTransceivers);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ nsresult rv = transceiver->UpdateConduit();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+void PeerConnectionMedia::StartIceChecks(const JsepSession& aSession) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (!mCanRegisterMDNSHostnamesDirectly) {
+ for (auto& pair : mMDNSHostnamesToRegister) {
+ mRegisteredMDNSHostnames.insert(pair.first);
+ mStunAddrsRequest->SendRegisterMDNSHostname(
+ nsCString(pair.first.c_str()), nsCString(pair.second.c_str()));
+ }
+ mMDNSHostnamesToRegister.clear();
+ mCanRegisterMDNSHostnamesDirectly = true;
+ }
+
+ std::vector<std::string> attributes;
+ if (aSession.RemoteIsIceLite()) {
+ attributes.push_back("ice-lite");
+ }
+
+ if (!aSession.GetIceOptions().empty()) {
+ attributes.push_back("ice-options:");
+ for (const auto& option : aSession.GetIceOptions()) {
+ attributes.back() += option + ' ';
+ }
+ }
+
+ nsCOMPtr<nsIRunnable> runnable(
+ WrapRunnable(mTransportHandler, &MediaTransportHandler::StartIceChecks,
+ aSession.IsIceControlling(), attributes));
+
+ PerformOrEnqueueIceCtxOperation(runnable);
+}
+
+bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const {
+ ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+ uint64_t winId = mParent->GetWindow()->WindowID();
+
+ bool default_address_only = Preferences::GetBool(
+ "media.peerconnection.ice.default_address_only", false);
+ default_address_only |=
+ !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
+ return default_address_only;
+}
+
+bool PeerConnectionMedia::GetPrefObfuscateHostAddresses() const {
+ ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+ uint64_t winId = mParent->GetWindow()->WindowID();
+
+ bool obfuscate_host_addresses = Preferences::GetBool(
+ "media.peerconnection.ice.obfuscate_host_addresses", false);
+ obfuscate_host_addresses &=
+ !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
+ obfuscate_host_addresses &= !PeerConnectionImpl::HostnameInPref(
+ "media.peerconnection.ice.obfuscate_host_addresses.blocklist",
+ mParent->GetWindow()->GetDocumentURI());
+ obfuscate_host_addresses &= XRE_IsContentProcess();
+
+ return obfuscate_host_addresses;
+}
+
+void PeerConnectionMedia::ConnectSignals() {
+ mTransportHandler->SignalGatheringStateChange.connect(
+ this, &PeerConnectionMedia::IceGatheringStateChange_s);
+ mTransportHandler->SignalConnectionStateChange.connect(
+ this, &PeerConnectionMedia::IceConnectionStateChange_s);
+ mTransportHandler->SignalCandidate.connect(
+ this, &PeerConnectionMedia::OnCandidateFound_s);
+ mTransportHandler->SignalAlpnNegotiated.connect(
+ this, &PeerConnectionMedia::AlpnNegotiated_s);
+}
+
+void PeerConnectionMedia::AddIceCandidate(const std::string& aCandidate,
+ const std::string& aTransportId,
+ const std::string& aUfrag) {
+ ASSERT_ON_THREAD(mMainThread);
+ MOZ_ASSERT(!aTransportId.empty());
+
+ bool obfuscate_host_addresses = Preferences::GetBool(
+ "media.peerconnection.ice.obfuscate_host_addresses", false);
+
+ if (obfuscate_host_addresses) {
+ std::vector<std::string> tokens;
+ TokenizeCandidate(aCandidate, tokens);
+
+ if (tokens.size() > 4) {
+ std::string addr = tokens[4];
+
+ // Check for address ending with .local
+ size_t nPeriods = std::count(addr.begin(), addr.end(), '.');
+ size_t dotLocalLength = 6; // length of ".local"
+
+ if (nPeriods == 1 &&
+ addr.rfind(".local") + dotLocalLength == addr.length()) {
+ if (mStunAddrsRequest) {
+ PendingIceCandidate cand;
+ cand.mTokenizedCandidate = std::move(tokens);
+ cand.mTransportId = aTransportId;
+ cand.mUfrag = aUfrag;
+ mQueriedMDNSHostnames[addr].push_back(cand);
+
+ mMainThread->Dispatch(NS_NewRunnableFunction(
+ "PeerConnectionMedia::SendQueryMDNSHostname",
+ [self = RefPtr<PeerConnectionMedia>(this), addr]() mutable {
+ if (self->mStunAddrsRequest) {
+ self->mParent->StampTimecard("Look up mDNS name");
+ self->mStunAddrsRequest->SendQueryMDNSHostname(
+ nsCString(nsAutoCString(addr.c_str())));
+ }
+ NS_ReleaseOnMainThread(
+ "PeerConnectionMedia::SendQueryMDNSHostname",
+ self.forget());
+ }));
+ }
+ // TODO: Bug 1535690, we don't want to tell the ICE context that remote
+ // trickle is done if we are waiting to resolve a mDNS candidate.
+ return;
+ }
+ }
+ }
+
+ mTransportHandler->AddIceCandidate(aTransportId, aCandidate, aUfrag, "");
+}
+
+void PeerConnectionMedia::UpdateNetworkState(bool online) {
+ mTransportHandler->UpdateNetworkState(online);
+}
+
+void PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (IsIceCtxReady()) {
+ for (auto& queuedIceCtxOperation : mQueuedIceCtxOperations) {
+ queuedIceCtxOperation->Run();
+ }
+ mQueuedIceCtxOperations.clear();
+ }
+}
+
+void PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(
+ nsIRunnable* runnable) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (IsIceCtxReady()) {
+ runnable->Run();
+ } else {
+ mQueuedIceCtxOperations.push_back(runnable);
+ }
+}
+
+void PeerConnectionMedia::GatherIfReady() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ // Init local addrs here so that if we re-gather after an ICE restart
+ // resulting from changing WiFi networks, we get new local addrs.
+ // Otherwise, we would reuse the addrs from the original WiFi network
+ // and the ICE restart will fail.
+ if (!mStunAddrs.Length()) {
+ InitLocalAddrs();
+ }
+
+ // If we had previously queued gathering or ICE start, unqueue them
+ mQueuedIceCtxOperations.clear();
+ nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::EnsureIceGathering, GetPrefDefaultAddressOnly(),
+ GetPrefObfuscateHostAddresses()));
+
+ PerformOrEnqueueIceCtxOperation(runnable);
+}
+
+already_AddRefed<nsIHttpChannelInternal> PeerConnectionMedia::GetChannel()
+ const {
+ Document* doc = mParent->GetWindow()->GetExtantDoc();
+ if (NS_WARN_IF(!doc)) {
+ NS_WARNING("Unable to get document from window");
+ return nullptr;
+ }
+
+ if (!doc->GetDocumentURI()->SchemeIs("file")) {
+ nsIChannel* channel = doc->GetChannel();
+ if (!channel) {
+ NS_WARNING("Unable to get channel from document");
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(channel);
+ if (NS_WARN_IF(!httpChannelInternal)) {
+ CSFLogInfo(LOGTAG, "%s: Document does not have an HTTP channel",
+ __FUNCTION__);
+ return nullptr;
+ }
+ return httpChannelInternal.forget();
+ }
+ return nullptr;
+}
+
+nsresult PeerConnectionMedia::SetTargetForDefaultLocalAddressLookup() {
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = GetChannel();
+ if (!httpChannelInternal) {
+ return NS_OK;
+ }
+
+ nsCString remoteIp;
+ nsresult rv = httpChannelInternal->GetRemoteAddress(remoteIp);
+ if (NS_FAILED(rv) || remoteIp.IsEmpty()) {
+ CSFLogError(LOGTAG, "%s: Failed to get remote IP address: %d", __FUNCTION__,
+ (int)rv);
+ return rv;
+ }
+
+ int32_t remotePort;
+ rv = httpChannelInternal->GetRemotePort(&remotePort);
+ if (NS_FAILED(rv)) {
+ CSFLogError(LOGTAG, "%s: Failed to get remote port number: %d",
+ __FUNCTION__, (int)rv);
+ return rv;
+ }
+
+ mTransportHandler->SetTargetForDefaultLocalAddressLookup(remoteIp.get(),
+ remotePort);
+
+ return NS_OK;
+}
+
+void PeerConnectionMedia::EnsureIceGathering(bool aDefaultRouteOnly,
+ bool aObfuscateHostAddresses) {
+ auto proxyConfig = GetProxyConfig();
+ if (proxyConfig) {
+ // Note that this could check if PrivacyRequested() is set on the PC and
+ // remove "webrtc" from the ALPN list. But that would only work if the PC
+ // was constructed with a peerIdentity constraint, not when isolated
+ // streams are added. If we ever need to signal to the proxy that the
+ // media is isolated, then we would need to restructure this code.
+ mTransportHandler->SetProxyConfig(std::move(*proxyConfig));
+ }
+
+ if (!mTargetForDefaultLocalAddressLookupIsSet) {
+ nsresult rv = SetTargetForDefaultLocalAddressLookup();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Unable to set target for default local address lookup");
+ }
+ mTargetForDefaultLocalAddressLookupIsSet = true;
+ }
+
+ // Make sure we don't call StartIceGathering if we're in e10s mode
+ // and we received no STUN addresses from the parent process. In the
+ // absence of previously provided STUN addresses, StartIceGathering will
+ // attempt to gather them (as in non-e10s mode), and this will cause a
+ // sandboxing exception in e10s mode.
+ if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
+ CSFLogInfo(LOGTAG, "%s: No STUN addresses returned from parent process",
+ __FUNCTION__);
+ return;
+ }
+
+ mTransportHandler->StartIceGathering(aDefaultRouteOnly,
+ aObfuscateHostAddresses, mStunAddrs);
+}
+
+void PeerConnectionMedia::SelfDestruct() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
+
+ mDestroyed = true;
+
+ if (mStunAddrsRequest) {
+ for (auto& hostname : mRegisteredMDNSHostnames) {
+ mStunAddrsRequest->SendUnregisterMDNSHostname(
+ nsCString(hostname.c_str()));
+ }
+ mRegisteredMDNSHostnames.clear();
+ mStunAddrsRequest->Cancel();
+ mStunAddrsRequest = nullptr;
+ }
+
+ for (auto& transceiver : mTransceivers) {
+ // transceivers are garbage-collected, so we need to poke them to perform
+ // cleanup right now so the appropriate events fire.
+ transceiver->Shutdown_m();
+ }
+
+ mTransceivers.clear();
+
+ mQueuedIceCtxOperations.clear();
+
+ // Shutdown the transport (async)
+ RUN_ON_THREAD(
+ mSTSThread,
+ WrapRunnable(this, &PeerConnectionMedia::ShutdownMediaTransport_s),
+ NS_DISPATCH_NORMAL);
+ mParent = nullptr;
+
+ CSFLogDebug(LOGTAG, "%s: Media shut down", __FUNCTION__);
+}
+
+void PeerConnectionMedia::SelfDestruct_m() {
+ CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
+
+ ASSERT_ON_THREAD(mMainThread);
+
+ mTransportHandler->RemoveTransportsExcept(std::set<std::string>());
+ mTransportHandler = nullptr;
+
+ mMainThread = nullptr;
+
+ // Final self-destruct.
+ this->Release();
+}
+
+void PeerConnectionMedia::ShutdownMediaTransport_s() {
+ ASSERT_ON_THREAD(mSTSThread);
+
+ CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
+
+ disconnect_all();
+
+ // we're holding a ref to 'this' that's released by SelfDestruct_m
+ mMainThread->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
+ NS_DISPATCH_NORMAL);
+}
+
+nsresult PeerConnectionMedia::AddTransceiver(
+ JsepTransceiver* aJsepTransceiver, dom::MediaStreamTrack* aSendTrack,
+ RefPtr<TransceiverImpl>* aTransceiverImpl) {
+ if (!mCall) {
+ mCall = WebRtcCallWrapper::Create(mParent->GetTimestampMaker());
+ }
+
+ RefPtr<TransceiverImpl> transceiver = new TransceiverImpl(
+ mParent->GetWindow(), mParent->PrivacyNeeded(), mParent->GetHandle(),
+ mTransportHandler, aJsepTransceiver, mMainThread.get(), mSTSThread.get(),
+ aSendTrack, mCall.get());
+
+ if (!transceiver->IsValid()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aSendTrack) {
+ // implement checking for peerIdentity (where failure == black/silence)
+ Document* doc = mParent->GetWindow()->GetExtantDoc();
+ if (doc) {
+ transceiver->UpdateSinkIdentity(nullptr, doc->NodePrincipal(),
+ mParent->GetPeerIdentity());
+ } else {
+ MOZ_CRASH();
+ return NS_ERROR_FAILURE; // Don't remove this till we know it's safe.
+ }
+ }
+
+ mTransceivers.push_back(transceiver);
+ *aTransceiverImpl = transceiver;
+
+ return NS_OK;
+}
+
+void PeerConnectionMedia::GetTransmitPipelinesMatching(
+ const MediaStreamTrack* aTrack,
+ nsTArray<RefPtr<MediaPipelineTransmit>>* aPipelines) {
+ for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ if (transceiver->HasSendTrack(aTrack)) {
+ aPipelines->AppendElement(transceiver->GetSendPipeline());
+ }
+ }
+}
+
+std::string PeerConnectionMedia::GetTransportIdMatchingSendTrack(
+ const dom::MediaStreamTrack& aTrack) const {
+ for (const RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ if (transceiver->HasSendTrack(&aTrack)) {
+ return transceiver->GetTransportId();
+ }
+ }
+ return std::string();
+}
+
+void PeerConnectionMedia::IceGatheringStateChange_s(
+ dom::RTCIceGatheringState aState) {
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::IceGatheringStateChange_m,
+ aState),
+ NS_DISPATCH_NORMAL);
+}
+
+void PeerConnectionMedia::IceConnectionStateChange_s(
+ dom::RTCIceConnectionState aState) {
+ ASSERT_ON_THREAD(mSTSThread);
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::IceConnectionStateChange_m,
+ aState),
+ NS_DISPATCH_NORMAL);
+}
+
+void PeerConnectionMedia::OnCandidateFound_s(
+ const std::string& aTransportId, const CandidateInfo& aCandidateInfo) {
+ ASSERT_ON_THREAD(mSTSThread);
+ MOZ_RELEASE_ASSERT(mTransportHandler);
+
+ CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aTransportId.c_str());
+
+ MOZ_ASSERT(!aCandidateInfo.mUfrag.empty());
+
+ // ShutdownMediaTransport_s has not run yet because it unhooks this function
+ // from its signal, which means that SelfDestruct_m has not been dispatched
+ // yet either, so this PCMedia will still be around when this dispatch reaches
+ // main.
+ GetMainThread()->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::OnCandidateFound_m, aTransportId,
+ aCandidateInfo),
+ NS_DISPATCH_NORMAL);
+}
+
+void PeerConnectionMedia::IceGatheringStateChange_m(
+ dom::RTCIceGatheringState aState) {
+ ASSERT_ON_THREAD(mMainThread);
+ if (mParent) {
+ mParent->IceGatheringStateChange(aState);
+ }
+}
+
+void PeerConnectionMedia::IceConnectionStateChange_m(
+ dom::RTCIceConnectionState aState) {
+ ASSERT_ON_THREAD(mMainThread);
+ if (mParent) {
+ mParent->IceConnectionStateChange(aState);
+ }
+}
+
+void PeerConnectionMedia::OnCandidateFound_m(
+ const std::string& aTransportId, const CandidateInfo& aCandidateInfo) {
+ ASSERT_ON_THREAD(mMainThread);
+ if (mStunAddrsRequest && !aCandidateInfo.mMDNSAddress.empty()) {
+ MOZ_ASSERT(!aCandidateInfo.mActualAddress.empty());
+
+ if (mCanRegisterMDNSHostnamesDirectly) {
+ auto itor = mRegisteredMDNSHostnames.find(aCandidateInfo.mMDNSAddress);
+
+ // We'll see the address twice if we're generating both UDP and TCP
+ // candidates.
+ if (itor == mRegisteredMDNSHostnames.end()) {
+ mRegisteredMDNSHostnames.insert(aCandidateInfo.mMDNSAddress);
+ mStunAddrsRequest->SendRegisterMDNSHostname(
+ nsCString(aCandidateInfo.mMDNSAddress.c_str()),
+ nsCString(aCandidateInfo.mActualAddress.c_str()));
+ }
+ } else {
+ mMDNSHostnamesToRegister.emplace(aCandidateInfo.mMDNSAddress,
+ aCandidateInfo.mActualAddress);
+ }
+ }
+
+ if (mParent) {
+ mParent->OnCandidateFound(aTransportId, aCandidateInfo);
+ }
+}
+
+void PeerConnectionMedia::AlpnNegotiated_s(const std::string& aAlpn,
+ bool aPrivacyRequested) {
+ MOZ_DIAGNOSTIC_ASSERT((aAlpn == "c-webrtc") == aPrivacyRequested);
+ GetMainThread()->Dispatch(
+ WrapRunnable(this, &PeerConnectionMedia::AlpnNegotiated_m,
+ aPrivacyRequested),
+ NS_DISPATCH_NORMAL);
+}
+
+void PeerConnectionMedia::AlpnNegotiated_m(bool aPrivacyRequested) {
+ if (mParent) {
+ mParent->OnAlpnNegotiated(aPrivacyRequested);
+ }
+}
+
+/**
+ * Tells you if any local track is isolated to a specific peer identity.
+ * Obviously, we want all the tracks to be isolated equally so that they can
+ * all be sent or not. We check once when we are setting a local description
+ * and that determines if we flip the "privacy requested" bit on. Once the bit
+ * is on, all media originating from this peer connection is isolated.
+ *
+ * @returns true if any track has a peerIdentity set on it
+ */
+bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const {
+ ASSERT_ON_THREAD(mMainThread);
+
+ for (const RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ if (transceiver->GetSendTrack() &&
+ transceiver->GetSendTrack()->GetPeerIdentity()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void PeerConnectionMedia::UpdateSinkIdentity_m(
+ const MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal,
+ const PeerIdentity* aSinkIdentity) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ transceiver->UpdateSinkIdentity(aTrack, aPrincipal, aSinkIdentity);
+ }
+}
+
+bool PeerConnectionMedia::AnyCodecHasPluginID(uint64_t aPluginID) {
+ for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
+ if (transceiver->ConduitHasPluginID(aPluginID)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsPIDOMWindowInner* PeerConnectionMedia::GetWindow() const {
+ return mParent->GetWindow();
+}
+
+std::unique_ptr<NrSocketProxyConfig> PeerConnectionMedia::GetProxyConfig()
+ const {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mForceProxy &&
+ Preferences::GetBool("media.peerconnection.disable_http_proxy", false)) {
+ return nullptr;
+ }
+
+ nsCString alpn = "webrtc,c-webrtc"_ns;
+ auto browserChild = BrowserChild::GetFrom(GetWindow());
+ if (!browserChild) {
+ // Android doesn't have browser child apparently...
+ return nullptr;
+ }
+
+ Document* doc = mParent->GetWindow()->GetExtantDoc();
+ if (NS_WARN_IF(!doc)) {
+ NS_WARNING("Unable to get document from window");
+ return nullptr;
+ }
+
+ TabId id = browserChild->GetTabId();
+ nsCOMPtr<nsILoadInfo> loadInfo =
+ new net::LoadInfo(doc->NodePrincipal(), doc->NodePrincipal(), doc, 0,
+ nsIContentPolicy::TYPE_INVALID);
+
+ Maybe<net::LoadInfoArgs> loadInfoArgs;
+ MOZ_ALWAYS_SUCCEEDS(
+ mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
+ return std::unique_ptr<NrSocketProxyConfig>(new NrSocketProxyConfig(
+ net::WebrtcProxyConfig(id, alpn, *loadInfoArgs, mForceProxy)));
+}
+
+} // namespace mozilla