Backed out 11 changesets (bug 1626278) for leaks and WebrtcGlobalInformation related failures CLOSED TREE

Backed out changeset 59228ee9d9e0 (bug 1626278)
Backed out changeset 59da0d11510e (bug 1626278)
Backed out changeset cd2e50c8af34 (bug 1626278)
Backed out changeset afdb75a17ac9 (bug 1626278)
Backed out changeset 5f453c8df70b (bug 1626278)
Backed out changeset 9b612ea670d4 (bug 1626278)
Backed out changeset 387a53fd83f3 (bug 1626278)
Backed out changeset 5870625073f6 (bug 1626278)
Backed out changeset 25c03ac56306 (bug 1626278)
Backed out changeset 3239d49be3ee (bug 1626278)
Backed out changeset 82ed327e71ed (bug 1626278)
This commit is contained in:
Bogdan Tara 2020-10-14 02:34:48 +03:00
parent dc6e67221c
commit 09263ac198
20 changed files with 583 additions and 938 deletions

View File

@ -6,8 +6,6 @@ support-files =
get_user_media_in_xorigin_frame.html
get_user_media_in_xorigin_frame_ancestor.html
head.js
single_peerconnection.html
prefs =
privacy.webrtc.allowSilencingNotifications=true
privacy.webrtc.legacyGlobalIndicator=false
@ -43,4 +41,3 @@ skip-if = (os == "win" && bits == 64) # win8: bug 1334752
[browser_tab_switch_warning.js]
[browser_webrtc_hooks.js]
[browser_devices_get_user_media_queue_request.js]
[browser_WebrtcGlobalInformation.js]

View File

@ -1,226 +0,0 @@
/* 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/. */
"use strict";
XPCOMUtils.defineLazyModuleGetters(this, {
BrowserTestUtils: "resource://testing-common/BrowserTestUtils.jsm",
});
const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
Ci.nsIProcessToolsService
);
let getStatsReports = async (filter = "") => {
let { reports } = await new Promise(r =>
WebrtcGlobalInformation.getAllStats(r, filter)
);
ok(Array.isArray(reports), "|reports| is an array");
let sanityCheckReport = report => {
isnot(report.pcid, "", "pcid is non-empty");
if (filter.length) {
is(report.pcid, filter, "pcid matches filter");
}
};
reports.forEach(sanityCheckReport);
return reports;
};
let getLogging = async () => {
let logs = await new Promise(r => WebrtcGlobalInformation.getLogging("", r));
ok(Array.isArray(logs), "|logs| is an array");
return logs;
};
let checkStatsReportCount = async (count, filter = "") => {
let reports = await getStatsReports(filter);
is(reports.length, count, `|reports| should have length ${count}`);
if (reports.length != count) {
info(`reports = ${JSON.stringify(reports)}`);
}
return reports;
};
let checkLoggingEmpty = async () => {
let logs = await getLogging();
is(logs.length, 0, "Logging is empty");
if (logs.length) {
info(`logs = ${JSON.stringify(logs)}`);
}
return logs;
};
let checkLoggingNonEmpty = async () => {
let logs = await getLogging();
isnot(logs.length, 0, "Logging is not empty");
return logs;
};
let clearAndCheck = async () => {
WebrtcGlobalInformation.clearAllStats();
WebrtcGlobalInformation.clearLogging();
await checkStatsReportCount(0);
await checkLoggingEmpty();
};
let openTabInNewProcess = async file => {
let rootDir = getRootDirectory(gTestPath);
rootDir = rootDir.replace(
"chrome://mochitests/content/",
"https://example.com/"
);
let absoluteURI = rootDir + file;
return BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: absoluteURI,
forceNewProcess: true,
});
};
let killTabProcess = async tab => {
ProcessTools.kill(tab.linkedBrowser.frameLoader.remoteTab.osPid);
};
add_task(async () => {
info("Test that clearAllStats is callable");
WebrtcGlobalInformation.clearAllStats();
ok(true, "clearAllStats returns");
});
add_task(async () => {
info("Test that clearLogging is callable");
WebrtcGlobalInformation.clearLogging();
ok(true, "clearLogging returns");
});
add_task(async () => {
info(
"Test that getAllStats is callable, and returns 0 results when no RTCPeerConnections have existed"
);
await checkStatsReportCount(0);
});
add_task(async () => {
info(
"Test that getLogging is callable, and returns 0 results when no RTCPeerConnections have existed"
);
await checkLoggingEmpty();
});
add_task(async () => {
info("Test that we can get stats/logging for a PC on the parent process");
await clearAndCheck();
let pc = new RTCPeerConnection();
await pc.setLocalDescription(
await pc.createOffer({ offerToReceiveAudio: true })
);
// Let ICE stack go quiescent
await new Promise(r => {
pc.onicegatheringstatechange = () => {
if (pc.iceGatheringState == "complete") {
r();
}
};
});
await checkStatsReportCount(1);
await checkLoggingNonEmpty();
pc.close();
pc = null;
// Closing a PC should not do anything to the ICE logging
await checkLoggingNonEmpty();
await clearAndCheck();
});
add_task(async () => {
info("Test that we can get stats/logging for a PC on a content process");
await clearAndCheck();
let tab = await openTabInNewProcess("single_peerconnection.html");
await checkStatsReportCount(1);
await checkLoggingNonEmpty();
await killTabProcess(tab);
BrowserTestUtils.removeTab(tab);
await clearAndCheck();
});
add_task(async () => {
info("Test filtering for stats reports (parent process)");
await clearAndCheck();
let pc1 = new RTCPeerConnection();
let pc2 = new RTCPeerConnection();
let allReports = await checkStatsReportCount(2);
await checkStatsReportCount(1, allReports[0].pcid);
pc1.close();
pc2.close();
pc1 = null;
pc2 = null;
await checkStatsReportCount(1, allReports[0].pcid);
await clearAndCheck();
});
add_task(async () => {
info("Test filtering for stats reports (content process)");
await clearAndCheck();
let tab1 = await openTabInNewProcess("single_peerconnection.html");
let tab2 = await openTabInNewProcess("single_peerconnection.html");
let allReports = await checkStatsReportCount(2);
await checkStatsReportCount(1, allReports[0].pcid);
await killTabProcess(tab1);
BrowserTestUtils.removeTab(tab1);
await killTabProcess(tab2);
BrowserTestUtils.removeTab(tab2);
await checkStatsReportCount(1, allReports[0].pcid);
await clearAndCheck();
});
add_task(async () => {
info("Test that stats/logging persists when PC is closed (parent process)");
await clearAndCheck();
let pc = new RTCPeerConnection();
// This stuff will generate logging
await pc.setLocalDescription(
await pc.createOffer({ offerToReceiveAudio: true })
);
await new Promise(r => (pc.onicecandidate = r));
let reports = await checkStatsReportCount(1);
isnot(
window.browsingContext.browserId,
undefined,
"browserId is defined for parent process"
);
is(
reports[0].browserId,
window.browsingContext.browserId,
"browserId for stats report matches parent process"
);
await checkLoggingNonEmpty();
pc.close();
pc = null;
await checkStatsReportCount(1);
await checkLoggingNonEmpty();
await clearAndCheck();
});
add_task(async () => {
info("Test that stats/logging persists when PC is closed (content process)");
await clearAndCheck();
let tab = await openTabInNewProcess("single_peerconnection.html");
let { browserId } = tab.linkedBrowser;
let reports = await checkStatsReportCount(1);
is(reports[0].browserId, browserId, "browserId for stats report matches tab");
isnot(
browserId,
window.browsingContext.browserId,
"tab browser id is not the same as parent process browser id"
);
await checkLoggingNonEmpty();
await killTabProcess(tab);
BrowserTestUtils.removeTab(tab);
await checkStatsReportCount(1);
await checkLoggingNonEmpty();
await clearAndCheck();
});

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"></head>
<body>
<div id="Page that opens a single peerconnection"></div>
<script>
let test = async () => {
let pc = new RTCPeerConnection();
await pc.setLocalDescription(await pc.createOffer({offerToReceiveAudio: true}));
};
test();
</script>
</body>
</html>

View File

@ -20,12 +20,14 @@ async protocol PWebrtcGlobal {
parent: // child -> parent messages
async __delete__();
#ifdef MOZ_WEBRTC
async GetStatsResult(int aRequestId, RTCStatsReportInternal[] aStats);
async GetLogResult(int aRequestId, WebrtcGlobalLog aLog);
child: // parent -> child messages
async GetStats(nsString aPcIdFilter) returns (RTCStatsReportInternal[] stats);
async ClearStats();
async GetLog() returns (WebrtcGlobalLog logs);
async ClearLog();
async GetStatsRequest(int aRequestId, nsString aPcIdFilter);
async ClearStatsRequest();
async GetLogRequest(int aRequestId, nsCString aPattern);
async ClearLogRequest();
async SetAecLogging(bool aEnable);
async SetDebugMode(int aLevel);
#endif

View File

@ -33,7 +33,6 @@
#include "mozilla/Algorithm.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
@ -65,6 +64,7 @@ class MediaTransportHandlerSTS : public MediaTransportHandler,
nsresult CreateIceCtx(const std::string& aName,
const nsTArray<dom::RTCIceServer>& aIceServers,
dom::RTCIceTransportPolicy aIcePolicy) override;
void Destroy() override;
// We will probably be able to move the proxy lookup stuff into
// this class once we move mtransport to its own process.
@ -114,13 +114,7 @@ class MediaTransportHandlerSTS : public MediaTransportHandler,
RefPtr<dom::RTCStatsPromise> GetIceStats(const std::string& aTransportId,
DOMHighResTimeStamp aNow) override;
void Shutdown();
private:
void Destroy() override;
void Destroy_s();
void DestroyFinal();
void Shutdown_s();
RefPtr<TransportFlow> CreateTransportFlow(const std::string& aTransportId,
bool aIsRtcp,
RefPtr<DtlsIdentity> aDtlsIdentity,
@ -189,41 +183,6 @@ already_AddRefed<MediaTransportHandler> MediaTransportHandler::Create(
return result.forget();
}
class STSShutdownHandler {
public:
~STSShutdownHandler() {
MOZ_ASSERT(NS_IsMainThread());
for (const auto& handler : mHandlers) {
handler->Shutdown();
}
}
void Register(MediaTransportHandlerSTS* aHandler) {
MOZ_ASSERT(NS_IsMainThread());
mHandlers.insert(aHandler);
}
void Deregister(MediaTransportHandlerSTS* aHandler) {
MOZ_ASSERT(NS_IsMainThread());
mHandlers.erase(aHandler);
}
private:
// Raw ptrs, registered on init, deregistered on destruction, all on main
std::set<MediaTransportHandlerSTS*> mHandlers;
};
static STSShutdownHandler* GetShutdownHandler() {
MOZ_ASSERT(NS_IsMainThread());
static UniquePtr<STSShutdownHandler> sHandler(new STSShutdownHandler);
static bool initted = false;
if (!initted) {
initted = true;
ClearOnShutdown(&sHandler, ShutdownPhase::WillShutdown);
}
return sHandler.get();
}
MediaTransportHandlerSTS::MediaTransportHandlerSTS(
nsISerialEventTarget* aCallbackThread)
: MediaTransportHandler(aCallbackThread) {
@ -235,7 +194,7 @@ MediaTransportHandlerSTS::MediaTransportHandlerSTS(
RLogConnector::CreateInstance();
CSFLogDebug(LOGTAG, "%s done %p", __func__, this);
CSFLogDebug(LOGTAG, "%s done", __func__);
// We do not set up mDNSService here, because we are not running on main (we
// use PBackground), and the DNS service asserts.
@ -484,8 +443,6 @@ nsresult MediaTransportHandlerSTS::CreateIceCtx(
config.mPolicy = toNrIcePolicy(aIcePolicy);
config.mNatSimulatorConfig = GetNatConfig();
GetShutdownHandler()->Register(this);
return InvokeAsync(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
@ -542,96 +499,49 @@ nsresult MediaTransportHandlerSTS::CreateIceCtx(
return NS_OK;
}
void MediaTransportHandlerSTS::Shutdown() {
CSFLogDebug(LOGTAG, "%s", __func__);
if (!mStsThread->IsOnCurrentThread()) {
mStsThread->Dispatch(NewNonOwningRunnableMethod(
__func__, this, &MediaTransportHandlerSTS::Shutdown_s));
return;
}
Shutdown_s();
}
void MediaTransportHandlerSTS::Shutdown_s() {
CSFLogDebug(LOGTAG, "%s", __func__);
disconnect_all();
// Clear the transports before destroying the ice ctx so that
// the close_notify alerts have a chance to be sent as the
// TransportFlow destructors execute.
mTransports.clear();
if (mIceCtx) {
// We're already on the STS thread, but the TransportFlow
// destructors executed when mTransports.clear() is called
// above dispatch calls to DestroyFinal to the STS thread. If
// we don't also dispatch the call to destory the NrIceCtx to
// the STS thread, it will tear down the NrIceMediaStreams
// before the TransportFlows are destroyed. Without a valid
// NrIceMediaStreams the close_notify alert cannot be sent.
mStsThread->Dispatch(
NS_NewRunnableFunction(__func__, [iceCtx = RefPtr<NrIceCtx>(mIceCtx)] {
NrIceStats stats = iceCtx->Destroy();
CSFLogDebug(LOGTAG,
"Ice Telemetry: stun (retransmits: %d)"
" turn (401s: %d 403s: %d 438s: %d)",
stats.stun_retransmits, stats.turn_401s, stats.turn_403s,
stats.turn_438s);
}));
}
mIceCtx = nullptr;
}
void MediaTransportHandlerSTS::Destroy() {
CSFLogDebug(LOGTAG, "%s %p", __func__, this);
// Our "destruction tour" starts on main, because we need to deregister.
if (!NS_IsMainThread()) {
GetMainThreadEventTarget()->Dispatch(NewNonOwningRunnableMethod(
__func__, this, &MediaTransportHandlerSTS::Destroy));
if (!mInitPromise) {
return;
}
MOZ_ASSERT(NS_IsMainThread());
if (!GetShutdownHandler()) {
// Already shut down. Nothing else to do.
delete this;
return;
}
mInitPromise->Then(
mStsThread, __func__,
[this, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
disconnect_all();
// Clear the transports before destroying the ice ctx so that
// the close_notify alerts have a chance to be sent as the
// TransportFlow destructors execute.
mTransports.clear();
if (mIceCtx) {
// We're already on the STS thread, but the TransportFlow
// destructors executed when mTransports.clear() is called
// above dispatch calls to DestroyFinal to the STS thread. If
// we don't also dispatch the call to destory the NrIceCtx to
// the STS thread, it will tear down the NrIceMediaStreams
// before the TransportFlows are destroyed. Without a valid
// NrIceMediaStreams the close_notify alert cannot be sent.
mStsThread->Dispatch(NS_NewRunnableFunction(
__func__, [iceCtx = RefPtr<NrIceCtx>(mIceCtx)] {
NrIceStats stats = iceCtx->Destroy();
CSFLogDebug(LOGTAG,
"Ice Telemetry: stun (retransmits: %d)"
" turn (401s: %d 403s: %d 438s: %d)",
stats.stun_retransmits, stats.turn_401s,
stats.turn_403s, stats.turn_438s);
}));
GetShutdownHandler()->Deregister(this);
// mIceCtx still has a reference to us via sigslot! We must dispach to STS,
// and clean up there. However, by the time _that_ happens, we may have
// dispatched a signal callback to mCallbackThread, so we have to dispatch
// the final destruction to mCallbackThread.
mStsThread->Dispatch(NewNonOwningRunnableMethod(
__func__, this, &MediaTransportHandlerSTS::Destroy_s));
mIceCtx = nullptr;
}
},
[](const std::string& aError) {});
}
void MediaTransportHandlerSTS::Destroy_s() {
Shutdown_s();
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
nsresult rv = mCallbackThread->Dispatch(NewNonOwningRunnableMethod(
__func__, this, &MediaTransportHandlerSTS::DestroyFinal));
if (NS_SUCCEEDED(rv)) {
return;
}
}
DestroyFinal();
}
void MediaTransportHandlerSTS::DestroyFinal() { delete this; }
void MediaTransportHandlerSTS::SetProxyConfig(
NrSocketProxyConfig&& aProxyConfig) {
mInitPromise->Then(
mStsThread, __func__,
[this, self = RefPtr<MediaTransportHandlerSTS>(this),
aProxyConfig = std::move(aProxyConfig)]() mutable {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
mIceCtx->SetProxyConfig(std::move(aProxyConfig));
},
[](const std::string& aError) {});
@ -643,10 +553,6 @@ void MediaTransportHandlerSTS::EnsureProvisionalTransport(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aTransportId));
if (!stream) {
CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%s components=%u",
@ -687,10 +593,6 @@ void MediaTransportHandlerSTS::ActivateTransport(
mStsThread, __func__,
[=, keyDer = aKeyDer.Clone(), certDer = aCertDer.Clone(),
self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
MOZ_ASSERT(aComponentCount);
RefPtr<DtlsIdentity> dtlsIdentity(
DtlsIdentity::Deserialize(keyDer, certDer, aAuthType));
@ -771,10 +673,6 @@ void MediaTransportHandlerSTS::SetTargetForDefaultLocalAddressLookup(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
mIceCtx->SetTargetForDefaultLocalAddressLookup(aTargetIp, aTargetPort);
},
[](const std::string& aError) {});
@ -787,10 +685,6 @@ void MediaTransportHandlerSTS::StartIceGathering(
mStsThread, __func__,
[=, stunAddrs = aStunAddrs.Clone(),
self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
mObfuscateHostAddresses = aObfuscateHostAddresses;
// Belt and suspenders - in e10s mode, the call below to SetStunAddrs
@ -828,10 +722,6 @@ void MediaTransportHandlerSTS::StartIceChecks(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
nsresult rv = mIceCtx->ParseGlobalAttributes(aIceOptions);
if (NS_FAILED(rv)) {
CSFLogError(LOGTAG, "%s: couldn't parse global parameters",
@ -873,10 +763,6 @@ void MediaTransportHandlerSTS::AddIceCandidate(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
std::vector<std::string> tokens;
TokenizeCandidate(aCandidate, tokens);
@ -912,10 +798,6 @@ void MediaTransportHandlerSTS::UpdateNetworkState(bool aOnline) {
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
mIceCtx->UpdateNetworkState(aOnline);
},
[](const std::string& aError) {});
@ -926,10 +808,6 @@ void MediaTransportHandlerSTS::RemoveTransportsExcept(
mInitPromise->Then(
mStsThread, __func__,
[=, self = RefPtr<MediaTransportHandlerSTS>(this)]() {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
for (auto it = mTransports.begin(); it != mTransports.end();) {
const std::string transportId(it->first);
if (!aTransportIds.count(transportId)) {
@ -967,10 +845,6 @@ void MediaTransportHandlerSTS::SendPacket(const std::string& aTransportId,
mStsThread, __func__,
[this, self = RefPtr<MediaTransportHandlerSTS>(this), aTransportId,
aPacket = std::move(aPacket)]() mutable {
if (!mIceCtx) {
return; // Probably due to XPCOM shutdown
}
MOZ_ASSERT(aPacket.type() != MediaPacket::UNCLASSIFIED);
RefPtr<TransportFlow> flow =
GetTransportFlow(aTransportId, aPacket.type() == MediaPacket::RTCP);
@ -1033,11 +907,10 @@ TransportLayer::State MediaTransportHandler::GetState(
void MediaTransportHandler::OnCandidate(const std::string& aTransportId,
const CandidateInfo& aCandidateInfo) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnCandidate, aTransportId,
aCandidateInfo),
NS_DISPATCH_NORMAL);
mCallbackThread->Dispatch(WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnCandidate,
aTransportId, aCandidateInfo),
NS_DISPATCH_NORMAL);
return;
}
@ -1047,8 +920,8 @@ void MediaTransportHandler::OnCandidate(const std::string& aTransportId,
void MediaTransportHandler::OnAlpnNegotiated(const std::string& aAlpn) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnAlpnNegotiated, aAlpn),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnAlpnNegotiated, aAlpn),
NS_DISPATCH_NORMAL);
return;
}
@ -1061,9 +934,8 @@ void MediaTransportHandler::OnGatheringStateChange(
dom::RTCIceGatheringState aState) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnGatheringStateChange,
aState),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnGatheringStateChange, aState),
NS_DISPATCH_NORMAL);
return;
}
@ -1075,9 +947,8 @@ void MediaTransportHandler::OnConnectionStateChange(
dom::RTCIceConnectionState aState) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnConnectionStateChange,
aState),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnConnectionStateChange, aState),
NS_DISPATCH_NORMAL);
return;
}
@ -1089,9 +960,9 @@ void MediaTransportHandler::OnPacketReceived(const std::string& aTransportId,
const MediaPacket& aPacket) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnPacketReceived,
aTransportId, const_cast<MediaPacket&>(aPacket)),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnPacketReceived, aTransportId,
const_cast<MediaPacket&>(aPacket)),
NS_DISPATCH_NORMAL);
return;
}
@ -1103,9 +974,9 @@ void MediaTransportHandler::OnEncryptedSending(const std::string& aTransportId,
const MediaPacket& aPacket) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnEncryptedSending,
aTransportId, const_cast<MediaPacket&>(aPacket)),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnEncryptedSending, aTransportId,
const_cast<MediaPacket&>(aPacket)),
NS_DISPATCH_NORMAL);
return;
}
@ -1117,8 +988,8 @@ void MediaTransportHandler::OnStateChange(const std::string& aTransportId,
TransportLayer::State aState) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnStateChange, aTransportId,
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnStateChange, aTransportId,
aState),
NS_DISPATCH_NORMAL);
return;
@ -1136,9 +1007,9 @@ void MediaTransportHandler::OnRtcpStateChange(const std::string& aTransportId,
TransportLayer::State aState) {
if (mCallbackThread && !mCallbackThread->IsOnCurrentThread()) {
mCallbackThread->Dispatch(
// This is being called from sigslot, which does not hold a strong ref.
WrapRunnable(this, &MediaTransportHandler::OnRtcpStateChange,
aTransportId, aState),
WrapRunnable(RefPtr<MediaTransportHandler>(this),
&MediaTransportHandler::OnRtcpStateChange, aTransportId,
aState),
NS_DISPATCH_NORMAL);
return;
}

View File

@ -63,6 +63,8 @@ class MediaTransportHandler {
virtual void EnterPrivateMode() = 0;
virtual void ExitPrivateMode() = 0;
virtual void Destroy() = 0;
virtual nsresult CreateIceCtx(const std::string& aName,
const nsTArray<dom::RTCIceServer>& aIceServers,
dom::RTCIceTransportPolicy aIcePolicy) = 0;
@ -127,9 +129,7 @@ class MediaTransportHandler {
sigslot::signal2<const std::string&, TransportLayer::State> SignalStateChange;
sigslot::signal2<const std::string&, TransportLayer::State>
SignalRtcpStateChange;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DESTROY(MediaTransportHandler,
Destroy())
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaTransportHandler)
TransportLayer::State GetState(const std::string& aTransportId,
bool aRtcp) const;
@ -148,7 +148,6 @@ class MediaTransportHandler {
TransportLayer::State aState);
void OnRtcpStateChange(const std::string& aTransportId,
TransportLayer::State aState);
virtual void Destroy() = 0;
virtual ~MediaTransportHandler() = default;
std::map<std::string, TransportLayer::State> mStateCache;
std::map<std::string, TransportLayer::State> mRtcpStateCache;

View File

@ -152,11 +152,15 @@ nsresult MediaTransportHandlerIPC::CreateIceCtx(
}
void MediaTransportHandlerIPC::Destroy() {
if (mChild) {
MediaTransportChild::Send__delete__(mChild);
mChild = nullptr;
}
delete this;
mInitPromise->Then(
mCallbackThread, __func__,
[=, self = RefPtr<MediaTransportHandlerIPC>(this)](bool /*dummy*/) {
if (mChild) {
MediaTransportChild::Send__delete__(mChild);
mChild = nullptr;
}
},
[](const nsCString& aError) {});
}
// We will probably be able to move the proxy lookup stuff into

View File

@ -25,6 +25,7 @@ class MediaTransportHandlerIPC : public MediaTransportHandler {
nsresult CreateIceCtx(const std::string& aName,
const nsTArray<dom::RTCIceServer>& aIceServers,
dom::RTCIceTransportPolicy aIcePolicy) override;
void Destroy() override;
// We will probably be able to move the proxy lookup stuff into
// this class once we move mtransport to its own process.
@ -75,7 +76,6 @@ class MediaTransportHandlerIPC : public MediaTransportHandler {
private:
friend class MediaTransportChild;
void Destroy() override;
// We do not own this; it will tell us when it is going away.
dom::PMediaTransportChild* mChild = nullptr;

View File

@ -37,6 +37,7 @@ class MediaTransportParent::Impl : public sigslot::has_slots<> {
virtual ~Impl() {
disconnect_all();
mHandler->Destroy();
mHandler = nullptr;
}

View File

@ -117,7 +117,7 @@ StaticRefPtr<PeerConnectionCtxObserver>
PeerConnectionCtx::gPeerConnectionCtxObserver;
const std::map<const std::string, PeerConnectionImpl*>&
PeerConnectionCtx::GetPeerConnections() {
PeerConnectionCtx::mGetPeerConnections() {
return mPeerConnections;
}
@ -354,7 +354,7 @@ nsresult PeerConnectionCtx::Cleanup() {
mQueuedJSEPOperations.Clear();
mGMPService = nullptr;
mTransportHandler = nullptr;
mTransportHandler->Destroy();
return NS_OK;
}

View File

@ -65,7 +65,7 @@ class PeerConnectionCtx {
mozilla::dom::Sequence<mozilla::dom::RTCStatsReportInternal>
mStatsForClosedPeerConnections;
const std::map<const std::string, PeerConnectionImpl*>& GetPeerConnections();
const std::map<const std::string, PeerConnectionImpl*>& mGetPeerConnections();
private:
// We could make these available only via accessors but it's too much trouble.

View File

@ -429,18 +429,29 @@ nsresult PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver,
CopyUTF16toUTF8(locationAStr, locationCStr);
if (!mUuidGen->Generate(&mHandle)) {
MOZ_CRASH();
return NS_ERROR_UNEXPECTED;
}
SprintfLiteral(temp, "%s %" PRIu64 " (id=%" PRIu64 " url=%s)",
mHandle.c_str(), static_cast<uint64_t>(timestamp),
SprintfLiteral(temp, "%" PRIu64 " (id=%" PRIu64 " url=%s)",
static_cast<uint64_t>(timestamp),
static_cast<uint64_t>(mWindow ? mWindow->WindowID() : 0),
locationCStr.get() ? locationCStr.get() : "NULL");
mName = temp;
// Generate a random handle
unsigned char handle_bin[8];
SECStatus rv;
rv = PK11_GenerateRandom(handle_bin, sizeof(handle_bin));
if (rv != SECSuccess) {
MOZ_CRASH();
return NS_ERROR_UNEXPECTED;
}
char hex[17];
SprintfLiteral(hex, "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", handle_bin[0],
handle_bin[1], handle_bin[2], handle_bin[3], handle_bin[4],
handle_bin[5], handle_bin[6], handle_bin[7]);
mHandle = hex;
STAMP_TIMECARD(mTimeCard, "Initializing PC Ctx");
res = PeerConnectionCtx::InitializeGlobal(mThread, mSTSThread);
NS_ENSURE_SUCCESS(res, res);

View File

@ -628,9 +628,6 @@ void PeerConnectionMedia::SelfDestruct_m() {
ASSERT_ON_THREAD(mMainThread);
mTransportHandler->RemoveTransportsExcept(std::set<std::string>());
mTransportHandler = nullptr;
mMainThread = nullptr;
// Final self-destruct.
@ -644,6 +641,9 @@ void PeerConnectionMedia::ShutdownMediaTransport_s() {
disconnect_all();
mTransportHandler->Destroy();
mTransportHandler = nullptr;
// we're holding a ref to 'this' that's released by SelfDestruct_m
mMainThread->Dispatch(
WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),

View File

@ -38,9 +38,6 @@ class RTCStatsTimestampMaker {
bool mCrossOriginIsolated = false;
};
// TODO(bug 1588303): If we ever get move semantics for webidl dictionaries, we
// can stop wrapping these in UniquePtr, which will allow us to simplify code
// in several places.
typedef MozPromise<UniquePtr<RTCStatsCollection>, nsresult, true>
RTCStatsPromise;

View File

@ -89,16 +89,10 @@ TransceiverImpl::TransceiverImpl(
mTransmitPipeline->SetTrack(mSendTrack);
auto self = nsMainThreadPtrHandle<TransceiverImpl>(
new nsMainThreadPtrHolder<TransceiverImpl>(
"TransceiverImpl::TransceiverImpl::self", this, false));
mStsThread->Dispatch(
NS_NewRunnableFunction("TransceiverImpl::TransceiverImpl", [self] {
self->mTransportHandler->SignalStateChange.connect(
self.get(), &TransceiverImpl::UpdateDtlsTransportState);
self->mTransportHandler->SignalRtcpStateChange.connect(
self.get(), &TransceiverImpl::UpdateDtlsTransportState);
}));
mTransportHandler->SignalStateChange.connect(
this, &TransceiverImpl::UpdateDtlsTransportState);
mTransportHandler->SignalRtcpStateChange.connect(
this, &TransceiverImpl::UpdateDtlsTransportState);
}
TransceiverImpl::~TransceiverImpl() = default;
@ -174,13 +168,7 @@ void TransceiverImpl::Shutdown_m() {
}
Stop();
mTransmitPipeline = nullptr;
auto self = nsMainThreadPtrHandle<TransceiverImpl>(
new nsMainThreadPtrHolder<TransceiverImpl>(
"TransceiverImpl::Shutdown_m::self", this, false));
mStsThread->Dispatch(NS_NewRunnableFunction(__func__, [self] {
self->disconnect_all();
self->mTransportHandler = nullptr;
}));
mTransportHandler = nullptr;
}
nsresult TransceiverImpl::UpdateSendTrack(dom::MediaStreamTrack* aSendTrack) {

View File

@ -18,15 +18,15 @@ class WebrtcGlobalChild : public PWebrtcGlobalChild {
MOZ_IMPLICIT WebrtcGlobalChild();
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual mozilla::ipc::IPCResult RecvGetStats(
const nsString& aPcIdFilter, GetStatsResolver&& aResolve) override;
virtual mozilla::ipc::IPCResult RecvClearStats() override;
virtual mozilla::ipc::IPCResult RecvGetStatsRequest(
const int& aRequestId, const nsString& aPcIdFilter) override;
virtual mozilla::ipc::IPCResult RecvClearStatsRequest() override;
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we can't do MOZ_CAN_RUN_SCRIPT in
// ipdl-generated things yet.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual mozilla::ipc::IPCResult RecvGetLog(
GetLogResolver&& aResolve) override;
virtual mozilla::ipc::IPCResult RecvClearLog() override;
virtual mozilla::ipc::IPCResult RecvGetLogRequest(
const int& aReqestId, const nsCString& aPattern) override;
virtual mozilla::ipc::IPCResult RecvClearLogRequest() override;
virtual mozilla::ipc::IPCResult RecvSetAecLogging(
const bool& aEnable) override;
virtual mozilla::ipc::IPCResult RecvSetDebugMode(const int& aLevel) override;

View File

@ -7,24 +7,28 @@
#include "WebrtcGlobalChild.h"
#include "WebrtcGlobalParent.h"
#include <deque>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <type_traits>
#include "common/browser_logging/CSFLog.h"
#include "common/browser_logging/WebRtcLog.h"
#include "mozilla/dom/WebrtcGlobalInformationBinding.h"
#include "mozilla/dom/RTCStatsReportBinding.h" // for RTCStatsReportInternal
#include "mozilla/dom/ContentChild.h"
#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
#include "nsServiceManagerUtils.h" // do_GetService
#include "mozilla/ErrorResult.h"
#include "nsProxyRelease.h" // nsMainThreadPtrHolder
#include "mozilla/Vector.h"
#include "nsProxyRelease.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ClearOnShutdown.h"
#include "transport/runnable_utils.h"
#include "MediaTransportHandler.h"
@ -40,11 +44,133 @@ static const char* wgiLogTag = "WebrtcGlobalInformation";
namespace mozilla {
namespace dom {
typedef nsTArray<RTCStatsReportInternal> Stats;
template <class Request, typename Callback, typename Result,
typename QueryParam>
class RequestManager {
public:
static Request* Create(Callback& aCallback, QueryParam& aParam) {
mozilla::StaticMutexAutoLock lock(sMutex);
int id = ++sLastRequestId;
auto result = sRequests.try_emplace(id, id, aCallback, aParam);
if (!result.second) {
return nullptr;
}
return &result.first->second;
}
static void Delete(int aId) {
mozilla::StaticMutexAutoLock lock(sMutex);
sRequests.erase(aId);
}
static Request* Get(int aId) {
mozilla::StaticMutexAutoLock lock(sMutex);
auto r = sRequests.find(aId);
if (r == sRequests.end()) {
return nullptr;
}
return &r->second;
}
Result mResult;
std::queue<RefPtr<WebrtcGlobalParent>> mContactList;
const int mRequestId;
RefPtr<WebrtcGlobalParent> GetNextParent() {
while (!mContactList.empty()) {
RefPtr<WebrtcGlobalParent> next = mContactList.front();
mContactList.pop();
if (next->IsActive()) {
return next;
}
}
return nullptr;
}
MOZ_CAN_RUN_SCRIPT
void Complete() {
IgnoredErrorResult rv;
using RealCallbackType = std::remove_pointer_t<decltype(mCallback.get())>;
RefPtr<RealCallbackType> callback(mCallback.get());
callback->Call(mResult, rv);
if (rv.Failed()) {
CSFLogError(LOGTAG, "Error firing stats observer callback");
}
}
protected:
// The mutex is used to protect two related operations involving the sRequest
// map and the sLastRequestId. For the map, it prevents more than one thread
// from adding or deleting map entries at the same time. For id generation, it
// creates an atomic allocation and increment.
static mozilla::StaticMutex sMutex;
static std::map<int, Request> sRequests;
static int sLastRequestId;
Callback mCallback;
explicit RequestManager(int aId, Callback& aCallback)
: mRequestId(aId), mCallback(aCallback) {}
~RequestManager() {}
private:
RequestManager() = delete;
RequestManager& operator=(const RequestManager&) = delete;
};
template <class Request, typename Callback, typename Result,
typename QueryParam>
mozilla::StaticMutex
RequestManager<Request, Callback, Result, QueryParam>::sMutex;
template <class Request, typename Callback, typename Result,
typename QueryParam>
std::map<int, Request>
RequestManager<Request, Callback, Result, QueryParam>::sRequests;
template <class Request, typename Callback, typename Result,
typename QueryParam>
int RequestManager<Request, Callback, Result, QueryParam>::sLastRequestId;
typedef nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback>
StatsRequestCallback;
class StatsRequest
: public RequestManager<StatsRequest, StatsRequestCallback,
WebrtcGlobalStatisticsReport, nsAString> {
public:
const nsString mPcIdFilter;
explicit StatsRequest(int aId, StatsRequestCallback& aCallback,
nsAString& aFilter)
: RequestManager(aId, aCallback), mPcIdFilter(aFilter) {}
private:
StatsRequest() = delete;
StatsRequest& operator=(const StatsRequest&) = delete;
};
typedef nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> LogRequestCallback;
class LogRequest : public RequestManager<LogRequest, LogRequestCallback,
Sequence<nsString>, const nsACString> {
public:
const nsCString mPattern;
explicit LogRequest(int aId, LogRequestCallback& aCallback,
const nsACString& aPattern)
: RequestManager(aId, aCallback), mPattern(aPattern) {}
private:
LogRequest() = delete;
LogRequest& operator=(const LogRequest&) = delete;
};
class WebrtcContentParents {
public:
static WebrtcGlobalParent* Alloc();
@ -88,69 +214,146 @@ static PeerConnectionCtx* GetPeerConnectionCtx() {
return nullptr;
}
static RefPtr<PWebrtcGlobalParent::GetStatsPromise>
GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) {
nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises;
MOZ_CAN_RUN_SCRIPT
static void OnStatsReport_m(
WebrtcGlobalChild* aThisChild, const int aRequestId,
nsTArray<UniquePtr<dom::RTCStatsReportInternal>>&& aReports) {
MOZ_ASSERT(NS_IsMainThread());
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (aThisChild) {
Stats stats;
// Copy stats generated for the currently active PeerConnections
for (auto& report : aReports) {
if (report) {
stats.AppendElement(*report);
}
}
// Reports saved for closed/destroyed PeerConnections
auto ctx = PeerConnectionCtx::GetInstance();
if (ctx) {
for (auto& pc : ctx->mStatsForClosedPeerConnections) {
stats.AppendElement(pc);
}
}
Unused << aThisChild->SendGetStatsResult(aRequestId, stats);
return;
}
// This is the last stats report to be collected. (Must be the gecko process).
MOZ_ASSERT(XRE_IsParentProcess());
StatsRequest* request = StatsRequest::Get(aRequestId);
if (!request) {
CSFLogError(LOGTAG, "Bad RequestId");
return;
}
for (auto& report : aReports) {
if (report) {
if (!request->mResult.mReports.AppendElement(*report, fallible)) {
mozalloc_handle_oom(0);
}
}
}
// Reports saved for closed/destroyed PeerConnections
auto ctx = PeerConnectionCtx::GetInstance();
if (ctx) {
// Grab stats for non-closed PCs
for (const auto& [id, pc] : ctx->GetPeerConnections()) {
if (aPcIdFilter.IsEmpty() || aPcIdFilter.EqualsASCII(id.c_str())) {
if (pc->HasMedia()) {
promises.AppendElement(pc->GetStats(nullptr, true));
}
}
}
// Grab stats for closed PCs
for (const auto& report : ctx->mStatsForClosedPeerConnections) {
if (aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) {
promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve(
MakeUnique<dom::RTCStatsReportInternal>(report), __func__));
for (auto&& pc : ctx->mStatsForClosedPeerConnections) {
if (!request->mResult.mReports.AppendElement(pc, fallible)) {
// XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
// involve multiple reallocations) and potentially crashing here,
// SetCapacity could be called outside the loop once.
mozalloc_handle_oom(0);
}
}
}
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());
request->Complete();
StatsRequest::Delete(aRequestId);
}
MOZ_CAN_RUN_SCRIPT
static void OnGetLogging_m(WebrtcGlobalChild* aThisChild, const int aRequestId,
Sequence<nsString>&& aLogList) {
MOZ_ASSERT(NS_IsMainThread());
if (!aLogList.IsEmpty()) {
if (!aLogList.AppendElement(u"+++++++ END ++++++++"_ns, fallible)) {
mozalloc_handle_oom(0);
}
}
if (aThisChild) {
// Add this log to the collection of logs and call into
// the next content process.
Unused << aThisChild->SendGetLogResult(aRequestId, aLogList);
return;
}
// This is the last log to be collected. (Must be the gecko process).
MOZ_ASSERT(XRE_IsParentProcess());
LogRequest* request = LogRequest::Get(aRequestId);
if (!request) {
CSFLogError(LOGTAG, "Bad RequestId");
return;
}
if (!request->mResult.AppendElements(std::move(aLogList), fallible)) {
mozalloc_handle_oom(0);
}
request->Complete();
LogRequest::Delete(aRequestId);
}
static void RunStatsQuery(
const std::map<const std::string, PeerConnectionImpl*>& aPeerConnections,
const nsAString& aPcIdFilter, WebrtcGlobalChild* aThisChild,
const int aRequestId) {
nsTArray<RefPtr<RTCStatsReportPromise>> promises;
for (auto& idAndPc : aPeerConnections) {
MOZ_ASSERT(idAndPc.second);
PeerConnectionImpl& pc = *idAndPc.second;
if (aPcIdFilter.IsEmpty() ||
aPcIdFilter.EqualsASCII(pc.GetIdAsAscii().c_str())) {
if (pc.HasMedia()) {
promises.AppendElement(
pc.GetStats(nullptr, true)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[=](UniquePtr<dom::RTCStatsReportInternal>&& aReport) {
return RTCStatsReportPromise::CreateAndResolve(
std::move(aReport), __func__);
},
[=](nsresult aError) {
// Ignore errors! Just resolve with a nullptr.
return RTCStatsReportPromise::CreateAndResolve(
UniquePtr<dom::RTCStatsReportInternal>(), __func__);
}));
}
}
return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve(
std::move(reports), __func__);
};
return dom::RTCStatsReportPromise::AllSettled(
GetMainThreadSerialEventTarget(), promises)
->Then(GetMainThreadSerialEventTarget(), __func__,
std::move(UnwrapUniquePtrs));
}
static nsTArray<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() {
static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash;
if (!sStash) {
sStash = new nsTArray<dom::RTCStatsReportInternal>();
ClearOnShutdown(&sStash);
}
return *sStash;
RTCStatsReportPromise::All(GetMainThreadSerialEventTarget(), promises)
->Then(
GetMainThreadSerialEventTarget(), __func__,
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we're going to run that
// function async anyway.
[aThisChild, aRequestId](
nsTArray<UniquePtr<dom::RTCStatsReportInternal>>&& aReports)
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
OnStatsReport_m(aThisChild, aRequestId, std::move(aReports));
},
[=](nsresult) { MOZ_CRASH(); });
}
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 ClearClosedStats() {
GetWebrtcGlobalStatsStash().Clear();
void ClearClosedStats() {
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (ctx) {
@ -168,8 +371,8 @@ void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) {
if (!WebrtcContentParents::Empty()) {
// Pass on the request to any content process based PeerConnections.
for (const auto& cp : WebrtcContentParents::GetAll()) {
Unused << cp->SendClearStats();
for (auto& cp : WebrtcContentParents::GetAll()) {
Unused << cp->SendClearStatsRequest();
}
}
@ -187,145 +390,98 @@ void WebrtcGlobalInformation::GetAllStats(
MOZ_ASSERT(XRE_IsParentProcess());
nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>> statsPromises;
nsString filter;
if (pcIdFilter.WasPassed()) {
filter = pcIdFilter.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!");
for (auto& contentProcessResult : aResult.ResolveValue()) {
// TODO: Report rejection on individual content processes someday?
if (contentProcessResult.IsResolve()) {
for (auto& pcStats : contentProcessResult.ResolveValue()) {
pcids.insert(pcStats.mPcid);
if (!flattened.mReports.AppendElement(std::move(pcStats),
fallible)) {
mozalloc_handle_oom(0);
}
}
}
}
nsString filter;
if (pcIdFilter.WasPassed()) {
filter = pcIdFilter.Value();
}
if (filter.IsEmpty()) {
// Unfiltered is pretty simple; add stuff from stash that is
// missing, then stomp the stash with the new reports.
for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
if (!pcids.count(pcStats.mPcid)) {
// Stats from a closed PC or stopped content process.
// Content process may have gone away before we got to update
// this.
pcStats.mClosed = true;
if (!flattened.mReports.AppendElement(std::move(pcStats),
fallible)) {
mozalloc_handle_oom(0);
}
}
}
GetWebrtcGlobalStatsStash() = flattened.mReports;
} else {
// Filtered is slightly more complex
if (flattened.mReports.IsEmpty()) {
// Find entry from stash and add it to report
for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
if (pcStats.mPcid == filter) {
pcStats.mClosed = true;
if (!flattened.mReports.AppendElement(std::move(pcStats),
fallible)) {
mozalloc_handle_oom(0);
}
}
}
} else {
// Find entries in stash, remove them, and then add new entries
for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) {
auto& pcStats = GetWebrtcGlobalStatsStash()[i];
if (pcStats.mPcid == filter) {
GetWebrtcGlobalStatsStash().RemoveElementAt(i);
} else {
++i;
}
}
GetWebrtcGlobalStatsStash().AppendElements(flattened.mReports);
}
}
auto* request = StatsRequest::Create(callbackHandle, filter);
IgnoredErrorResult rv;
callbackHandle->Call(flattened, rv);
};
if (!request) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
PWebrtcGlobalParent::GetStatsPromise::AllSettled(
GetMainThreadSerialEventTarget(), statsPromises)
->Then(GetMainThreadSerialEventTarget(), __func__,
std::move(FlattenThenStashThenCallback));
if (!WebrtcContentParents::Empty()) {
// Pass on the request to any content based PeerConnections.
for (auto& cp : WebrtcContentParents::GetAll()) {
request->mContactList.push(cp);
}
auto next = request->GetNextParent();
if (next) {
aRv = next->SendGetStatsRequest(request->mRequestId, request->mPcIdFilter)
? NS_OK
: NS_ERROR_FAILURE;
return;
}
}
// No content resident PeerConnectionCtx instances.
// Check this process.
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (ctx) {
RunStatsQuery(ctx->mGetPeerConnections(), filter, nullptr,
request->mRequestId);
} else {
// Just send back an empty report.
request->Complete();
StatsRequest::Delete(request->mRequestId);
}
aRv = NS_OK;
}
static RefPtr<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() {
MOZ_CAN_RUN_SCRIPT
static nsresult RunLogQuery(const nsCString& aPattern,
WebrtcGlobalChild* aThisChild,
const int aRequestId) {
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (!ctx) {
// This process has never created a PeerConnection, so no ICE logging.
return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
Sequence<nsString>(), __func__);
OnGetLogging_m(aThisChild, aRequestId, Sequence<nsString>());
return NS_OK;
}
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__);
if (NS_FAILED(rv)) {
return rv;
}
if (!stsThread) {
return NS_ERROR_FAILURE;
}
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__);
};
InvokeAsync(stsThread, __func__,
[transportHandler, aPattern]() {
return transportHandler->GetIceLog(aPattern);
})
->Then(
GetMainThreadSerialEventTarget(), __func__,
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we're going to run that
// function async anyway.
[aRequestId, aThisChild](Sequence<nsString>&& aLogLines)
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
OnGetLogging_m(aThisChild, aRequestId, std::move(aLogLines));
},
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we're going to run that
// function async anyway.
[aRequestId, aThisChild](nsresult aError)
MOZ_CAN_RUN_SCRIPT_BOUNDARY {
OnGetLogging_m(aThisChild, aRequestId, Sequence<nsString>());
});
return transportHandler->GetIceLog(nsCString())
->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers));
return NS_OK;
}
static nsresult RunLogClear() {
@ -361,12 +517,11 @@ void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) {
// 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();
for (auto& cp : WebrtcContentParents::GetAll()) {
Unused << cp->SendClearLogRequest();
}
}
@ -374,44 +529,6 @@ void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) {
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) {
@ -422,36 +539,44 @@ void WebrtcGlobalInformation::GetLogging(
MOZ_ASSERT(XRE_IsParentProcess());
nsAutoCString pattern;
CopyUTF16toUTF8(aPattern, pattern);
// 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);
};
nsAutoCString pattern;
CopyUTF16toUTF8(aPattern, pattern);
UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__,
std::move(FilterThenCallback));
aRv = NS_OK;
LogRequest* request = LogRequest::Create(callbackHandle, pattern);
if (!request) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
if (!WebrtcContentParents::Empty()) {
// Pass on the request to any content based PeerConnections.
for (auto& cp : WebrtcContentParents::GetAll()) {
request->mContactList.push(cp);
}
auto next = request->GetNextParent();
if (next) {
aRv = next->SendGetLogRequest(request->mRequestId, request->mPattern)
? NS_OK
: NS_ERROR_FAILURE;
return;
}
}
nsresult rv = RunLogQuery(request->mPattern, nullptr, request->mRequestId);
if (NS_FAILED(rv)) {
LogRequest::Delete(request->mRequestId);
}
aRv = rv;
}
static int32_t sLastSetLevel = 0;
@ -467,7 +592,7 @@ void WebrtcGlobalInformation::SetDebugLevel(const GlobalObject& aGlobal,
}
sLastSetLevel = aLevel;
for (const auto& cp : WebrtcContentParents::GetAll()) {
for (auto& cp : WebrtcContentParents::GetAll()) {
Unused << cp->SendSetDebugMode(aLevel);
}
}
@ -486,7 +611,7 @@ void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal,
sLastAECDebug = aEnable;
for (const auto& cp : WebrtcContentParents::GetAll()) {
for (auto& cp : WebrtcContentParents::GetAll()) {
Unused << cp->SendSetAecLogging(aEnable);
}
}
@ -500,6 +625,85 @@ void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal,
aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns));
}
mozilla::ipc::IPCResult WebrtcGlobalParent::RecvGetStatsResult(
const int& aRequestId, nsTArray<RTCStatsReportInternal>&& Stats) {
MOZ_ASSERT(NS_IsMainThread());
StatsRequest* request = StatsRequest::Get(aRequestId);
if (!request) {
CSFLogError(LOGTAG, "Bad RequestId");
return IPC_FAIL_NO_REASON(this);
}
for (auto& s : Stats) {
if (!request->mResult.mReports.AppendElement(s, fallible)) {
CSFLogError(LOGTAG, "Out of memory");
return IPC_FAIL_NO_REASON(this);
}
}
auto next = request->GetNextParent();
if (next) {
// There are more content instances to query.
if (!next->SendGetStatsRequest(request->mRequestId, request->mPcIdFilter)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
// Content queries complete, run chrome instance query if applicable
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (ctx) {
RunStatsQuery(ctx->mGetPeerConnections(), request->mPcIdFilter, nullptr,
aRequestId);
} else {
// No instance in the process, return the collections as is
request->Complete();
StatsRequest::Delete(aRequestId);
}
return IPC_OK();
}
mozilla::ipc::IPCResult WebrtcGlobalParent::RecvGetLogResult(
const int& aRequestId, const WebrtcGlobalLog& aLog) {
MOZ_ASSERT(NS_IsMainThread());
LogRequest* request = LogRequest::Get(aRequestId);
if (!request) {
CSFLogError(LOGTAG, "Bad RequestId");
return IPC_FAIL_NO_REASON(this);
}
if (!request->mResult.AppendElements(aLog, fallible)) {
CSFLogError(LOGTAG, "Out of memory");
return IPC_FAIL_NO_REASON(this);
}
auto next = request->GetNextParent();
if (next) {
// There are more content instances to query.
if (!next->SendGetLogRequest(request->mRequestId, request->mPattern)) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
// Content queries complete, run chrome instance query if applicable
nsresult rv = RunLogQuery(request->mPattern, nullptr, aRequestId);
if (NS_FAILED(rv)) {
// Unable to get gecko process log. Return what has been collected.
CSFLogError(LOGTAG, "Unable to extract chrome process log");
request->Complete();
LogRequest::Delete(aRequestId);
}
return IPC_OK();
}
WebrtcGlobalParent* WebrtcGlobalParent::Alloc() {
return WebrtcContentParents::Alloc();
}
@ -525,25 +729,26 @@ MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() {
MOZ_COUNT_DTOR(WebrtcGlobalParent);
}
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats(
const nsString& aPcIdFilter, GetStatsResolver&& aResolve) {
if (!mShutdown) {
GetStatsPromiseForThisProcess(aPcIdFilter)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[resolve = std::move(aResolve)](
nsTArray<dom::RTCStatsReportInternal>&& aReports) {
resolve(std::move(aReports));
},
[]() { MOZ_CRASH(); });
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStatsRequest(
const int& aRequestId, const nsString& aPcIdFilter) {
if (mShutdown) {
return IPC_OK();
}
aResolve(nsTArray<RTCStatsReportInternal>());
PeerConnectionCtx* ctx = GetPeerConnectionCtx();
if (ctx) {
RunStatsQuery(ctx->mGetPeerConnections(), aPcIdFilter, this, aRequestId);
return IPC_OK();
}
nsTArray<RTCStatsReportInternal> empty_stats;
SendGetStatsResult(aRequestId, empty_stats);
return IPC_OK();
}
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() {
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStatsRequest() {
if (mShutdown) {
return IPC_OK();
}
@ -552,28 +757,23 @@ mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() {
return IPC_OK();
}
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog(
GetLogResolver&& aResolve) {
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLogRequest(
const int& aRequestId, const nsCString& aPattern) {
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>());
}
});
nsresult rv = RunLogQuery(aPattern, this, aRequestId);
if (NS_FAILED(rv)) {
Sequence<nsString> empty_log;
SendGetLogResult(aRequestId, empty_log);
}
return IPC_OK();
}
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() {
mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLogRequest() {
if (mShutdown) {
return IPC_OK();
}

View File

@ -6,6 +6,7 @@
#define _WEBRTC_GLOBAL_PARENT_H_
#include "mozilla/dom/PWebrtcGlobalParent.h"
#include "mozilla/dom/RTCStatsReportBinding.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsISupportsImpl.h"
@ -26,6 +27,18 @@ class WebrtcGlobalParent : public PWebrtcGlobalParent {
static WebrtcGlobalParent* Alloc();
static bool Dealloc(WebrtcGlobalParent* aActor);
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we can't do MOZ_CAN_RUN_SCRIPT in
// ipdl-generated things yet.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual mozilla::ipc::IPCResult RecvGetStatsResult(
const int& aRequestId,
nsTArray<RTCStatsReportInternal>&& aStats) override;
// MOZ_CAN_RUN_SCRIPT_BOUNDARY because we can't do MOZ_CAN_RUN_SCRIPT in
// ipdl-generated things yet.
MOZ_CAN_RUN_SCRIPT_BOUNDARY
virtual mozilla::ipc::IPCResult RecvGetLogResult(
const int& aRequestId, const WebrtcGlobalLog& aLog) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
virtual mozilla::ipc::IPCResult Recv__delete__() override;

View File

@ -256,36 +256,6 @@ TEST(MozPromise, PromiseAllResolve)
});
}
TEST(MozPromise, PromiseAllResolveAsync)
{
AutoTaskQueue atq;
RefPtr<TaskQueue> queue = atq.Queue();
RunOnTaskQueue(queue, [queue]() -> void {
nsTArray<RefPtr<TestPromise>> promises;
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(22, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(32, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(42, __func__);
}));
TestPromise::All(queue, promises)
->Then(
queue, __func__,
[queue](const CopyableTArray<int>& aResolveValues) -> void {
EXPECT_EQ(aResolveValues.Length(), 3UL);
EXPECT_EQ(aResolveValues[0], 22);
EXPECT_EQ(aResolveValues[1], 32);
EXPECT_EQ(aResolveValues[2], 42);
queue->BeginShutdown();
},
[]() { EXPECT_TRUE(false); });
});
}
TEST(MozPromise, PromiseAllReject)
{
AutoTaskQueue atq;
@ -308,107 +278,6 @@ TEST(MozPromise, PromiseAllReject)
});
}
TEST(MozPromise, PromiseAllRejectAsync)
{
AutoTaskQueue atq;
RefPtr<TaskQueue> queue = atq.Queue();
RunOnTaskQueue(queue, [queue]() -> void {
nsTArray<RefPtr<TestPromise>> promises;
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(22, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndReject(32.0, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(42, __func__);
}));
// Ensure that more than one rejection doesn't cause a crash (bug #1207312)
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndReject(52.0, __func__);
}));
TestPromise::All(queue, promises)
->Then(
queue, __func__, []() { EXPECT_TRUE(false); },
[queue](float aRejectValue) -> void {
EXPECT_EQ(aRejectValue, 32.0);
queue->BeginShutdown();
});
});
}
TEST(MozPromise, PromiseAllSettled)
{
AutoTaskQueue atq;
RefPtr<TaskQueue> queue = atq.Queue();
RunOnTaskQueue(queue, [queue]() -> void {
nsTArray<RefPtr<TestPromise>> promises;
promises.AppendElement(TestPromise::CreateAndResolve(22, __func__));
promises.AppendElement(TestPromise::CreateAndReject(32.0, __func__));
promises.AppendElement(TestPromise::CreateAndResolve(42, __func__));
promises.AppendElement(TestPromise::CreateAndReject(52.0, __func__));
TestPromise::AllSettled(queue, promises)
->Then(
queue, __func__,
[queue](const TestPromise::AllSettledPromiseType::ResolveValueType&
aResolveValues) -> void {
EXPECT_EQ(aResolveValues.Length(), 4UL);
EXPECT_TRUE(aResolveValues[0].IsResolve());
EXPECT_EQ(aResolveValues[0].ResolveValue(), 22);
EXPECT_FALSE(aResolveValues[1].IsResolve());
EXPECT_EQ(aResolveValues[1].RejectValue(), 32.0);
EXPECT_TRUE(aResolveValues[2].IsResolve());
EXPECT_EQ(aResolveValues[2].ResolveValue(), 42);
EXPECT_FALSE(aResolveValues[3].IsResolve());
EXPECT_EQ(aResolveValues[3].RejectValue(), 52.0);
queue->BeginShutdown();
},
[]() { EXPECT_TRUE(false); });
});
}
TEST(MozPromise, PromiseAllSettledAsync)
{
AutoTaskQueue atq;
RefPtr<TaskQueue> queue = atq.Queue();
RunOnTaskQueue(queue, [queue]() -> void {
nsTArray<RefPtr<TestPromise>> promises;
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(22, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndReject(32.0, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndResolve(42, __func__);
}));
promises.AppendElement(InvokeAsync(queue, __func__, []() {
return TestPromise::CreateAndReject(52.0, __func__);
}));
TestPromise::AllSettled(queue, promises)
->Then(
queue, __func__,
[queue](const TestPromise::AllSettledPromiseType::ResolveValueType&
aResolveValues) -> void {
EXPECT_EQ(aResolveValues.Length(), 4UL);
EXPECT_TRUE(aResolveValues[0].IsResolve());
EXPECT_EQ(aResolveValues[0].ResolveValue(), 22);
EXPECT_FALSE(aResolveValues[1].IsResolve());
EXPECT_EQ(aResolveValues[1].RejectValue(), 32.0);
EXPECT_TRUE(aResolveValues[2].IsResolve());
EXPECT_EQ(aResolveValues[2].ResolveValue(), 42);
EXPECT_FALSE(aResolveValues[3].IsResolve());
EXPECT_EQ(aResolveValues[3].RejectValue(), 52.0);
queue->BeginShutdown();
},
[]() { EXPECT_TRUE(false); });
});
}
// Test we don't hit the assertions in MozPromise when exercising promise
// chaining upon task queue shutdown.
TEST(MozPromise, Chaining)

View File

@ -292,9 +292,6 @@ class MozPromise : public MozPromiseBase {
IsExclusive>
AllPromiseType;
typedef MozPromise<CopyableTArray<ResolveOrRejectValue>, bool, IsExclusive>
AllSettledPromiseType;
private:
class AllPromiseHolder : public MozPromiseRefcountable {
public:
@ -344,50 +341,6 @@ class MozPromise : public MozPromiseBase {
size_t mOutstandingPromises;
};
// Trying to pass ResolveOrRejectValue by value fails static analysis checks,
// so we need to use either a const& or an rvalue reference, depending on
// whether IsExclusive is true or not.
typedef std::conditional_t<IsExclusive, ResolveOrRejectValue&&,
const ResolveOrRejectValue&>
ResolveOrRejectValueParam;
class AllSettledPromiseHolder : public MozPromiseRefcountable {
public:
explicit AllSettledPromiseHolder(size_t aDependentPromises)
: mPromise(new typename AllSettledPromiseType::Private(__func__)),
mOutstandingPromises(aDependentPromises) {
MOZ_ASSERT(aDependentPromises > 0);
mValues.SetLength(aDependentPromises);
}
void Settle(size_t aIndex, ResolveOrRejectValueParam aValue) {
if (!mPromise) {
// Already rejected.
return;
}
mValues[aIndex].emplace(MaybeMove(aValue));
if (--mOutstandingPromises == 0) {
nsTArray<ResolveOrRejectValue> values;
values.SetCapacity(mValues.Length());
for (auto&& value : mValues) {
values.AppendElement(std::move(value.ref()));
}
mPromise->Resolve(std::move(values), __func__);
mPromise = nullptr;
mValues.Clear();
}
}
AllSettledPromiseType* Promise() { return mPromise; }
private:
nsTArray<Maybe<ResolveOrRejectValue>> mValues;
RefPtr<typename AllSettledPromiseType::Private> mPromise;
size_t mOutstandingPromises;
};
public:
[[nodiscard]] static RefPtr<AllPromiseType> All(
nsISerialEventTarget* aProcessingTarget,
@ -412,26 +365,6 @@ class MozPromise : public MozPromiseBase {
return promise;
}
[[nodiscard]] static RefPtr<AllSettledPromiseType> AllSettled(
nsISerialEventTarget* aProcessingTarget,
nsTArray<RefPtr<MozPromise>>& aPromises) {
if (aPromises.Length() == 0) {
return AllSettledPromiseType::CreateAndResolve(
CopyableTArray<ResolveOrRejectValue>(), __func__);
}
RefPtr<AllSettledPromiseHolder> holder =
new AllSettledPromiseHolder(aPromises.Length());
RefPtr<AllSettledPromiseType> promise = holder->Promise();
for (size_t i = 0; i < aPromises.Length(); ++i) {
aPromises[i]->Then(aProcessingTarget, __func__,
[holder, i](ResolveOrRejectValueParam aValue) -> void {
holder->Settle(i, MaybeMove(aValue));
});
}
return promise;
}
class Request : public MozPromiseRefcountable {
public:
virtual void Disconnect() = 0;