/* 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 "WebrtcGlobalInformation.h" #include "WebrtcGlobalStatsHistory.h" #include "mozilla/Assertions.h" #include "mozilla/StaticPtr.h" #include "mozilla/dom/PWebrtcGlobal.h" #include "mozilla/dom/PWebrtcGlobalChild.h" #include "mozilla/media/webrtc/WebrtcGlobal.h" #include "WebrtcGlobalChild.h" #include "WebrtcGlobalParent.h" #include #include #include #include "mozilla/dom/WebrtcGlobalInformationBinding.h" #include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal #include "mozilla/dom/ContentChild.h" #include "nsISupports.h" #include "nsITimer.h" #include "nsLiteralString.h" #include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID #include "nsServiceManagerUtils.h" // do_GetService #include "nsXULAppAPI.h" #include "mozilla/ErrorResult.h" #include "nsProxyRelease.h" // nsMainThreadPtrHolder #include "mozilla/Telemetry.h" #include "mozilla/Unused.h" #include "mozilla/RefPtr.h" #include "mozilla/ClearOnShutdown.h" #include "common/browser_logging/WebRtcLog.h" #include "nsString.h" #include "transport/runnable_utils.h" #include "MediaTransportHandler.h" #include "PeerConnectionCtx.h" #include "PeerConnectionImpl.h" #ifdef XP_WIN # include #endif namespace mozilla::dom { using StatsRequestCallback = nsMainThreadPtrHandle; using LogRequestCallback = nsMainThreadPtrHandle; class WebrtcContentParents { public: static WebrtcGlobalParent* Alloc(); static void Dealloc(WebrtcGlobalParent* aParent); static bool Empty() { return sContentParents.empty(); } static const std::vector>& GetAll() { return sContentParents; } WebrtcContentParents() = delete; WebrtcContentParents(const WebrtcContentParents&) = delete; WebrtcContentParents& operator=(const WebrtcContentParents&) = delete; private: static std::vector> sContentParents; }; std::vector> WebrtcContentParents::sContentParents; WebrtcGlobalParent* WebrtcContentParents::Alloc() { RefPtr cp = new WebrtcGlobalParent; sContentParents.push_back(cp); return cp.get(); } void WebrtcContentParents::Dealloc(WebrtcGlobalParent* aParent) { if (aParent) { aParent->mShutdown = true; auto cp = std::find(sContentParents.begin(), sContentParents.end(), aParent); if (cp != sContentParents.end()) { sContentParents.erase(cp); } } } static PeerConnectionCtx* GetPeerConnectionCtx() { if (PeerConnectionCtx::isActive()) { MOZ_ASSERT(PeerConnectionCtx::GetInstance()); return PeerConnectionCtx::GetInstance(); } return nullptr; } static nsTArray& GetWebrtcGlobalStatsStash() { static StaticAutoPtr> sStash; if (!sStash) { sStash = new nsTArray(); ClearOnShutdown(&sStash); } return *sStash; } static RefPtr GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) { nsTArray> promises; std::set pcids; if (auto* ctx = GetPeerConnectionCtx()) { // Grab stats for PCs that still exist ctx->ForEachPeerConnection([&](PeerConnectionImpl* aPc) { if (!aPcIdFilter.IsEmpty() && !aPcIdFilter.EqualsASCII(aPc->GetIdAsAscii().c_str())) { return; } if (!aPc->IsClosed() || !aPc->LongTermStatsIsDisabled()) { nsString id; aPc->GetId(id); pcids.insert(id); promises.AppendElement(aPc->GetStats(nullptr, true)); } }); // Grab previously stashed stats, if they aren't dupes, and ensure they // are marked closed. (In a content process, this should already have // happened, but in the parent process, the stash will contain the last // observed stats from the content processes. From the perspective of the // parent process, these are assumed closed unless we see new stats from the // content process that say otherwise.) for (auto& report : GetWebrtcGlobalStatsStash()) { report.mClosed = true; if ((aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) && !pcids.count(report.mPcid)) { promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve( MakeUnique(report), __func__)); } } } auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType:: ResolveOrRejectValue&& aResult) { nsTArray reports; MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!"); for (auto& reportResult : aResult.ResolveValue()) { if (reportResult.IsResolve()) { reports.AppendElement(*reportResult.ResolveValue()); } } return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve( std::move(reports), __func__); }; return dom::RTCStatsReportPromise::AllSettled( GetMainThreadSerialEventTarget(), promises) ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(UnwrapUniquePtrs)); } static std::map>& GetWebrtcGlobalLogStash() { static StaticAutoPtr>> sStash; if (!sStash) { sStash = new std::map>(); ClearOnShutdown(&sStash); } return *sStash; } static void ClearLongTermStats() { if (!NS_IsMainThread()) { MOZ_ASSERT(NS_IsMainThread()); return; } GetWebrtcGlobalStatsStash().Clear(); if (XRE_IsParentProcess()) { WebrtcGlobalStatsHistory::Clear(); } if (auto* ctx = GetPeerConnectionCtx()) { ctx->ClearClosedStats(); } } void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) { if (!NS_IsMainThread()) { return; } // Chrome-only API MOZ_ASSERT(XRE_IsParentProcess()); if (!WebrtcContentParents::Empty()) { // Pass on the request to any content process based PeerConnections. for (const auto& cp : WebrtcContentParents::GetAll()) { Unused << cp->SendClearStats(); } } // Flush the history for the chrome process ClearLongTermStats(); } void WebrtcGlobalInformation::GetStatsHistoryPcIds( const GlobalObject& aGlobal, WebrtcGlobalStatisticsHistoryPcIdsCallback& aPcIdsCallback, ErrorResult& aRv) { if (!NS_IsMainThread()) { aRv.Throw(NS_ERROR_NOT_SAME_THREAD); return; } MOZ_ASSERT(XRE_IsParentProcess()); IgnoredErrorResult rv; aPcIdsCallback.Call(WebrtcGlobalStatsHistory::PcIds(), rv); aRv = NS_OK; } void WebrtcGlobalInformation::GetStatsHistorySince( const GlobalObject& aGlobal, WebrtcGlobalStatisticsHistoryCallback& aStatsCallback, const nsAString& pcIdFilter, const Optional& aAfter, const Optional& aSdpAfter, ErrorResult& aRv) { if (!NS_IsMainThread()) { aRv.Throw(NS_ERROR_NOT_SAME_THREAD); return; } MOZ_ASSERT(XRE_IsParentProcess()); WebrtcGlobalStatisticsReport history; auto statsAfter = aAfter.WasPassed() ? Some(aAfter.Value()) : Nothing(); auto sdpAfter = aSdpAfter.WasPassed() ? Some(aSdpAfter.Value()) : Nothing(); WebrtcGlobalStatsHistory::GetHistory(pcIdFilter).apply([&](auto& hist) { if (!history.mReports.AppendElements(hist->Since(statsAfter), fallible)) { mozalloc_handle_oom(0); } if (!history.mSdpHistories.AppendElement(hist->SdpSince(sdpAfter), fallible)) { mozalloc_handle_oom(0); } }); IgnoredErrorResult rv; aStatsCallback.Call(history, rv); aRv = NS_OK; } using StatsPromiseArray = nsTArray>; void WebrtcGlobalInformation::GatherHistory() { const nsString emptyFilter; if (!NS_IsMainThread()) { MOZ_ASSERT(NS_IsMainThread()); return; } MOZ_ASSERT(XRE_IsParentProcess()); using StatsPromise = PWebrtcGlobalParent::GetStatsPromise; auto resolveThenAppendStatsHistory = [](RefPtr&& promise) { auto AppendStatsHistory = [](StatsPromise::ResolveOrRejectValue&& result) { if (result.IsReject()) { return; } for (const auto& report : result.ResolveValue()) { WebrtcGlobalStatsHistory::Record( MakeUnique(report)); } }; promise->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AppendStatsHistory)); }; for (const auto& cp : WebrtcContentParents::GetAll()) { resolveThenAppendStatsHistory(cp->SendGetStats(emptyFilter)); } resolveThenAppendStatsHistory(GetStatsPromiseForThisProcess(emptyFilter)); } void WebrtcGlobalInformation::GetAllStats( const GlobalObject& aGlobal, WebrtcGlobalStatisticsCallback& aStatsCallback, const Optional& aPcIdFilter, ErrorResult& aRv) { if (!NS_IsMainThread()) { aRv.Throw(NS_ERROR_NOT_SAME_THREAD); return; } MOZ_ASSERT(XRE_IsParentProcess()); StatsPromiseArray statsPromises; nsString filter; if (aPcIdFilter.WasPassed()) { filter = aPcIdFilter.Value(); } for (const auto& cp : WebrtcContentParents::GetAll()) { statsPromises.AppendElement(cp->SendGetStats(filter)); } // Stats from this (the parent) process. How long do we keep supporting this? statsPromises.AppendElement(GetStatsPromiseForThisProcess(filter)); // CallbackObject does not support threadsafe refcounting, and must be // used and destroyed on main. StatsRequestCallback callbackHandle( new nsMainThreadPtrHolder( "WebrtcGlobalStatisticsCallback", &aStatsCallback)); auto FlattenThenStashThenCallback = [callbackHandle, filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType:: ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY { std::set pcids; WebrtcGlobalStatisticsReport flattened; MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!"); // Flatten stats from content processes and parent process. // The stats from the parent process (which will come last) might // contain some stale content-process stats, so skip those. for (auto& processResult : aResult.ResolveValue()) { // TODO: Report rejection on individual content processes someday? if (processResult.IsResolve()) { for (auto& pcStats : processResult.ResolveValue()) { if (!pcids.count(pcStats.mPcid)) { pcids.insert(pcStats.mPcid); if (!flattened.mReports.AppendElement(std::move(pcStats), fallible)) { mozalloc_handle_oom(0); } } } } } if (filter.IsEmpty()) { // Unfiltered is simple; the flattened result becomes the new stash. GetWebrtcGlobalStatsStash() = flattened.mReports; } else if (!flattened.mReports.IsEmpty()) { // Update our stash with the single result. MOZ_ASSERT(flattened.mReports.Length() == 1); StashStats(flattened.mReports[0]); } IgnoredErrorResult rv; callbackHandle->Call(flattened, rv); }; PWebrtcGlobalParent::GetStatsPromise::AllSettled( GetMainThreadSerialEventTarget(), statsPromises) ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(FlattenThenStashThenCallback)); aRv = NS_OK; } static RefPtr GetLogPromise() { PeerConnectionCtx* ctx = GetPeerConnectionCtx(); if (!ctx) { // This process has never created a PeerConnection, so no ICE logging. return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( Sequence(), __func__); } nsresult rv; nsCOMPtr stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) { return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( Sequence(), __func__); } RefPtr transportHandler = ctx->GetTransportHandler(); auto AddMarkers = [](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) { nsString pid; pid.AppendInt(getpid()); Sequence logs; if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) { bool ok = logs.AppendElement( u"+++++++ BEGIN (process id "_ns + pid + u") ++++++++"_ns, fallible); ok &= !!logs.AppendElements(std::move(aValue.ResolveValue()), fallible); ok &= !!logs.AppendElement( u"+++++++ END (process id "_ns + pid + u") ++++++++"_ns, fallible); if (!ok) { mozalloc_handle_oom(0); } } return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve( std::move(logs), __func__); }; return transportHandler->GetIceLog(nsCString()) ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers)); } static nsresult RunLogClear() { PeerConnectionCtx* ctx = GetPeerConnectionCtx(); if (!ctx) { // This process has never created a PeerConnection, so no ICE logging. return NS_OK; } nsresult rv; nsCOMPtr stsThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { return rv; } if (!stsThread) { return NS_ERROR_FAILURE; } RefPtr transportHandler = ctx->GetTransportHandler(); return RUN_ON_THREAD( stsThread, WrapRunnable(transportHandler, &MediaTransportHandler::ClearIceLog), NS_DISPATCH_NORMAL); } void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) { if (!NS_IsMainThread()) { return; } // Chrome-only API MOZ_ASSERT(XRE_IsParentProcess()); GetWebrtcGlobalLogStash().clear(); if (!WebrtcContentParents::Empty()) { // Clear content process signaling logs for (const auto& cp : WebrtcContentParents::GetAll()) { Unused << cp->SendClearLog(); } } // Clear chrome process signaling logs Unused << RunLogClear(); } static RefPtr UpdateLogStash() { nsTArray> logPromises; MOZ_ASSERT(XRE_IsParentProcess()); for (const auto& cp : WebrtcContentParents::GetAll()) { auto StashLog = [id = cp->Id() * 2 /* Make sure 1 isn't used */]( PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) { GetWebrtcGlobalLogStash()[id] = aValue.ResolveValue(); } return GenericPromise::CreateAndResolve(true, __func__); }; logPromises.AppendElement(cp->SendGetLog()->Then( GetMainThreadSerialEventTarget(), __func__, std::move(StashLog))); } // Get ICE logging for this (the parent) process. How long do we support this? logPromises.AppendElement(GetLogPromise()->Then( GetMainThreadSerialEventTarget(), __func__, [](PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsResolve()) { GetWebrtcGlobalLogStash()[1] = aValue.ResolveValue(); } return GenericPromise::CreateAndResolve(true, __func__); })); return GenericPromise::AllSettled(GetMainThreadSerialEventTarget(), logPromises) ->Then(GetMainThreadSerialEventTarget(), __func__, [](GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&& aValue) { // We don't care about the value, since we're just going to copy // what is in the stash. This ignores failures too, which is what // we want. return GenericPromise::CreateAndResolve(true, __func__); }); } void WebrtcGlobalInformation::GetLogging( const GlobalObject& aGlobal, const nsAString& aPattern, WebrtcGlobalLoggingCallback& aLoggingCallback, ErrorResult& aRv) { if (!NS_IsMainThread()) { aRv.Throw(NS_ERROR_NOT_SAME_THREAD); return; } MOZ_ASSERT(XRE_IsParentProcess()); nsAutoString pattern(aPattern); // CallbackObject does not support threadsafe refcounting, and must be // destroyed on main. LogRequestCallback callbackHandle( new nsMainThreadPtrHolder( "WebrtcGlobalLoggingCallback", &aLoggingCallback)); auto FilterThenCallback = [pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue) MOZ_CAN_RUN_SCRIPT_BOUNDARY { dom::Sequence flattened; for (const auto& [id, log] : GetWebrtcGlobalLogStash()) { (void)id; for (const auto& line : log) { if (pattern.IsEmpty() || (line.Find(pattern) != kNotFound)) { if (!flattened.AppendElement(line, fallible)) { mozalloc_handle_oom(0); } } } } IgnoredErrorResult rv; callbackHandle->Call(flattened, rv); }; UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__, std::move(FilterThenCallback)); aRv = NS_OK; } static int32_t sLastSetLevel = 0; static bool sLastAECDebug = false; static Maybe sAecDebugLogDir; void WebrtcGlobalInformation::SetDebugLevel(const GlobalObject& aGlobal, int32_t aLevel) { if (aLevel) { StartWebRtcLog(mozilla::LogLevel(aLevel)); } else { StopWebRtcLog(); } sLastSetLevel = aLevel; for (const auto& cp : WebrtcContentParents::GetAll()) { Unused << cp->SendSetDebugMode(aLevel); } } int32_t WebrtcGlobalInformation::DebugLevel(const GlobalObject& aGlobal) { return sLastSetLevel; } void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal, bool aEnable) { if (aEnable) { sAecDebugLogDir = Some(StartAecLog()); } else { StopAecLog(); } sLastAECDebug = aEnable; for (const auto& cp : WebrtcContentParents::GetAll()) { Unused << cp->SendSetAecLogging(aEnable); } } bool WebrtcGlobalInformation::AecDebug(const GlobalObject& aGlobal) { return sLastAECDebug; } void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal, nsAString& aDir) { aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns)); } /*static*/ void WebrtcGlobalInformation::StashStats( const dom::RTCStatsReportInternal& aReport) { // Remove previous report, if present // TODO: Make this a map instead of an array? for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) { auto& pcStats = GetWebrtcGlobalStatsStash()[i]; if (pcStats.mPcid == aReport.mPcid) { GetWebrtcGlobalStatsStash().RemoveElementAt(i); break; } ++i; } // Stash final stats GetWebrtcGlobalStatsStash().AppendElement(aReport); } void WebrtcGlobalInformation::AdjustTimerReferences( PcTrackingUpdate&& aUpdate) { static StaticRefPtr sHistoryTimer; static StaticAutoPtr> sPcids; MOZ_ASSERT(NS_IsMainThread()); auto HandleAdd = [&](nsString&& aPcid, bool aIsLongTermStatsDisabled) { if (!sPcids) { sPcids = new nsTHashSet(); ClearOnShutdown(&sPcids); } sPcids->EnsureInserted(aPcid); // Reserve a stats history WebrtcGlobalStatsHistory::InitHistory(nsString(aPcid), aIsLongTermStatsDisabled); if (!sHistoryTimer) { sHistoryTimer = NS_NewTimer(GetMainThreadSerialEventTarget()); if (sHistoryTimer) { sHistoryTimer->InitWithNamedFuncCallback( [](nsITimer* aTimer, void* aClosure) { if (WebrtcGlobalStatsHistory::Pref::Enabled()) { WebrtcGlobalInformation::GatherHistory(); } }, nullptr, WebrtcGlobalStatsHistory::Pref::PollIntervalMs(), nsITimer::TYPE_REPEATING_SLACK, "WebrtcGlobalInformation::GatherHistory"); } ClearOnShutdown(&sHistoryTimer); } }; auto HandleRemove = [&](const nsString& aRemoved) { WebrtcGlobalStatsHistory::CloseHistory(nsString(aRemoved)); if (!sPcids || !sPcids->Count()) { return; } if (!sPcids->Contains(aRemoved)) { return; } sPcids->Remove(aRemoved); if (!sPcids->Count() && sHistoryTimer) { sHistoryTimer->Cancel(); sHistoryTimer = nullptr; } }; switch (aUpdate.Type()) { case PcTrackingUpdate::Type::Add: { HandleAdd(std::move(aUpdate.mPcid), aUpdate.mLongTermStatsDisabled.valueOrFrom([&]() { MOZ_ASSERT(aUpdate.mLongTermStatsDisabled.isNothing()); return true; })); return; } case PcTrackingUpdate::Type::Remove: { HandleRemove(aUpdate.mPcid); return; } default: { MOZ_ASSERT(false, "Invalid PcCount operation"); } } } WebrtcGlobalParent* WebrtcGlobalParent::Alloc() { return WebrtcContentParents::Alloc(); } bool WebrtcGlobalParent::Dealloc(WebrtcGlobalParent* aActor) { WebrtcContentParents::Dealloc(aActor); return true; } void WebrtcGlobalParent::ActorDestroy(ActorDestroyReason aWhy) { mShutdown = true; for (const auto& pcId : mPcids) { using Update = WebrtcGlobalInformation::PcTrackingUpdate; auto update = Update::Remove(nsString(pcId)); WebrtcGlobalInformation::PeerConnectionTracking(update); } } mozilla::ipc::IPCResult WebrtcGlobalParent::Recv__delete__() { return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionCreated( const nsAString& aPcId, const bool& aIsLongTermStatsDisabled) { if (mShutdown) { return IPC_OK(); } mPcids.EnsureInserted(aPcId); using Update = WebrtcGlobalInformation::PcTrackingUpdate; auto update = Update::Add(nsString(aPcId), aIsLongTermStatsDisabled); WebrtcGlobalInformation::PeerConnectionTracking(update); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionDestroyed( const nsAString& aPcId) { mPcids.EnsureRemoved(aPcId); using Update = WebrtcGlobalInformation::PcTrackingUpdate; auto update = Update::Remove(nsString(aPcId)); WebrtcGlobalStatsHistory::CloseHistory(aPcId); WebrtcGlobalInformation::PeerConnectionTracking(update); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionFinalStats( const RTCStatsReportInternal& aFinalStats) { auto finalStats = MakeUnique(aFinalStats); WebrtcGlobalStatsHistory::Record(std::move(finalStats)); WebrtcGlobalStatsHistory::CloseHistory(aFinalStats.mPcid); return IPC_OK(); } MOZ_IMPLICIT WebrtcGlobalParent::WebrtcGlobalParent() : mShutdown(false) { MOZ_COUNT_CTOR(WebrtcGlobalParent); } MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() { MOZ_COUNT_DTOR(WebrtcGlobalParent); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats( const nsAString& aPcIdFilter, GetStatsResolver&& aResolve) { if (!mShutdown) { GetStatsPromiseForThisProcess(aPcIdFilter) ->Then( GetMainThreadSerialEventTarget(), __func__, [resolve = std::move(aResolve)]( nsTArray&& aReports) { resolve(std::move(aReports)); }, []() { MOZ_CRASH(); }); return IPC_OK(); } aResolve(nsTArray()); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() { if (mShutdown) { return IPC_OK(); } ClearLongTermStats(); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog( GetLogResolver&& aResolve) { if (mShutdown) { aResolve(Sequence()); return IPC_OK(); } GetLogPromise()->Then( GetMainThreadSerialEventTarget(), __func__, [aResolve = std::move(aResolve)]( PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsResolve()) { aResolve(aValue.ResolveValue()); } else { aResolve(Sequence()); } }); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() { if (mShutdown) { return IPC_OK(); } RunLogClear(); return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetAecLogging( const bool& aEnable) { if (!mShutdown) { if (aEnable) { StartAecLog(); } else { StopAecLog(); } } return IPC_OK(); } mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetDebugMode(const int& aLevel) { if (!mShutdown) { if (aLevel) { StartWebRtcLog(mozilla::LogLevel(aLevel)); } else { StopWebRtcLog(); } } return IPC_OK(); } WebrtcGlobalChild* WebrtcGlobalChild::GetOrSet( const Maybe& aChild) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(XRE_IsContentProcess()); static WebrtcGlobalChild* sChild; if (!sChild && !aChild) { sChild = static_cast( ContentChild::GetSingleton()->SendPWebrtcGlobalConstructor()); } aChild.apply([](auto* child) { sChild = child; }); return sChild; } WebrtcGlobalChild* WebrtcGlobalChild::Get() { return GetOrSet(Nothing()); } void WebrtcGlobalChild::ActorDestroy(ActorDestroyReason aWhy) { mShutdown = true; } MOZ_IMPLICIT WebrtcGlobalChild::WebrtcGlobalChild() : mShutdown(false) { MOZ_COUNT_CTOR(WebrtcGlobalChild); } MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() { MOZ_COUNT_DTOR(WebrtcGlobalChild); GetOrSet(Some(nullptr)); } } // namespace mozilla::dom