summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp')
-rw-r--r--dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp829
1 files changed, 829 insertions, 0 deletions
diff --git a/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp b/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp
new file mode 100644
index 0000000000..7d0a9e64b1
--- /dev/null
+++ b/dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp
@@ -0,0 +1,829 @@
+/* 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 <algorithm>
+#include <vector>
+#include <type_traits>
+
+#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 <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;
+};
+
+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<int32_t, dom::Sequence<nsString>>& GetWebrtcGlobalLogStash() {
+ static StaticAutoPtr<std::map<int32_t, dom::Sequence<nsString>>> sStash;
+ if (!sStash) {
+ sStash = new std::map<int32_t, 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([&](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<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 int32_t sLastSetLevel = 0;
+static bool sLastAECDebug = false;
+static Maybe<nsCString> 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<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();
+}
+
+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<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