Bug 1212237 - Use a content principal by default for received tracks. r=bwc

This swaps things around so that received tracks in peer connections have a
content principal instead of a null principal initially.

This puts an extra requirement on us to not output any frames under the content
principal after ALPN has been negotiated with requested privacy, but before this
private principal has been signaled to the MediaPipelines. The private principal
is signaled from the STS thread, via the main thread, whereas media is consumed
directly by MediaPipelines after being received on the STS thread.

This patch adds an extra signaling path directly from the STS thread that tells
MediaPipelines to make their PrincipalHandle private, and to ignore any data
until the private PrincipalHandle is set. It also moves the responsibility of
updating the principal of the received MediaStreamTracks from TransceiverImpl
to MediaPipeline, so it's all in the same path.

This lets all MediaStream and MediaStreamTrack APIs consume received tracks
directly after getting exposed to JS without errors. In case privacy is later
requested, consumers that have already been set up must handle this on the fly.
(They do, in specs and impls)

Differential Revision: https://phabricator.services.mozilla.com/D48947

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andreas Pehrson 2019-11-05 20:04:45 +00:00
Родитель 0b04e39974
Коммит 18c4e9ab29
11 изменённых файлов: 145 добавлений и 128 удалений

Просмотреть файл

@ -30,6 +30,7 @@
#include "VideoStreamTrack.h"
#include "VideoUtils.h"
#include "mozilla/Logging.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PeerIdentity.h"
#include "mozilla/Preferences.h"
#include "mozilla/SharedThreadPool.h"
@ -320,6 +321,8 @@ void MediaPipeline::UpdateTransport_s(const std::string& aTransportId,
this, &MediaPipeline::EncryptedPacketSending);
mTransportHandler->SignalPacketReceived.connect(
this, &MediaPipeline::PacketReceived);
mTransportHandler->SignalAlpnNegotiated.connect(
this, &MediaPipeline::AlpnNegotiated);
mSignalsConnected = true;
}
@ -638,6 +641,13 @@ void MediaPipeline::PacketReceived(const std::string& aTransportId,
}
}
void MediaPipeline::AlpnNegotiated(const std::string& aAlpn,
bool aPrivacyRequested) {
if (aPrivacyRequested) {
MakePrincipalPrivate_s();
}
}
void MediaPipeline::EncryptedPacketSending(const std::string& aTransportId,
MediaPacket& aPacket) {
if (mTransportId == aTransportId) {
@ -1163,8 +1173,10 @@ void MediaPipelineTransmit::PipelineListener::NewData(
class GenericReceiveListener : public MediaTrackListener {
public:
explicit GenericReceiveListener(dom::MediaStreamTrack* aTrack)
: mTrackSource(new nsMainThreadPtrHolder<RemoteTrackSource>(
explicit GenericReceiveListener(RefPtr<nsISerialEventTarget> aMainThread,
dom::MediaStreamTrack* aTrack)
: mMainThread(std::move(aMainThread)),
mTrackSource(new nsMainThreadPtrHolder<RemoteTrackSource>(
"GenericReceiveListener::mTrackSource",
&static_cast<RemoteTrackSource&>(aTrack->GetSource()))),
mSource(mTrackSource->mStream),
@ -1201,7 +1213,7 @@ class GenericReceiveListener : public MediaTrackListener {
void OnRtpReceived() {
if (mMaybeTrackNeedsUnmute) {
mMaybeTrackNeedsUnmute = false;
NS_DispatchToMainThread(
mMainThread->Dispatch(
NewRunnableMethod("GenericReceiveListener::OnRtpReceived_m", this,
&GenericReceiveListener::OnRtpReceived_m));
}
@ -1224,16 +1236,19 @@ class GenericReceiveListener : public MediaTrackListener {
mSource->Destroy();
}
NS_DispatchToMainThread(NewRunnableMethod("RemoteTrackSource::ForceEnded",
mTrackSource.get(),
&RemoteTrackSource::ForceEnded));
mMainThread->Dispatch(NewRunnableMethod("RemoteTrackSource::ForceEnded",
mTrackSource.get(),
&RemoteTrackSource::ForceEnded));
}
protected:
const RefPtr<nsISerialEventTarget> mMainThread;
const nsMainThreadPtrHandle<RemoteTrackSource> mTrackSource;
const RefPtr<SourceMediaTrack> mSource;
const bool mIsAudio;
// Main thread only.
bool mListening;
// Any thread.
Atomic<bool> mMaybeTrackNeedsUnmute;
};
@ -1250,10 +1265,11 @@ MediaPipelineReceive::~MediaPipelineReceive() {}
class MediaPipelineReceiveAudio::PipelineListener
: public GenericReceiveListener {
public:
PipelineListener(dom::MediaStreamTrack* aTrack,
PipelineListener(RefPtr<nsISerialEventTarget> aMainThread,
dom::MediaStreamTrack* aTrack,
const RefPtr<MediaSessionConduit>& aConduit,
const PrincipalHandle& aPrincipalHandle)
: GenericReceiveListener(aTrack),
: GenericReceiveListener(std::move(aMainThread), aTrack),
mConduit(aConduit),
// AudioSession conduit only supports 16, 32, 44.1 and 48kHz
// This is an artificial limitation, it would however require more
@ -1267,7 +1283,8 @@ class MediaPipelineReceiveAudio::PipelineListener
new TaskQueue(GetMediaThreadPool(MediaThreadType::WEBRTC_DECODER),
"AudioPipelineListener")),
mPlayedTicks(0),
mPrincipalHandle(aPrincipalHandle) {
mPrincipalHandle(aPrincipalHandle),
mForceSilence(false) {
mSource->SetAppendDataSourceRate(mRate);
mSource->AddListener(this);
}
@ -1278,31 +1295,41 @@ class MediaPipelineReceiveAudio::PipelineListener
NotifyPullImpl(aDesiredTime);
}
// Must be called on the main thread
void SetPrincipalHandle_m(const PrincipalHandle& aPrincipalHandle) {
class Message : public ControlMessage {
public:
Message(PipelineListener* aListener,
const PrincipalHandle& aPrincipalHandle)
: ControlMessage(nullptr),
mListener(aListener),
mPrincipalHandle(aPrincipalHandle) {}
void MakePrincipalPrivate_s() {
mForceSilence = true;
void Run() override {
mListener->SetPrincipalHandle_mtg(mPrincipalHandle);
}
mMainThread->Dispatch(NS_NewRunnableFunction(
"MediaPipelineReceiveAudio::PipelineListener::MakePrincipalPrivate_s",
[self = RefPtr<PipelineListener>(this), this] {
class Message : public ControlMessage {
public:
Message(RefPtr<PipelineListener> aListener,
const PrincipalHandle& aPrivatePrincipal)
: ControlMessage(nullptr),
mListener(std::move(aListener)),
mPrivatePrincipal(aPrivatePrincipal) {}
const RefPtr<PipelineListener> mListener;
const PrincipalHandle mPrincipalHandle;
};
void Run() override {
mListener->mPrincipalHandle = mPrivatePrincipal;
mListener->mForceSilence = false;
}
mSource->GraphImpl()->AppendMessage(
MakeUnique<Message>(this, aPrincipalHandle));
}
const RefPtr<PipelineListener> mListener;
PrincipalHandle mPrivatePrincipal;
};
// Must be called on the MediaTrackGraph thread
void SetPrincipalHandle_mtg(const PrincipalHandle& aPrincipalHandle) {
mPrincipalHandle = aPrincipalHandle;
RefPtr<nsIPrincipal> privatePrincipal =
NullPrincipal::CreateWithInheritedAttributes(
mTrackSource->GetPrincipal());
mTrackSource->SetPrincipal(privatePrincipal);
if (mSource->IsDestroyed()) {
return;
}
mSource->GraphImpl()->AppendMessage(
MakeUnique<Message>(this, MakePrincipalHandle(privatePrincipal)));
}));
}
private:
@ -1359,26 +1386,30 @@ class MediaPipelineReceiveAudio::PipelineListener
SharedBuffer::Create(samplesLength * sizeof(uint16_t));
int16_t* samplesData = static_cast<int16_t*>(samples->Data());
AudioSegment segment;
AutoTArray<int16_t*, 2> channels;
AutoTArray<const int16_t*, 2> outputChannels;
size_t frames = samplesLength / channelCount;
if (mForceSilence) {
segment.AppendNullData(frames);
} else {
AutoTArray<int16_t*, 2> channels;
AutoTArray<const int16_t*, 2> outputChannels;
channels.SetLength(channelCount);
channels.SetLength(channelCount);
size_t offset = 0;
for (size_t i = 0; i < channelCount; i++) {
channels[i] = samplesData + offset;
offset += frames;
size_t offset = 0;
for (size_t i = 0; i < channelCount; i++) {
channels[i] = samplesData + offset;
offset += frames;
}
DeinterleaveAndConvertBuffer(scratchBuffer, frames, channelCount,
channels.Elements());
outputChannels.AppendElements(channels);
segment.AppendFrames(samples.forget(), outputChannels, frames,
mPrincipalHandle);
}
DeinterleaveAndConvertBuffer(scratchBuffer, frames, channelCount,
channels.Elements());
outputChannels.AppendElements(channels);
segment.AppendFrames(samples.forget(), outputChannels, frames,
mPrincipalHandle);
// Handle track not actually added yet or removed/finished
if (TrackTime appended = mSource->AppendData(&segment)) {
mPlayedTicks += appended;
@ -1399,9 +1430,14 @@ class MediaPipelineReceiveAudio::PipelineListener
const TrackRate mRate;
const RefPtr<TaskQueue> mTaskQueue;
// Number of frames of data that has been added to the SourceMediaTrack in
// the graph's rate.
// the graph's rate. Graph thread only.
TrackTicks mPlayedTicks;
// Principal handle used when appending data to the SourceMediaTrack. Graph
// thread only.
PrincipalHandle mPrincipalHandle;
// Set to true on the sts thread if privacy is requested when ALPN was
// negotiated. Set to false again when mPrincipalHandle is private.
Atomic<bool> mForceSilence;
};
MediaPipelineReceiveAudio::MediaPipelineReceiveAudio(
@ -1412,9 +1448,9 @@ MediaPipelineReceiveAudio::MediaPipelineReceiveAudio(
const PrincipalHandle& aPrincipalHandle)
: MediaPipelineReceive(aPc, aTransportHandler, aMainThread, aStsThread,
aConduit),
mListener(aTrack
? new PipelineListener(aTrack, mConduit, aPrincipalHandle)
: nullptr) {
mListener(aTrack ? new PipelineListener(aMainThread.get(), aTrack,
mConduit, aPrincipalHandle)
: nullptr) {
mDescription = mPc + "| Receive audio";
}
@ -1425,10 +1461,9 @@ void MediaPipelineReceiveAudio::DetachMedia() {
}
}
void MediaPipelineReceiveAudio::SetPrincipalHandle_m(
const PrincipalHandle& aPrincipalHandle) {
void MediaPipelineReceiveAudio::MakePrincipalPrivate_s() {
if (mListener) {
mListener->SetPrincipalHandle_m(aPrincipalHandle);
mListener->MakePrincipalPrivate_s();
}
}
@ -1455,9 +1490,10 @@ void MediaPipelineReceiveAudio::OnRtpPacketReceived() {
class MediaPipelineReceiveVideo::PipelineListener
: public GenericReceiveListener {
public:
PipelineListener(dom::MediaStreamTrack* aTrack,
PipelineListener(RefPtr<nsISerialEventTarget> aMainThread,
dom::MediaStreamTrack* aTrack,
const PrincipalHandle& aPrincipalHandle)
: GenericReceiveListener(aTrack),
: GenericReceiveListener(std::move(aMainThread), aTrack),
mImageContainer(
LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS)),
mMutex("MediaPipelineReceiveVideo::PipelineListener::mMutex"),
@ -1465,9 +1501,23 @@ class MediaPipelineReceiveVideo::PipelineListener
mSource->AddListener(this);
}
void SetPrincipalHandle_m(const PrincipalHandle& aPrincipalHandle) {
MutexAutoLock lock(mMutex);
mPrincipalHandle = aPrincipalHandle;
void MakePrincipalPrivate_s() {
{
MutexAutoLock lock(mMutex);
mForceDropFrames = true;
}
mMainThread->Dispatch(NS_NewRunnableFunction(
__func__, [self = RefPtr<PipelineListener>(this), this] {
RefPtr<nsIPrincipal> privatePrincipal =
NullPrincipal::CreateWithInheritedAttributes(
mTrackSource->GetPrincipal());
mTrackSource->SetPrincipal(privatePrincipal);
MutexAutoLock lock(mMutex);
mPrincipalHandle = MakePrincipalHandle(privatePrincipal);
mForceDropFrames = false;
}));
}
void RenderVideoFrame(const webrtc::VideoFrameBuffer& aBuffer,
@ -1475,6 +1525,9 @@ class MediaPipelineReceiveVideo::PipelineListener
PrincipalHandle principal;
{
MutexAutoLock lock(mMutex);
if (mForceDropFrames) {
return;
}
principal = mPrincipalHandle;
}
RefPtr<Image> image;
@ -1529,6 +1582,9 @@ class MediaPipelineReceiveVideo::PipelineListener
RefPtr<layers::ImageContainer> mImageContainer;
Mutex mMutex; // Protects the below members.
PrincipalHandle mPrincipalHandle;
// Set to true on the sts thread if privacy is requested when ALPN was
// negotiated. Set to false again when mPrincipalHandle is private.
bool mForceDropFrames = false;
};
class MediaPipelineReceiveVideo::PipelineRenderer
@ -1559,7 +1615,8 @@ MediaPipelineReceiveVideo::MediaPipelineReceiveVideo(
: MediaPipelineReceive(aPc, aTransportHandler, aMainThread, aStsThread,
aConduit),
mRenderer(new PipelineRenderer(this)),
mListener(aTrack ? new PipelineListener(aTrack, aPrincipalHandle)
mListener(aTrack ? new PipelineListener(aMainThread.get(), aTrack,
aPrincipalHandle)
: nullptr) {
mDescription = mPc + "| Receive video";
aConduit->AttachRenderer(mRenderer);
@ -1578,10 +1635,9 @@ void MediaPipelineReceiveVideo::DetachMedia() {
}
}
void MediaPipelineReceiveVideo::SetPrincipalHandle_m(
const PrincipalHandle& aPrincipalHandle) {
void MediaPipelineReceiveVideo::MakePrincipalPrivate_s() {
if (mListener) {
mListener->SetPrincipalHandle_m(aPrincipalHandle);
mListener->MakePrincipalPrivate_s();
}
}

Просмотреть файл

@ -205,6 +205,7 @@ class MediaPipeline : public sigslot::has_slots<> {
void RtcpStateChange(const std::string& aTransportId, TransportLayer::State);
virtual void CheckTransportStates();
void PacketReceived(const std::string& aTransportId, MediaPacket& packet);
void AlpnNegotiated(const std::string& aAlpn, bool aPrivacyRequested);
void RtpPacketReceived(MediaPacket& packet);
void RtcpPacketReceived(MediaPacket& packet);
@ -214,6 +215,10 @@ class MediaPipeline : public sigslot::has_slots<> {
void SetDescription_s(const std::string& description);
// Called when ALPN is negotiated and is requesting privacy, so receive
// pipelines do not enter data into the graph under a content principal.
virtual void MakePrincipalPrivate_s() {}
const DirectionType mDirection;
size_t mLevel;
std::string mTransportId;
@ -331,11 +336,6 @@ class MediaPipelineReceive : public MediaPipeline {
nsCOMPtr<nsISerialEventTarget> aStsThread,
RefPtr<MediaSessionConduit> aConduit);
// Sets the PrincipalHandle we set on the media chunks produced by this
// pipeline. Must be called on the main thread.
virtual void SetPrincipalHandle_m(
const PrincipalHandle& aPrincipalHandle) = 0;
protected:
~MediaPipelineReceive();
};
@ -356,7 +356,7 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive {
bool IsVideo() const override { return false; }
void SetPrincipalHandle_m(const PrincipalHandle& aPrincipalHandle) override;
void MakePrincipalPrivate_s() override;
void Start() override;
void Stop() override;
@ -387,7 +387,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive {
bool IsVideo() const override { return true; }
void SetPrincipalHandle_m(const PrincipalHandle& aPrincipalHandle) override;
void MakePrincipalPrivate_s() override;
void Start() override;
void Stop() override;

Просмотреть файл

@ -836,7 +836,8 @@ void MediaTransportHandler::OnAlpnNegotiated(const std::string& aAlpn) {
return;
}
SignalAlpnNegotiated(aAlpn);
const bool privacyRequested = aAlpn == "c-webrtc";
SignalAlpnNegotiated(aAlpn, privacyRequested);
}
void MediaTransportHandler::OnGatheringStateChange(

Просмотреть файл

@ -124,7 +124,7 @@ class MediaTransportHandler {
std::unique_ptr<dom::RTCStatsReportInternal>&& aReport) = 0;
sigslot::signal2<const std::string&, const CandidateInfo&> SignalCandidate;
sigslot::signal1<const std::string&> SignalAlpnNegotiated;
sigslot::signal2<const std::string&, bool> SignalAlpnNegotiated;
sigslot::signal1<dom::RTCIceGatheringState> SignalGatheringStateChange;
sigslot::signal1<dom::RTCIceConnectionState> SignalConnectionStateChange;

Просмотреть файл

@ -47,7 +47,7 @@ class MediaTransportParent::Impl : public sigslot::has_slots<> {
NS_ENSURE_TRUE_VOID(mParent->SendOnCandidate(aTransportId, aCandidateInfo));
}
void OnAlpnNegotiated(const std::string& aAlpn) {
void OnAlpnNegotiated(const std::string& aAlpn, bool aPrivacyRequested) {
NS_ENSURE_TRUE_VOID(mParent->SendOnAlpnNegotiated(aAlpn));
}

Просмотреть файл

@ -1007,13 +1007,13 @@ already_AddRefed<TransceiverImpl> PeerConnectionImpl::CreateTransceiverImpl(
RefPtr<nsIPrincipal> principal;
Document* doc = GetWindow()->GetExtantDoc();
MOZ_ASSERT(doc);
const bool privacyRequested = mPrivacyRequested.valueOr(true);
if (privacyRequested) {
// We're either certain that we need isolation for the tracks, OR
// we're not sure and we can fix the track and pipeline in AlpnNegotiated.
if (mPrivacyRequested.valueOr(false)) {
principal =
NullPrincipal::CreateWithInheritedAttributes(doc->NodePrincipal());
} else {
// We're either certain that we don't need isolation for the tracks, OR
// we're not sure and the pipeline fixes the track later in
// MediaPipeline::AlpnNegotiated.
principal = doc->NodePrincipal();
}
@ -1643,28 +1643,14 @@ PeerConnectionImpl::SetPeerIdentity(const nsAString& aPeerIdentity) {
return NS_OK;
}
nsresult PeerConnectionImpl::OnAlpnNegotiated(const std::string& aAlpn) {
nsresult PeerConnectionImpl::OnAlpnNegotiated(bool aPrivacyRequested) {
PC_AUTO_ENTER_API_CALL(false);
if (mPrivacyRequested.isSome()) {
MOZ_DIAGNOSTIC_ASSERT(*mPrivacyRequested == aPrivacyRequested);
return NS_OK;
}
mPrivacyRequested = Some(aAlpn == "c-webrtc");
// For this, as with mPrivacyRequested, once we've connected to a peer, we
// fixate on that peer. Dealing with multiple peers or connections is more
// than this run-down wreck of an object can handle.
// Besides, this is only used to say if we have been connected ever.
if (!*mPrivacyRequested) {
// Neither side wants privacy
Document* doc = GetWindow()->GetExtantDoc();
if (!doc) {
CSFLogInfo(LOGTAG, "Can't update principal on streams; document gone");
return NS_ERROR_FAILURE;
}
mMedia->UpdateRemoteStreamPrincipals_m(doc->NodePrincipal());
}
mPrivacyRequested = Some(aPrivacyRequested);
return NS_OK;
}

Просмотреть файл

@ -484,7 +484,7 @@ class PeerConnectionImpl final
bool IsClosed() const;
// called when DTLS connects; we only need this once
nsresult OnAlpnNegotiated(const std::string& aAlpn);
nsresult OnAlpnNegotiated(bool aPrivacyRequested);
bool HasMedia() const;

Просмотреть файл

@ -756,15 +756,18 @@ void PeerConnectionMedia::OnCandidateFound_m(
}
}
void PeerConnectionMedia::AlpnNegotiated_s(const std::string& aAlpn) {
void PeerConnectionMedia::AlpnNegotiated_s(const std::string& aAlpn,
bool aPrivacyRequested) {
MOZ_DIAGNOSTIC_ASSERT((aAlpn == "c-webrtc") == aPrivacyRequested);
GetMainThread()->Dispatch(
WrapRunnable(this, &PeerConnectionMedia::AlpnNegotiated_m, aAlpn),
WrapRunnable(this, &PeerConnectionMedia::AlpnNegotiated_m,
aPrivacyRequested),
NS_DISPATCH_NORMAL);
}
void PeerConnectionMedia::AlpnNegotiated_m(const std::string& aAlpn) {
void PeerConnectionMedia::AlpnNegotiated_m(bool aPrivacyRequested) {
if (mParent) {
mParent->OnAlpnNegotiated(aAlpn);
mParent->OnAlpnNegotiated(aPrivacyRequested);
}
}
@ -789,15 +792,6 @@ bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const {
return false;
}
void PeerConnectionMedia::UpdateRemoteStreamPrincipals_m(
nsIPrincipal* aPrincipal) {
ASSERT_ON_THREAD(mMainThread);
for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
transceiver->UpdatePrincipal(aPrincipal);
}
}
void PeerConnectionMedia::UpdateSinkIdentity_m(
const MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal,
const PeerIdentity* aSinkIdentity) {

Просмотреть файл

@ -110,9 +110,6 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
const PeerIdentity* aSinkIdentity);
// this determines if any track is peerIdentity constrained
bool AnyLocalTrackHasPeerIdentity() const;
// When we finally learn who is on the other end, we need to change the
// ownership on streams
void UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal);
bool AnyCodecHasPluginID(uint64_t aPluginID);
@ -130,8 +127,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
nsPIDOMWindowInner* GetWindow() const;
already_AddRefed<nsIHttpChannelInternal> GetChannel() const;
void AlpnNegotiated_s(const std::string& aAlpn);
void AlpnNegotiated_m(const std::string& aAlpn);
void AlpnNegotiated_s(const std::string& aAlpn, bool aPrivacyRequested);
void AlpnNegotiated_m(bool aPrivacyRequested);
// TODO: Move to PeerConnectionImpl
RefPtr<WebRtcCallWrapper> mCall;

Просмотреть файл

@ -254,21 +254,6 @@ void TransceiverImpl::SetReceiveTrackMuted(bool aMuted) {
static_cast<RemoteTrackSource&>(mReceiveTrack->GetSource()).SetMuted(aMuted);
}
nsresult TransceiverImpl::UpdatePrincipal(nsIPrincipal* aPrincipal) {
if (mJsepTransceiver->IsStopped()) {
return NS_OK;
}
// This updates the existing principal. If we're setting a principal that does
// not subsume the old principal, there will be a delay while the principal
// propagates in media content, before the track has the principal aPrincipal.
static_cast<RemoteTrackSource&>(mReceiveTrack->GetSource())
.SetPrincipal(aPrincipal);
mReceivePipeline->SetPrincipalHandle_m(MakePrincipalHandle(aPrincipal));
return NS_OK;
}
void TransceiverImpl::ResetSync() {
if (mConduit) {
mConduit->SetSyncGroup("");

Просмотреть файл

@ -69,8 +69,6 @@ class TransceiverImpl : public nsISupports {
nsresult UpdateConduit();
nsresult UpdatePrincipal(nsIPrincipal* aPrincipal);
void ResetSync();
nsresult SyncWithMatchingVideoConduits(