471 lines
17 KiB
C++
471 lines
17 KiB
C++
/* 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 "MediaTransportHandlerIPC.h"
|
|
#include "mozilla/dom/MediaTransportChild.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/net/SocketProcessBridgeChild.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "common/browser_logging/CSFLog.h"
|
|
|
|
namespace mozilla {
|
|
|
|
static const char* mthipcLogTag = "MediaTransportHandler";
|
|
#ifdef LOGTAG
|
|
# undef LOGTAG
|
|
#endif
|
|
#define LOGTAG mthipcLogTag
|
|
|
|
MediaTransportHandlerIPC::MediaTransportHandlerIPC(
|
|
nsISerialEventTarget* aCallbackThread)
|
|
: MediaTransportHandler(aCallbackThread) {}
|
|
|
|
MediaTransportHandlerIPC::~MediaTransportHandlerIPC() = default;
|
|
|
|
void MediaTransportHandlerIPC::Initialize() {
|
|
using EndpointPromise =
|
|
MozPromise<mozilla::ipc::Endpoint<mozilla::dom::PMediaTransportChild>,
|
|
nsCString, true>;
|
|
mInitPromise =
|
|
net::SocketProcessBridgeChild::GetSocketProcessBridge()
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[](const RefPtr<net::SocketProcessBridgeChild>& aBridge) {
|
|
mozilla::ipc::Endpoint<mozilla::dom::PMediaTransportParent>
|
|
parentEndpoint;
|
|
mozilla::ipc::Endpoint<mozilla::dom::PMediaTransportChild>
|
|
childEndpoint;
|
|
mozilla::dom::PMediaTransport::CreateEndpoints(&parentEndpoint,
|
|
&childEndpoint);
|
|
|
|
if (!aBridge || !aBridge->SendInitMediaTransport(
|
|
std::move(parentEndpoint))) {
|
|
NS_WARNING(
|
|
"MediaTransportHandlerIPC async init failed! Webrtc "
|
|
"networking "
|
|
"will not work!");
|
|
return EndpointPromise::CreateAndReject(
|
|
nsCString("SendInitMediaTransport failed!"), __func__);
|
|
}
|
|
|
|
return EndpointPromise::CreateAndResolve(
|
|
std::move(childEndpoint), __func__);
|
|
},
|
|
[](const nsCString& aError) {
|
|
return EndpointPromise::CreateAndReject(aError, __func__);
|
|
})
|
|
->Then(
|
|
mCallbackThread, __func__,
|
|
[this, self = RefPtr<MediaTransportHandlerIPC>(this)](
|
|
mozilla::ipc::Endpoint<mozilla::dom::PMediaTransportChild>&&
|
|
aEndpoint) {
|
|
RefPtr<MediaTransportChild> child =
|
|
new MediaTransportChild(this);
|
|
aEndpoint.Bind(child);
|
|
mChild = child;
|
|
|
|
CSFLogDebug(LOGTAG, "%s Init done", __func__);
|
|
return InitPromise::CreateAndResolve(true, __func__);
|
|
},
|
|
[=](const nsCString& aError) {
|
|
CSFLogError(
|
|
LOGTAG,
|
|
"MediaTransportHandlerIPC async init failed! Webrtc "
|
|
"networking will not work! Error was %s",
|
|
aError.get());
|
|
NS_WARNING(
|
|
"MediaTransportHandlerIPC async init failed! Webrtc "
|
|
"networking "
|
|
"will not work!");
|
|
return InitPromise::CreateAndReject(aError, __func__);
|
|
});
|
|
}
|
|
|
|
RefPtr<MediaTransportHandler::IceLogPromise>
|
|
MediaTransportHandlerIPC::GetIceLog(const nsCString& aPattern) {
|
|
return mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /* dummy */) {
|
|
if (!mChild) {
|
|
return IceLogPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
|
}
|
|
// Compiler has trouble deducing the return type here for some reason,
|
|
// so we use a temp variable as a hint.
|
|
// SendGetIceLog _almost_ returns an IceLogPromise; the reject value
|
|
// differs (ipc::ResponseRejectReason vs nsresult) so we need to
|
|
// convert.
|
|
RefPtr<IceLogPromise> promise = mChild->SendGetIceLog(aPattern)->Then(
|
|
mCallbackThread, __func__,
|
|
[](WebrtcGlobalLog&& aLogLines) {
|
|
return IceLogPromise::CreateAndResolve(std::move(aLogLines),
|
|
__func__);
|
|
},
|
|
[](ipc::ResponseRejectReason aReason) {
|
|
return IceLogPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
|
});
|
|
return promise;
|
|
},
|
|
[](const nsCString& aError) {
|
|
return IceLogPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
|
});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::ClearIceLog() {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendClearIceLog();
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::EnterPrivateMode() {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendEnterPrivateMode();
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::ExitPrivateMode() {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendExitPrivateMode();
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::CreateIceCtx(const std::string& aName) {
|
|
CSFLogDebug(LOGTAG, "MediaTransportHandlerIPC::CreateIceCtx start");
|
|
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
CSFLogDebug(LOGTAG, "%s starting", __func__);
|
|
if (NS_WARN_IF(!mChild->SendCreateIceCtx(aName))) {
|
|
CSFLogError(LOGTAG, "%s failed!", __func__);
|
|
}
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
nsresult MediaTransportHandlerIPC::SetIceConfig(
|
|
const nsTArray<dom::RTCIceServer>& aIceServers,
|
|
dom::RTCIceTransportPolicy aIcePolicy) {
|
|
// Run some validation on this side of the IPC boundary so we can return
|
|
// errors synchronously. We don't actually use the results. It might make
|
|
// sense to move this check to PeerConnection and have this API take the
|
|
// converted form, but we would need to write IPC serialization code for
|
|
// the NrIce*Server types.
|
|
std::vector<NrIceStunServer> stunServers;
|
|
std::vector<NrIceTurnServer> turnServers;
|
|
nsresult rv = ConvertIceServers(aIceServers, &stunServers, &turnServers);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, iceServers = aIceServers.Clone(),
|
|
self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
if (NS_WARN_IF(!mChild->SendSetIceConfig(std::move(iceServers),
|
|
aIcePolicy))) {
|
|
CSFLogError(LOGTAG, "%s failed!", __func__);
|
|
}
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::Destroy() {
|
|
if (mChild) {
|
|
mChild->Shutdown();
|
|
mCallbackThread->Dispatch(NS_NewRunnableFunction(
|
|
__func__, [child = std::move(mChild)]() { child->Close(); }));
|
|
}
|
|
delete this;
|
|
}
|
|
|
|
// We will probably be able to move the proxy lookup stuff into
|
|
// this class once we move mtransport to its own process.
|
|
void MediaTransportHandlerIPC::SetProxyConfig(
|
|
NrSocketProxyConfig&& aProxyConfig) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[aProxyConfig = std::move(aProxyConfig), this,
|
|
self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) mutable {
|
|
if (mChild) {
|
|
mChild->SendSetProxyConfig(aProxyConfig.GetConfig());
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::EnsureProvisionalTransport(
|
|
const std::string& aTransportId, const std::string& aLocalUfrag,
|
|
const std::string& aLocalPwd, int aComponentCount) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendEnsureProvisionalTransport(aTransportId, aLocalUfrag,
|
|
aLocalPwd, aComponentCount);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::SetTargetForDefaultLocalAddressLookup(
|
|
const std::string& aTargetIp, uint16_t aTargetPort) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendSetTargetForDefaultLocalAddressLookup(aTargetIp,
|
|
aTargetPort);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
// We set default-route-only as late as possible because it depends on what
|
|
// capture permissions have been granted on the window, which could easily
|
|
// change between Init (ie; when the PC is created) and StartIceGathering
|
|
// (ie; when we set the local description).
|
|
void MediaTransportHandlerIPC::StartIceGathering(
|
|
bool aDefaultRouteOnly, bool aObfuscateHostAddresses,
|
|
// TODO(bug 1522205): It probably makes sense to look this up internally
|
|
const nsTArray<NrIceStunAddr>& aStunAddrs) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, stunAddrs = aStunAddrs.Clone(),
|
|
self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendStartIceGathering(aDefaultRouteOnly,
|
|
aObfuscateHostAddresses, stunAddrs);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::ActivateTransport(
|
|
const std::string& aTransportId, const std::string& aLocalUfrag,
|
|
const std::string& aLocalPwd, size_t aComponentCount,
|
|
const std::string& aUfrag, const std::string& aPassword,
|
|
const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer,
|
|
SSLKEAType aAuthType, bool aDtlsClient, const DtlsDigestList& aDigests,
|
|
bool aPrivacyRequested) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, keyDer = aKeyDer.Clone(), certDer = aCertDer.Clone(),
|
|
self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendActivateTransport(aTransportId, aLocalUfrag, aLocalPwd,
|
|
aComponentCount, aUfrag, aPassword,
|
|
keyDer, certDer, aAuthType, aDtlsClient,
|
|
aDigests, aPrivacyRequested);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::RemoveTransportsExcept(
|
|
const std::set<std::string>& aTransportIds) {
|
|
std::vector<std::string> transportIds(aTransportIds.begin(),
|
|
aTransportIds.end());
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendRemoveTransportsExcept(transportIds);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::StartIceChecks(
|
|
bool aIsControlling, const std::vector<std::string>& aIceOptions) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendStartIceChecks(aIsControlling, aIceOptions);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::SendPacket(const std::string& aTransportId,
|
|
MediaPacket&& aPacket) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[this, self = RefPtr<MediaTransportHandlerIPC>(this), aTransportId,
|
|
aPacket = std::move(aPacket)](bool /*dummy*/) mutable {
|
|
if (mChild) {
|
|
mChild->SendSendPacket(aTransportId, aPacket);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::AddIceCandidate(
|
|
const std::string& aTransportId, const std::string& aCandidate,
|
|
const std::string& aUfrag, const std::string& aObfuscatedAddress) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendAddIceCandidate(aTransportId, aCandidate, aUfrag,
|
|
aObfuscatedAddress);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
void MediaTransportHandlerIPC::UpdateNetworkState(bool aOnline) {
|
|
mInitPromise->Then(
|
|
mCallbackThread, __func__,
|
|
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
|
|
if (mChild) {
|
|
mChild->SendUpdateNetworkState(aOnline);
|
|
}
|
|
},
|
|
[](const nsCString& aError) {});
|
|
}
|
|
|
|
RefPtr<dom::RTCStatsPromise> MediaTransportHandlerIPC::GetIceStats(
|
|
const std::string& aTransportId, DOMHighResTimeStamp aNow) {
|
|
using IPCPromise = dom::PMediaTransportChild::GetIceStatsPromise;
|
|
return mInitPromise
|
|
->Then(mCallbackThread, __func__,
|
|
[aTransportId, aNow, this, self = RefPtr(this)](
|
|
const InitPromise::ResolveOrRejectValue& aValue) {
|
|
if (aValue.IsReject()) {
|
|
return IPCPromise::CreateAndResolve(
|
|
MakeUnique<dom::RTCStatsCollection>(),
|
|
"MediaTransportHandlerIPC::GetIceStats_1");
|
|
}
|
|
if (!mChild) {
|
|
return IPCPromise::CreateAndResolve(
|
|
MakeUnique<dom::RTCStatsCollection>(),
|
|
"MediaTransportHandlerIPC::GetIceStats_1");
|
|
}
|
|
return mChild->SendGetIceStats(aTransportId, aNow);
|
|
})
|
|
->Then(mCallbackThread, __func__,
|
|
[](IPCPromise::ResolveOrRejectValue&& aValue) {
|
|
if (aValue.IsReject()) {
|
|
return dom::RTCStatsPromise::CreateAndResolve(
|
|
MakeUnique<dom::RTCStatsCollection>(),
|
|
"MediaTransportHandlerIPC::GetIceStats_2");
|
|
}
|
|
return dom::RTCStatsPromise::CreateAndResolve(
|
|
std::move(aValue.ResolveValue()),
|
|
"MediaTransportHandlerIPC::GetIceStats_2");
|
|
});
|
|
}
|
|
|
|
MediaTransportChild::MediaTransportChild(MediaTransportHandlerIPC* aUser)
|
|
: mMutex("MediaTransportChild"), mUser(aUser) {}
|
|
|
|
MediaTransportChild::~MediaTransportChild() = default;
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnCandidate(
|
|
const string& transportId, const CandidateInfo& candidateInfo) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnCandidate(transportId, candidateInfo);
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnAlpnNegotiated(
|
|
const string& alpn) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnAlpnNegotiated(alpn);
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnGatheringStateChange(
|
|
const string& transportId, const int& state) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnGatheringStateChange(transportId,
|
|
static_cast<dom::RTCIceGathererState>(state));
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnConnectionStateChange(
|
|
const string& transportId, const int& state) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnConnectionStateChange(
|
|
transportId, static_cast<dom::RTCIceTransportState>(state));
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnPacketReceived(
|
|
const string& transportId, const MediaPacket& packet) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnPacketReceived(transportId, packet);
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnEncryptedSending(
|
|
const string& transportId, const MediaPacket& packet) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnEncryptedSending(transportId, packet);
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnStateChange(
|
|
const string& transportId, const int& state) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnStateChange(transportId,
|
|
static_cast<TransportLayer::State>(state));
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult MediaTransportChild::RecvOnRtcpStateChange(
|
|
const string& transportId, const int& state) {
|
|
MutexAutoLock lock(mMutex);
|
|
if (mUser) {
|
|
mUser->OnRtcpStateChange(transportId,
|
|
static_cast<TransportLayer::State>(state));
|
|
}
|
|
return ipc::IPCResult::Ok();
|
|
}
|
|
|
|
void MediaTransportChild::Shutdown() {
|
|
MutexAutoLock lock(mMutex);
|
|
mUser = nullptr;
|
|
}
|
|
|
|
} // namespace mozilla
|