tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebrtcGlobalInformation.cpp (25884B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "WebrtcGlobalInformation.h"
      6 
      7 #include <algorithm>
      8 #include <vector>
      9 
     10 #include "ErrorList.h"
     11 #include "MediaTransportHandler.h"
     12 #include "PeerConnectionCtx.h"
     13 #include "PeerConnectionImpl.h"
     14 #include "WebrtcGlobalChild.h"
     15 #include "WebrtcGlobalParent.h"
     16 #include "WebrtcGlobalStatsHistory.h"
     17 #include "common/browser_logging/WebRtcLog.h"
     18 #include "libwebrtcglue/VideoConduit.h"
     19 #include "mozilla/Assertions.h"
     20 #include "mozilla/ClearOnShutdown.h"
     21 #include "mozilla/ErrorResult.h"
     22 #include "mozilla/RefPtr.h"
     23 #include "mozilla/StaticPtr.h"
     24 #include "mozilla/dom/ContentChild.h"
     25 #include "mozilla/dom/PWebrtcGlobal.h"
     26 #include "mozilla/dom/PWebrtcGlobalChild.h"
     27 #include "mozilla/dom/RTCStatsReportBinding.h"  // for RTCStatsReportInternal
     28 #include "mozilla/dom/WebrtcGlobalInformationBinding.h"
     29 #include "nsISupports.h"
     30 #include "nsITimer.h"
     31 #include "nsLiteralString.h"
     32 #include "nsNetCID.h"               // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
     33 #include "nsProxyRelease.h"         // nsMainThreadPtrHolder
     34 #include "nsServiceManagerUtils.h"  // do_GetService
     35 #include "nsString.h"
     36 #include "nsXULAppAPI.h"
     37 #include "transport/runnable_utils.h"
     38 
     39 #ifdef XP_WIN
     40 #  include <process.h>
     41 #endif
     42 
     43 namespace mozilla::dom {
     44 
     45 using StatsRequestCallback =
     46    nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback>;
     47 
     48 using LogRequestCallback = nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback>;
     49 
     50 class WebrtcContentParents {
     51 public:
     52  static WebrtcGlobalParent* Alloc();
     53  static void Dealloc(WebrtcGlobalParent* aParent);
     54  static bool Empty() { return sContentParents.empty(); }
     55  static const std::vector<RefPtr<WebrtcGlobalParent>>& GetAll() {
     56    return sContentParents;
     57  }
     58 
     59  WebrtcContentParents() = delete;
     60  WebrtcContentParents(const WebrtcContentParents&) = delete;
     61  WebrtcContentParents& operator=(const WebrtcContentParents&) = delete;
     62 
     63 private:
     64  static std::vector<RefPtr<WebrtcGlobalParent>> sContentParents;
     65 };
     66 
     67 MOZ_RUNINIT std::vector<RefPtr<WebrtcGlobalParent>>
     68    WebrtcContentParents::sContentParents;
     69 
     70 WebrtcGlobalParent* WebrtcContentParents::Alloc() {
     71  RefPtr<WebrtcGlobalParent> cp = new WebrtcGlobalParent;
     72  sContentParents.push_back(cp);
     73  return cp.get();
     74 }
     75 
     76 void WebrtcContentParents::Dealloc(WebrtcGlobalParent* aParent) {
     77  if (aParent) {
     78    aParent->mShutdown = true;
     79    auto cp =
     80        std::find(sContentParents.begin(), sContentParents.end(), aParent);
     81    if (cp != sContentParents.end()) {
     82      sContentParents.erase(cp);
     83    }
     84  }
     85 }
     86 
     87 static PeerConnectionCtx* GetPeerConnectionCtx() {
     88  if (PeerConnectionCtx::isActive()) {
     89    MOZ_ASSERT(PeerConnectionCtx::GetInstance());
     90    return PeerConnectionCtx::GetInstance();
     91  }
     92  return nullptr;
     93 }
     94 
     95 static nsTArray<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() {
     96  static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash;
     97  if (!sStash) {
     98    sStash = new nsTArray<dom::RTCStatsReportInternal>();
     99    ClearOnShutdown(&sStash);
    100  }
    101  return *sStash;
    102 }
    103 
    104 static RefPtr<PWebrtcGlobalParent::GetStatsPromise>
    105 GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) {
    106  nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises;
    107 
    108  std::set<nsString> pcids;
    109  if (auto* ctx = GetPeerConnectionCtx()) {
    110    // Grab stats for PCs that still exist
    111    ctx->ForEachPeerConnection([&](PeerConnectionImpl* aPc) {
    112      if (!aPcIdFilter.IsEmpty() &&
    113          !aPcIdFilter.EqualsASCII(aPc->GetIdAsAscii().c_str())) {
    114        return;
    115      }
    116      if (!aPc->IsClosed() || !aPc->LongTermStatsIsDisabled()) {
    117        nsString id;
    118        aPc->GetId(id);
    119        pcids.insert(id);
    120        promises.AppendElement(aPc->GetStats(nullptr, true));
    121      }
    122    });
    123 
    124    // Grab previously stashed stats, if they aren't dupes, and ensure they
    125    // are marked closed. (In a content process, this should already have
    126    // happened, but in the parent process, the stash will contain the last
    127    // observed stats from the content processes. From the perspective of the
    128    // parent process, these are assumed closed unless we see new stats from the
    129    // content process that say otherwise.)
    130    for (auto& report : GetWebrtcGlobalStatsStash()) {
    131      report.mClosed = true;
    132      if ((aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) &&
    133          !pcids.count(report.mPcid)) {
    134        promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve(
    135            MakeUnique<dom::RTCStatsReportInternal>(report), __func__));
    136      }
    137    }
    138  }
    139 
    140  auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType::
    141                                 ResolveOrRejectValue&& aResult) {
    142    nsTArray<dom::RTCStatsReportInternal> reports;
    143    MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!");
    144    for (auto& reportResult : aResult.ResolveValue()) {
    145      if (reportResult.IsResolve()) {
    146        reports.AppendElement(*reportResult.ResolveValue());
    147      }
    148    }
    149    return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve(
    150        std::move(reports), __func__);
    151  };
    152 
    153  return dom::RTCStatsReportPromise::AllSettled(
    154             GetMainThreadSerialEventTarget(), promises)
    155      ->Then(GetMainThreadSerialEventTarget(), __func__,
    156             std::move(UnwrapUniquePtrs));
    157 }
    158 
    159 static std::map<mozilla::ipc::ActorId, dom::Sequence<nsString>>&
    160 GetWebrtcGlobalLogStash() {
    161  static StaticAutoPtr<std::map<mozilla::ipc::ActorId, dom::Sequence<nsString>>>
    162      sStash;
    163  if (!sStash) {
    164    sStash = new std::map<mozilla::ipc::ActorId, dom::Sequence<nsString>>();
    165    ClearOnShutdown(&sStash);
    166  }
    167  return *sStash;
    168 }
    169 
    170 static void ClearLongTermStats() {
    171  if (!NS_IsMainThread()) {
    172    MOZ_ASSERT(NS_IsMainThread());
    173    return;
    174  }
    175 
    176  GetWebrtcGlobalStatsStash().Clear();
    177  if (XRE_IsParentProcess()) {
    178    WebrtcGlobalStatsHistory::Clear();
    179  }
    180  if (auto* ctx = GetPeerConnectionCtx()) {
    181    ctx->ClearClosedStats();
    182  }
    183 }
    184 
    185 void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) {
    186  if (!NS_IsMainThread()) {
    187    return;
    188  }
    189 
    190  // Chrome-only API
    191  MOZ_ASSERT(XRE_IsParentProcess());
    192 
    193  if (!WebrtcContentParents::Empty()) {
    194    // Pass on the request to any content process based PeerConnections.
    195    for (const auto& cp : WebrtcContentParents::GetAll()) {
    196      (void)cp->SendClearStats();
    197    }
    198  }
    199 
    200  // Flush the history for the chrome process
    201  ClearLongTermStats();
    202 }
    203 
    204 void WebrtcGlobalInformation::GetStatsHistoryPcIds(
    205    const GlobalObject& aGlobal,
    206    WebrtcGlobalStatisticsHistoryPcIdsCallback& aPcIdsCallback,
    207    ErrorResult& aRv) {
    208  if (!NS_IsMainThread()) {
    209    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
    210    return;
    211  }
    212 
    213  MOZ_ASSERT(XRE_IsParentProcess());
    214 
    215  IgnoredErrorResult rv;
    216  aPcIdsCallback.Call(WebrtcGlobalStatsHistory::PcIds(), rv);
    217  aRv = NS_OK;
    218 }
    219 
    220 void WebrtcGlobalInformation::GetStatsHistorySince(
    221    const GlobalObject& aGlobal,
    222    WebrtcGlobalStatisticsHistoryCallback& aStatsCallback,
    223    const nsAString& pcIdFilter, const Optional<DOMHighResTimeStamp>& aAfter,
    224    const Optional<DOMHighResTimeStamp>& aSdpAfter, ErrorResult& aRv) {
    225  if (!NS_IsMainThread()) {
    226    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
    227    return;
    228  }
    229 
    230  MOZ_ASSERT(XRE_IsParentProcess());
    231 
    232  WebrtcGlobalStatisticsReport history;
    233 
    234  auto statsAfter = aAfter.WasPassed() ? Some(aAfter.Value()) : Nothing();
    235  auto sdpAfter = aSdpAfter.WasPassed() ? Some(aSdpAfter.Value()) : Nothing();
    236 
    237  WebrtcGlobalStatsHistory::GetHistory(pcIdFilter).apply([&](const auto& hist) {
    238    if (!history.mReports.AppendElements(hist->Since(statsAfter), fallible)) {
    239      mozalloc_handle_oom(0);
    240    }
    241    if (!history.mSdpHistories.AppendElement(hist->SdpSince(sdpAfter),
    242                                             fallible)) {
    243      mozalloc_handle_oom(0);
    244    }
    245  });
    246 
    247  IgnoredErrorResult rv;
    248  aStatsCallback.Call(history, rv);
    249  aRv = NS_OK;
    250 }
    251 
    252 void WebrtcGlobalInformation::GetMediaContext(
    253    const GlobalObject& aGlobal, WebrtcGlobalMediaContext& aContext) {
    254  aContext.mHasH264Hardware = WebrtcVideoConduit::HasH264Hardware();
    255  aContext.mHasAv1 = WebrtcVideoConduit::HasAv1();
    256 }
    257 
    258 using StatsPromiseArray =
    259    nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>>;
    260 
    261 void WebrtcGlobalInformation::GatherHistory() {
    262  const nsString emptyFilter;
    263  if (!NS_IsMainThread()) {
    264    MOZ_ASSERT(NS_IsMainThread());
    265    return;
    266  }
    267 
    268  MOZ_ASSERT(XRE_IsParentProcess());
    269  using StatsPromise = PWebrtcGlobalParent::GetStatsPromise;
    270  auto resolveThenAppendStatsHistory = [](RefPtr<StatsPromise>&& promise) {
    271    auto AppendStatsHistory = [](StatsPromise::ResolveOrRejectValue&& result) {
    272      if (result.IsReject()) {
    273        return;
    274      }
    275      for (const auto& report : result.ResolveValue()) {
    276        WebrtcGlobalStatsHistory::Record(
    277            MakeUnique<RTCStatsReportInternal>(report));
    278      }
    279    };
    280    promise->Then(GetMainThreadSerialEventTarget(), __func__,
    281                  std::move(AppendStatsHistory));
    282  };
    283  for (const auto& cp : WebrtcContentParents::GetAll()) {
    284    resolveThenAppendStatsHistory(cp->SendGetStats(emptyFilter));
    285  }
    286  resolveThenAppendStatsHistory(GetStatsPromiseForThisProcess(emptyFilter));
    287 }
    288 
    289 void WebrtcGlobalInformation::GetAllStats(
    290    const GlobalObject& aGlobal, WebrtcGlobalStatisticsCallback& aStatsCallback,
    291    const Optional<nsAString>& aPcIdFilter, ErrorResult& aRv) {
    292  if (!NS_IsMainThread()) {
    293    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
    294    return;
    295  }
    296 
    297  MOZ_ASSERT(XRE_IsParentProcess());
    298 
    299  StatsPromiseArray statsPromises;
    300 
    301  nsString filter;
    302  if (aPcIdFilter.WasPassed()) {
    303    filter = aPcIdFilter.Value();
    304  }
    305 
    306  for (const auto& cp : WebrtcContentParents::GetAll()) {
    307    statsPromises.AppendElement(cp->SendGetStats(filter));
    308  }
    309 
    310  // Stats from this (the parent) process. How long do we keep supporting this?
    311  statsPromises.AppendElement(GetStatsPromiseForThisProcess(filter));
    312 
    313  // CallbackObject does not support threadsafe refcounting, and must be
    314  // used and destroyed on main.
    315  StatsRequestCallback callbackHandle(
    316      new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(
    317          "WebrtcGlobalStatisticsCallback", &aStatsCallback));
    318 
    319  auto FlattenThenStashThenCallback =
    320      [callbackHandle,
    321       filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType::
    322                   ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    323        std::set<nsString> pcids;
    324        WebrtcGlobalStatisticsReport flattened;
    325        MOZ_RELEASE_ASSERT(aResult.IsResolve(),
    326                           "AllSettled should never reject!");
    327        // Flatten stats from content processes and parent process.
    328        // The stats from the parent process (which will come last) might
    329        // contain some stale content-process stats, so skip those.
    330        for (auto& processResult : aResult.ResolveValue()) {
    331          // TODO: Report rejection on individual content processes someday?
    332          if (processResult.IsResolve()) {
    333            for (auto& pcStats : processResult.ResolveValue()) {
    334              if (!pcids.count(pcStats.mPcid)) {
    335                pcids.insert(pcStats.mPcid);
    336                if (!flattened.mReports.AppendElement(std::move(pcStats),
    337                                                      fallible)) {
    338                  mozalloc_handle_oom(0);
    339                }
    340              }
    341            }
    342          }
    343        }
    344 
    345        if (filter.IsEmpty()) {
    346          // Unfiltered is simple; the flattened result becomes the new stash.
    347          GetWebrtcGlobalStatsStash() = flattened.mReports;
    348        } else if (!flattened.mReports.IsEmpty()) {
    349          // Update our stash with the single result.
    350          MOZ_ASSERT(flattened.mReports.Length() == 1);
    351          StashStats(flattened.mReports[0]);
    352        }
    353 
    354        IgnoredErrorResult rv;
    355        callbackHandle->Call(flattened, rv);
    356      };
    357 
    358  PWebrtcGlobalParent::GetStatsPromise::AllSettled(
    359      GetMainThreadSerialEventTarget(), statsPromises)
    360      ->Then(GetMainThreadSerialEventTarget(), __func__,
    361             std::move(FlattenThenStashThenCallback));
    362 
    363  aRv = NS_OK;
    364 }
    365 
    366 static RefPtr<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() {
    367  PeerConnectionCtx* ctx = GetPeerConnectionCtx();
    368  if (!ctx) {
    369    // This process has never created a PeerConnection, so no ICE logging.
    370    return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
    371        Sequence<nsString>(), __func__);
    372  }
    373 
    374  nsresult rv;
    375  nsCOMPtr<nsISerialEventTarget> stsThread =
    376      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    377 
    378  if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) {
    379    return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
    380        Sequence<nsString>(), __func__);
    381  }
    382 
    383  RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
    384 
    385  auto AddMarkers =
    386      [](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) {
    387        nsString pid;
    388        pid.AppendInt(getpid());
    389        Sequence<nsString> logs;
    390        if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
    391          bool ok = logs.AppendElement(
    392              u"+++++++ BEGIN (process id "_ns + pid + u") ++++++++"_ns,
    393              fallible);
    394          ok &=
    395              !!logs.AppendElements(std::move(aValue.ResolveValue()), fallible);
    396          ok &= !!logs.AppendElement(
    397              u"+++++++ END (process id "_ns + pid + u") ++++++++"_ns,
    398              fallible);
    399          if (!ok) {
    400            mozalloc_handle_oom(0);
    401          }
    402        }
    403        return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
    404            std::move(logs), __func__);
    405      };
    406 
    407  return transportHandler->GetIceLog(nsCString())
    408      ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers));
    409 }
    410 
    411 static nsresult RunLogClear() {
    412  PeerConnectionCtx* ctx = GetPeerConnectionCtx();
    413  if (!ctx) {
    414    // This process has never created a PeerConnection, so no ICE logging.
    415    return NS_OK;
    416  }
    417 
    418  nsresult rv;
    419  nsCOMPtr<nsISerialEventTarget> stsThread =
    420      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    421 
    422  if (NS_FAILED(rv)) {
    423    return rv;
    424  }
    425  if (!stsThread) {
    426    return NS_ERROR_FAILURE;
    427  }
    428 
    429  RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
    430 
    431  return RUN_ON_THREAD(
    432      stsThread,
    433      WrapRunnable(transportHandler, &MediaTransportHandler::ClearIceLog),
    434      NS_DISPATCH_NORMAL);
    435 }
    436 
    437 void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) {
    438  if (!NS_IsMainThread()) {
    439    return;
    440  }
    441 
    442  // Chrome-only API
    443  MOZ_ASSERT(XRE_IsParentProcess());
    444  GetWebrtcGlobalLogStash().clear();
    445 
    446  if (!WebrtcContentParents::Empty()) {
    447    // Clear content process signaling logs
    448    for (const auto& cp : WebrtcContentParents::GetAll()) {
    449      (void)cp->SendClearLog();
    450    }
    451  }
    452 
    453  // Clear chrome process signaling logs
    454  (void)RunLogClear();
    455 }
    456 
    457 static RefPtr<GenericPromise> UpdateLogStash() {
    458  nsTArray<RefPtr<GenericPromise>> logPromises;
    459  MOZ_ASSERT(XRE_IsParentProcess());
    460  for (const auto& cp : WebrtcContentParents::GetAll()) {
    461    auto StashLog =
    462        [id = cp->Id() * 2 /* Make sure 1 isn't used */](
    463            PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
    464          if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
    465            GetWebrtcGlobalLogStash()[id] = aValue.ResolveValue();
    466          }
    467          return GenericPromise::CreateAndResolve(true, __func__);
    468        };
    469    logPromises.AppendElement(cp->SendGetLog()->Then(
    470        GetMainThreadSerialEventTarget(), __func__, std::move(StashLog)));
    471  }
    472 
    473  // Get ICE logging for this (the parent) process. How long do we support this?
    474  logPromises.AppendElement(GetLogPromise()->Then(
    475      GetMainThreadSerialEventTarget(), __func__,
    476      [](PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
    477        if (aValue.IsResolve()) {
    478          GetWebrtcGlobalLogStash()[1] = aValue.ResolveValue();
    479        }
    480        return GenericPromise::CreateAndResolve(true, __func__);
    481      }));
    482 
    483  return GenericPromise::AllSettled(GetMainThreadSerialEventTarget(),
    484                                    logPromises)
    485      ->Then(GetMainThreadSerialEventTarget(), __func__,
    486             [](GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&&
    487                    aValue) {
    488               // We don't care about the value, since we're just going to copy
    489               // what is in the stash. This ignores failures too, which is what
    490               // we want.
    491               return GenericPromise::CreateAndResolve(true, __func__);
    492             });
    493 }
    494 
    495 void WebrtcGlobalInformation::GetLogging(
    496    const GlobalObject& aGlobal, const nsAString& aPattern,
    497    WebrtcGlobalLoggingCallback& aLoggingCallback, ErrorResult& aRv) {
    498  if (!NS_IsMainThread()) {
    499    aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
    500    return;
    501  }
    502 
    503  MOZ_ASSERT(XRE_IsParentProcess());
    504 
    505  nsAutoString pattern(aPattern);
    506 
    507  // CallbackObject does not support threadsafe refcounting, and must be
    508  // destroyed on main.
    509  LogRequestCallback callbackHandle(
    510      new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(
    511          "WebrtcGlobalLoggingCallback", &aLoggingCallback));
    512 
    513  auto FilterThenCallback =
    514      [pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue)
    515          MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    516            dom::Sequence<nsString> flattened;
    517            for (const auto& [id, log] : GetWebrtcGlobalLogStash()) {
    518              (void)id;
    519              for (const auto& line : log) {
    520                if (pattern.IsEmpty() || (line.Find(pattern) != kNotFound)) {
    521                  if (!flattened.AppendElement(line, fallible)) {
    522                    mozalloc_handle_oom(0);
    523                  }
    524                }
    525              }
    526            }
    527            IgnoredErrorResult rv;
    528            callbackHandle->Call(flattened, rv);
    529          };
    530 
    531  UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__,
    532                         std::move(FilterThenCallback));
    533  aRv = NS_OK;
    534 }
    535 
    536 static bool sLastAECDebug = false;
    537 MOZ_RUNINIT static Maybe<nsCString> sAecDebugLogDir;
    538 
    539 void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal,
    540                                          bool aEnable) {
    541  if (aEnable) {
    542    sAecDebugLogDir = Some(StartAecLog());
    543  } else {
    544    StopAecLog();
    545  }
    546 
    547  sLastAECDebug = aEnable;
    548 
    549  for (const auto& cp : WebrtcContentParents::GetAll()) {
    550    (void)cp->SendSetAecLogging(aEnable);
    551  }
    552 }
    553 
    554 bool WebrtcGlobalInformation::AecDebug(const GlobalObject& aGlobal) {
    555  return sLastAECDebug;
    556 }
    557 
    558 void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal,
    559                                                nsAString& aDir) {
    560  aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns));
    561 }
    562 
    563 /*static*/
    564 void WebrtcGlobalInformation::StashStats(
    565    const dom::RTCStatsReportInternal& aReport) {
    566  // Remove previous report, if present
    567  // TODO: Make this a map instead of an array?
    568  for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) {
    569    auto& pcStats = GetWebrtcGlobalStatsStash()[i];
    570    if (pcStats.mPcid == aReport.mPcid) {
    571      GetWebrtcGlobalStatsStash().RemoveElementAt(i);
    572      break;
    573    }
    574    ++i;
    575  }
    576  // Stash final stats
    577  GetWebrtcGlobalStatsStash().AppendElement(aReport);
    578 }
    579 
    580 void WebrtcGlobalInformation::AdjustTimerReferences(
    581    PcTrackingUpdate&& aUpdate) {
    582  static StaticRefPtr<nsITimer> sHistoryTimer;
    583  static StaticAutoPtr<nsTHashSet<nsString>> sPcids;
    584 
    585  MOZ_ASSERT(NS_IsMainThread());
    586 
    587  auto HandleAdd = [&](nsString&& aPcid, bool aIsLongTermStatsDisabled) {
    588    if (!sPcids) {
    589      sPcids = new nsTHashSet<nsString>();
    590      ClearOnShutdown(&sPcids);
    591    }
    592    sPcids->EnsureInserted(aPcid);
    593    // Reserve a stats history
    594    WebrtcGlobalStatsHistory::InitHistory(nsString(aPcid),
    595                                          aIsLongTermStatsDisabled);
    596    if (!sHistoryTimer) {
    597      sHistoryTimer = NS_NewTimer(GetMainThreadSerialEventTarget());
    598      if (sHistoryTimer) {
    599        sHistoryTimer->InitWithNamedFuncCallback(
    600            [](nsITimer* aTimer, void* aClosure) {
    601              if (WebrtcGlobalStatsHistory::Pref::Enabled()) {
    602                WebrtcGlobalInformation::GatherHistory();
    603              }
    604            },
    605            nullptr, WebrtcGlobalStatsHistory::Pref::PollIntervalMs(),
    606            nsITimer::TYPE_REPEATING_SLACK,
    607            "WebrtcGlobalInformation::GatherHistory"_ns);
    608      }
    609      ClearOnShutdown(&sHistoryTimer);
    610    }
    611  };
    612 
    613  auto HandleRemove = [&](const nsString& aRemoved) {
    614    WebrtcGlobalStatsHistory::CloseHistory(nsString(aRemoved));
    615    if (!sPcids || !sPcids->Count()) {
    616      return;
    617    }
    618    if (!sPcids->Contains(aRemoved)) {
    619      return;
    620    }
    621    sPcids->Remove(aRemoved);
    622    if (!sPcids->Count() && sHistoryTimer) {
    623      sHistoryTimer->Cancel();
    624      sHistoryTimer = nullptr;
    625    }
    626  };
    627 
    628  switch (aUpdate.Type()) {
    629    case PcTrackingUpdate::Type::Add: {
    630      HandleAdd(std::move(aUpdate.mPcid),
    631                aUpdate.mLongTermStatsDisabled.valueOrFrom([&]() {
    632                  MOZ_ASSERT(aUpdate.mLongTermStatsDisabled.isNothing());
    633                  return true;
    634                }));
    635      return;
    636    }
    637    case PcTrackingUpdate::Type::Remove: {
    638      HandleRemove(aUpdate.mPcid);
    639      return;
    640    }
    641    default: {
    642      MOZ_ASSERT(false, "Invalid PcCount operation");
    643    }
    644  }
    645 }
    646 
    647 WebrtcGlobalParent* WebrtcGlobalParent::Alloc() {
    648  return WebrtcContentParents::Alloc();
    649 }
    650 
    651 bool WebrtcGlobalParent::Dealloc(WebrtcGlobalParent* aActor) {
    652  WebrtcContentParents::Dealloc(aActor);
    653  return true;
    654 }
    655 
    656 void WebrtcGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
    657  mShutdown = true;
    658  for (const auto& pcId : mPcids) {
    659    using Update = WebrtcGlobalInformation::PcTrackingUpdate;
    660    auto update = Update::Remove(nsString(pcId));
    661    WebrtcGlobalInformation::PeerConnectionTracking(update);
    662  }
    663 }
    664 
    665 mozilla::ipc::IPCResult WebrtcGlobalParent::Recv__delete__() {
    666  return IPC_OK();
    667 }
    668 
    669 mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionCreated(
    670    const nsAString& aPcId, const bool& aIsLongTermStatsDisabled) {
    671  if (mShutdown) {
    672    return IPC_OK();
    673  }
    674  mPcids.EnsureInserted(aPcId);
    675  using Update = WebrtcGlobalInformation::PcTrackingUpdate;
    676  auto update = Update::Add(nsString(aPcId), aIsLongTermStatsDisabled);
    677  WebrtcGlobalInformation::PeerConnectionTracking(update);
    678  return IPC_OK();
    679 }
    680 
    681 mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionDestroyed(
    682    const nsAString& aPcId) {
    683  mPcids.EnsureRemoved(aPcId);
    684  using Update = WebrtcGlobalInformation::PcTrackingUpdate;
    685  auto update = Update::Remove(nsString(aPcId));
    686  WebrtcGlobalStatsHistory::CloseHistory(aPcId);
    687  WebrtcGlobalInformation::PeerConnectionTracking(update);
    688  return IPC_OK();
    689 }
    690 
    691 mozilla::ipc::IPCResult WebrtcGlobalParent::RecvPeerConnectionFinalStats(
    692    const RTCStatsReportInternal& aFinalStats) {
    693  auto finalStats = MakeUnique<RTCStatsReportInternal>(aFinalStats);
    694  WebrtcGlobalStatsHistory::Record(std::move(finalStats));
    695  WebrtcGlobalStatsHistory::CloseHistory(aFinalStats.mPcid);
    696  return IPC_OK();
    697 }
    698 
    699 MOZ_IMPLICIT WebrtcGlobalParent::WebrtcGlobalParent() : mShutdown(false) {
    700  MOZ_COUNT_CTOR(WebrtcGlobalParent);
    701 }
    702 
    703 MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() {
    704  MOZ_COUNT_DTOR(WebrtcGlobalParent);
    705 }
    706 
    707 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats(
    708    const nsAString& aPcIdFilter, GetStatsResolver&& aResolve) {
    709  if (!mShutdown) {
    710    GetStatsPromiseForThisProcess(aPcIdFilter)
    711        ->Then(
    712            GetMainThreadSerialEventTarget(), __func__,
    713            [resolve = std::move(aResolve)](
    714                nsTArray<dom::RTCStatsReportInternal>&& aReports) {
    715              resolve(std::move(aReports));
    716            },
    717            []() { MOZ_CRASH(); });
    718    return IPC_OK();
    719  }
    720 
    721  aResolve(nsTArray<RTCStatsReportInternal>());
    722  return IPC_OK();
    723 }
    724 
    725 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() {
    726  if (mShutdown) {
    727    return IPC_OK();
    728  }
    729 
    730  ClearLongTermStats();
    731  return IPC_OK();
    732 }
    733 
    734 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog(
    735    GetLogResolver&& aResolve) {
    736  if (mShutdown) {
    737    aResolve(Sequence<nsString>());
    738    return IPC_OK();
    739  }
    740 
    741  GetLogPromise()->Then(
    742      GetMainThreadSerialEventTarget(), __func__,
    743      [aResolve = std::move(aResolve)](
    744          PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
    745        if (aValue.IsResolve()) {
    746          aResolve(aValue.ResolveValue());
    747        } else {
    748          aResolve(Sequence<nsString>());
    749        }
    750      });
    751 
    752  return IPC_OK();
    753 }
    754 
    755 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() {
    756  if (mShutdown) {
    757    return IPC_OK();
    758  }
    759 
    760  RunLogClear();
    761  return IPC_OK();
    762 }
    763 
    764 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetAecLogging(
    765    const bool& aEnable) {
    766  if (!mShutdown) {
    767    if (aEnable) {
    768      StartAecLog();
    769    } else {
    770      StopAecLog();
    771    }
    772  }
    773  return IPC_OK();
    774 }
    775 
    776 WebrtcGlobalChild* WebrtcGlobalChild::GetOrSet(
    777    const Maybe<WebrtcGlobalChild*>& aChild) {
    778  MOZ_ASSERT(NS_IsMainThread());
    779  MOZ_ASSERT(XRE_IsContentProcess());
    780  static WebrtcGlobalChild* sChild;
    781  if (!sChild && !aChild) {
    782    sChild = static_cast<WebrtcGlobalChild*>(
    783        ContentChild::GetSingleton()->SendPWebrtcGlobalConstructor());
    784  }
    785  aChild.apply([](auto* child) { sChild = child; });
    786  return sChild;
    787 }
    788 
    789 WebrtcGlobalChild* WebrtcGlobalChild::Get() { return GetOrSet(Nothing()); }
    790 
    791 void WebrtcGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
    792  mShutdown = true;
    793 }
    794 
    795 MOZ_IMPLICIT WebrtcGlobalChild::WebrtcGlobalChild() : mShutdown(false) {
    796  MOZ_COUNT_CTOR(WebrtcGlobalChild);
    797 }
    798 
    799 MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() {
    800  MOZ_COUNT_DTOR(WebrtcGlobalChild);
    801  GetOrSet(Some(nullptr));
    802 }
    803 
    804 }  // namespace mozilla::dom