807 lines
25 KiB
C++
807 lines
25 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 "WebrtcGlobalInformation.h"
|
|
#include "WebrtcGlobalStatsHistory.h"
|
|
#include "libwebrtcglue/VideoConduit.h"
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/dom/PWebrtcGlobal.h"
|
|
#include "mozilla/dom/PWebrtcGlobalChild.h"
|
|
#include "WebrtcGlobalChild.h"
|
|
#include "WebrtcGlobalParent.h"
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "mozilla/dom/WebrtcGlobalInformationBinding.h"
|
|
#include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal
|
|
#include "mozilla/dom/ContentChild.h"
|
|
|
|
#include "ErrorList.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/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 <process.h>
|
|
#endif
|
|
|
|
namespace mozilla::dom {
|
|
|
|
using StatsRequestCallback =
|
|
nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback>;
|
|
|
|
using LogRequestCallback = nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback>;
|
|
|
|
class WebrtcContentParents {
|
|
public:
|
|
static WebrtcGlobalParent* Alloc();
|
|
static void Dealloc(WebrtcGlobalParent* aParent);
|
|
static bool Empty() { return sContentParents.empty(); }
|
|
static const std::vector<RefPtr<WebrtcGlobalParent>>& GetAll() {
|
|
return sContentParents;
|
|
}
|
|
|
|
WebrtcContentParents() = delete;
|
|
WebrtcContentParents(const WebrtcContentParents&) = delete;
|
|
WebrtcContentParents& operator=(const WebrtcContentParents&) = delete;
|
|
|
|
private:
|
|
static std::vector<RefPtr<WebrtcGlobalParent>> sContentParents;
|
|
};
|
|
|
|
MOZ_RUNINIT std::vector<RefPtr<WebrtcGlobalParent>>
|
|
WebrtcContentParents::sContentParents;
|
|
|
|
WebrtcGlobalParent* WebrtcContentParents::Alloc() {
|
|
RefPtr<WebrtcGlobalParent> 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<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() {
|
|
static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash;
|
|
if (!sStash) {
|
|
sStash = new nsTArray<dom::RTCStatsReportInternal>();
|
|
ClearOnShutdown(&sStash);
|
|
}
|
|
return *sStash;
|
|
}
|
|
|
|
static RefPtr<PWebrtcGlobalParent::GetStatsPromise>
|
|
GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) {
|
|
nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises;
|
|
|
|
std::set<nsString> 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<dom::RTCStatsReportInternal>(report), __func__));
|
|
}
|
|
}
|
|
}
|
|
|
|
auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType::
|
|
ResolveOrRejectValue&& aResult) {
|
|
nsTArray<dom::RTCStatsReportInternal> 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<mozilla::ipc::ActorId, dom::Sequence<nsString>>&
|
|
GetWebrtcGlobalLogStash() {
|
|
static StaticAutoPtr<std::map<mozilla::ipc::ActorId, dom::Sequence<nsString>>>
|
|
sStash;
|
|
if (!sStash) {
|
|
sStash = new std::map<mozilla::ipc::ActorId, dom::Sequence<nsString>>();
|
|
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<DOMHighResTimeStamp>& aAfter,
|
|
const Optional<DOMHighResTimeStamp>& 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([&](const 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;
|
|
}
|
|
|
|
void WebrtcGlobalInformation::GetMediaContext(
|
|
const GlobalObject& aGlobal, WebrtcGlobalMediaContext& aContext) {
|
|
aContext.mHasH264Hardware = WebrtcVideoConduit::HasH264Hardware();
|
|
aContext.mHasAv1 = WebrtcVideoConduit::HasAv1();
|
|
}
|
|
|
|
using StatsPromiseArray =
|
|
nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>>;
|
|
|
|
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<StatsPromise>&& promise) {
|
|
auto AppendStatsHistory = [](StatsPromise::ResolveOrRejectValue&& result) {
|
|
if (result.IsReject()) {
|
|
return;
|
|
}
|
|
for (const auto& report : result.ResolveValue()) {
|
|
WebrtcGlobalStatsHistory::Record(
|
|
MakeUnique<RTCStatsReportInternal>(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<nsAString>& 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>(
|
|
"WebrtcGlobalStatisticsCallback", &aStatsCallback));
|
|
|
|
auto FlattenThenStashThenCallback =
|
|
[callbackHandle,
|
|
filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType::
|
|
ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
|
|
std::set<nsString> 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<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() {
|
|
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
|
|
if (!ctx) {
|
|
// This process has never created a PeerConnection, so no ICE logging.
|
|
return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
|
|
Sequence<nsString>(), __func__);
|
|
}
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsISerialEventTarget> stsThread =
|
|
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) {
|
|
return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
|
|
Sequence<nsString>(), __func__);
|
|
}
|
|
|
|
RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
|
|
|
|
auto AddMarkers =
|
|
[](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) {
|
|
nsString pid;
|
|
pid.AppendInt(getpid());
|
|
Sequence<nsString> 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<nsISerialEventTarget> stsThread =
|
|
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
if (!stsThread) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
RefPtr<MediaTransportHandler> 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<GenericPromise> UpdateLogStash() {
|
|
nsTArray<RefPtr<GenericPromise>> 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>(
|
|
"WebrtcGlobalLoggingCallback", &aLoggingCallback));
|
|
|
|
auto FilterThenCallback =
|
|
[pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue)
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
|
|
dom::Sequence<nsString> 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 bool sLastAECDebug = false;
|
|
MOZ_RUNINIT static Maybe<nsCString> sAecDebugLogDir;
|
|
|
|
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<nsITimer> sHistoryTimer;
|
|
static StaticAutoPtr<nsTHashSet<nsString>> sPcids;
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
auto HandleAdd = [&](nsString&& aPcid, bool aIsLongTermStatsDisabled) {
|
|
if (!sPcids) {
|
|
sPcids = new nsTHashSet<nsString>();
|
|
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<RTCStatsReportInternal>(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<dom::RTCStatsReportInternal>&& aReports) {
|
|
resolve(std::move(aReports));
|
|
},
|
|
[]() { MOZ_CRASH(); });
|
|
return IPC_OK();
|
|
}
|
|
|
|
aResolve(nsTArray<RTCStatsReportInternal>());
|
|
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<nsString>());
|
|
return IPC_OK();
|
|
}
|
|
|
|
GetLogPromise()->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[aResolve = std::move(aResolve)](
|
|
PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
|
|
if (aValue.IsResolve()) {
|
|
aResolve(aValue.ResolveValue());
|
|
} else {
|
|
aResolve(Sequence<nsString>());
|
|
}
|
|
});
|
|
|
|
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();
|
|
}
|
|
|
|
WebrtcGlobalChild* WebrtcGlobalChild::GetOrSet(
|
|
const Maybe<WebrtcGlobalChild*>& aChild) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(XRE_IsContentProcess());
|
|
static WebrtcGlobalChild* sChild;
|
|
if (!sChild && !aChild) {
|
|
sChild = static_cast<WebrtcGlobalChild*>(
|
|
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
|