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