Bug 1208371 - Let PeerConnection consume principals from tracks instead of streams. r?mt draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 10 Mar 2016 17:35:35 +0100
changeset 342152 9c1c883ac38202c2e30133592a742f536016effa
parent 342151 2734e42f96a537cc91e4b9b531f3c6bf329dbd09
child 342153 2b8ce704b0745150f21ae464f517a72c4678795b
push id13352
push userpehrsons@gmail.com
push dateFri, 18 Mar 2016 13:49:47 +0000
reviewersmt
bugs1208371
milestone47.0a1
Bug 1208371 - Let PeerConnection consume principals from tracks instead of streams. r?mt MozReview-Commit-ID: 4kp8wTFohxZ
media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
media/webrtc/signaling/test/FakeMediaStreams.h
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp
@@ -673,20 +673,29 @@ void MediaPipelineTransmit::AttachToTrac
 
 #ifndef MOZILLA_INTERNAL_API
   // this enables the unit tests that can't fiddle with principals and the like
   listener_->SetEnabled(true);
 #endif
 }
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
-void MediaPipelineTransmit::UpdateSinkIdentity_m(nsIPrincipal* principal,
+void MediaPipelineTransmit::UpdateSinkIdentity_m(MediaStreamTrack* track,
+                                                 nsIPrincipal* principal,
                                                  const PeerIdentity* sinkIdentity) {
   ASSERT_ON_THREAD(main_thread_);
 
+  if (track != nullptr && track != domtrack_) {
+    // If a track is specified, then it might not be for this pipeline,
+    // since we receive notifications for all tracks on the PC.
+    // nullptr means that the PeerIdentity has changed and shall be applied
+    // to all tracks of the PC.
+    return;
+  }
+
   bool enableTrack = principal->Subsumes(domtrack_->GetPrincipal());
   if (!enableTrack) {
     // first try didn't work, but there's a chance that this is still available
     // if our track is bound to a peerIdentity, and the peer connection (our
     // sink) is bound to the same identity, then we can enable the track.
     const PeerIdentity* trackIdentity = domtrack_->GetPeerIdentity();
     if (sinkIdentity && trackIdentity) {
       enableTrack = (*sinkIdentity == *trackIdentity);
--- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
+++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h
@@ -396,19 +396,21 @@ public:
   virtual nsresult Init() override;
 
   virtual void AttachToTrack(const std::string& track_id);
 
   // written and used from MainThread
   virtual bool IsVideo() const override { return !!domtrack_->AsVideoStreamTrack(); }
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
-  // when the principal of the PeerConnection changes, it calls through to here
-  // so that we can determine whether to enable stream transmission
-  virtual void UpdateSinkIdentity_m(nsIPrincipal* principal,
+  // When the principal of the domtrack changes, it calls through to here
+  // so that we can determine whether to enable track transmission.
+  // `track` has to be null or equal `domtrack_` for us to apply the update.
+  virtual void UpdateSinkIdentity_m(dom::MediaStreamTrack* track,
+                                    nsIPrincipal* principal,
                                     const PeerIdentity* sinkIdentity);
 #endif
 
   // Called on the main thread.
   virtual void DetachMedia() override {
     ASSERT_ON_THREAD(main_thread_);
     if (domtrack_) {
       domtrack_->RemoveDirectListener(listener_);
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -586,17 +586,18 @@ MediaPipelineFactory::CreateMediaPipelin
       aRtpFlow,
       aRtcpFlow,
       aFilter);
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   // implement checking for peerIdentity (where failure == black/silence)
   nsIDocument* doc = mPC->GetWindow()->GetExtantDoc();
   if (doc) {
-    pipeline->UpdateSinkIdentity_m(doc->NodePrincipal(),
+    pipeline->UpdateSinkIdentity_m(track,
+                                   doc->NodePrincipal(),
                                    mPC->GetPeerIdentity());
   } else {
     MOZ_MTLOG(ML_ERROR, "Cannot initialize pipeline without attached doc");
     return NS_ERROR_FAILURE; // Don't remove this till we know it's safe.
   }
 #endif
 
   rv = pipeline->Init();
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2135,17 +2135,18 @@ PeerConnectionImpl::SetPeerIdentity(cons
     }
   } else {
     mPeerIdentity = new PeerIdentity(aPeerIdentity);
     nsIDocument* doc = GetWindow()->GetExtantDoc();
     if (!doc) {
       CSFLogInfo(logTag, "Can't update principal on streams; document gone");
       return NS_ERROR_FAILURE;
     }
-    mMedia->UpdateSinkIdentity_m(doc->NodePrincipal(), mPeerIdentity);
+    MediaStreamTrack* allTracks = nullptr;
+    mMedia->UpdateSinkIdentity_m(allTracks, doc->NodePrincipal(), mPeerIdentity);
   }
   return NS_OK;
 }
 #endif
 
 nsresult
 PeerConnectionImpl::SetDtlsConnected(bool aPrivacyRequested)
 {
@@ -2168,20 +2169,20 @@ PeerConnectionImpl::SetDtlsConnected(boo
 #endif
   mDtlsConnected = true;
   mPrivacyRequested = mPrivacyRequested || aPrivacyRequested;
   return NS_OK;
 }
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 void
-PeerConnectionImpl::PrincipalChanged(DOMMediaStream* aMediaStream) {
+PeerConnectionImpl::PrincipalChanged(MediaStreamTrack* aTrack) {
   nsIDocument* doc = GetWindow()->GetExtantDoc();
   if (doc) {
-    mMedia->UpdateSinkIdentity_m(doc->NodePrincipal(), mPeerIdentity);
+    mMedia->UpdateSinkIdentity_m(aTrack, doc->NodePrincipal(), mPeerIdentity);
   } else {
     CSFLogInfo(logTag, "Can't update sink principal; document gone");
   }
 }
 #endif
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 nsresult
@@ -2245,31 +2246,28 @@ PeerConnectionImpl::AddTrack(MediaStream
 nsresult
 PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
                              DOMMediaStream& aMediaStream)
 {
   if (!aMediaStream.HasTrack(aTrack)) {
     CSFLogError(logTag, "%s: Track is not in stream", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
-  uint32_t num = mMedia->LocalStreamsLength();
 
   std::string streamId = PeerConnectionImpl::GetStreamId(aMediaStream);
   std::string trackId = PeerConnectionImpl::GetTrackId(aTrack);
   nsresult res = mMedia->AddTrack(aMediaStream, streamId, aTrack, trackId);
   if (NS_FAILED(res)) {
     return res;
   }
 
   CSFLogDebug(logTag, "Added track (%s) to stream %s",
                       trackId.c_str(), streamId.c_str());
 
-  if (num != mMedia->LocalStreamsLength()) {
-    aMediaStream.AddPrincipalChangeObserver(this);
-  }
+  aTrack.AddPrincipalChangeObserver(this);
 
   if (aTrack.AsAudioStreamTrack()) {
     res = AddTrackToJsepSession(SdpMediaSection::kAudio, streamId, trackId);
     if (NS_FAILED(res)) {
       return res;
     }
     mNumAudioStreams++;
   }
@@ -2331,16 +2329,18 @@ PeerConnectionImpl::RemoveTrack(MediaStr
                 __FUNCTION__,
                 info->GetId().c_str(),
                 trackId.c_str());
     return rv;
   }
 
   media()->RemoveLocalTrack(info->GetId(), trackId);
 
+  aTrack.RemovePrincipalChangeObserver(this);
+
   OnNegotiationNeeded();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PeerConnectionImpl::ReplaceTrack(MediaStreamTrack& aThisTrack,
                                  MediaStreamTrack& aWithTrack) {
@@ -2420,16 +2420,18 @@ PeerConnectionImpl::ReplaceTrack(MediaSt
                              ObString("Failed to replace track"),
                              jrv);
     if (jrv.Failed()) {
       CSFLogError(logTag, "Error firing replaceTrack error callback");
       return NS_ERROR_UNEXPECTED;
     }
     return NS_OK;
   }
+  aThisTrack.RemovePrincipalChangeObserver(this);
+  aWithTrack.AddPrincipalChangeObserver(this);
   pco->OnReplaceTrackSuccess(jrv);
   if (jrv.Failed()) {
     CSFLogError(logTag, "Error firing replaceTrack success callback");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
@@ -2783,20 +2785,22 @@ void
 PeerConnectionImpl::ShutdownMedia()
 {
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
   if (!mMedia)
     return;
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
-  // before we destroy references to local streams, detach from them
+  // before we destroy references to local tracks, detach from them
   for(uint32_t i = 0; i < media()->LocalStreamsLength(); ++i) {
     LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i);
-    info->GetMediaStream()->RemovePrincipalChangeObserver(this);
+    for (const auto& pair : info->GetMediaStreamTracks()) {
+      pair.second->RemovePrincipalChangeObserver(this);
+    }
   }
 
   // End of call to be recorded in Telemetry
   if (!mStartTime.IsNull()){
     TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
     Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_CALL_DURATION :
                                     Telemetry::WEBRTC_CALL_DURATION,
                           timeDelta.ToSeconds());
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -26,29 +26,27 @@
 #include "nsIThread.h"
 
 #include "signaling/src/jsep/JsepSession.h"
 #include "signaling/src/jsep/JsepSessionImpl.h"
 #include "signaling/src/sdp/SdpMediaSection.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/PeerConnectionImplEnumsBinding.h"
+#include "PrincipalChangeObserver.h"
 #include "StreamBuffer.h"
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
 #include "mozilla/TimeStamp.h"
 #include "mozilla/net/DataChannel.h"
 #include "VideoUtils.h"
 #include "VideoSegment.h"
 #include "mozilla/dom/RTCStatsReportBinding.h"
 #include "nsIPrincipal.h"
 #include "mozilla/PeerIdentity.h"
-#ifndef USE_FAKE_MEDIA_STREAMS
-#include "DOMMediaStream.h"
-#endif
 #endif
 
 namespace test {
 #ifdef USE_FAKE_PCOBSERVER
 class AFakePCObserver;
 #endif
 }
 
@@ -243,17 +241,17 @@ class RTCStatsQuery {
       nsresult res = CheckApiState(assert_ice_ready);             \
       if (NS_FAILED(res)) return; \
     } while(0)
 #define PC_AUTO_ENTER_API_CALL_NO_CHECK() CheckThread()
 
 class PeerConnectionImpl final : public nsISupports,
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
                                  public mozilla::DataChannelConnection::DataConnectionListener,
-                                 public dom::PrincipalChangeObserver<DOMMediaStream>,
+                                 public dom::PrincipalChangeObserver<dom::MediaStreamTrack>,
 #endif
                                  public sigslot::has_slots<>
 {
   struct Internal; // Avoid exposing c includes to bindings
 
 public:
   explicit PeerConnectionImpl(const mozilla::dom::GlobalObject* aGlobal = nullptr);
 
@@ -643,19 +641,19 @@ public:
   void startCallTelem();
 
   nsresult BuildStatsQuery_m(
       mozilla::dom::MediaStreamTrack *aSelector,
       RTCStatsQuery *query);
 
   static nsresult ExecuteStatsQuery_s(RTCStatsQuery *query);
 
-  // for monitoring changes in stream ownership
+  // for monitoring changes in track ownership
   // PeerConnectionMedia can't do it because it doesn't know about principals
-  virtual void PrincipalChanged(DOMMediaStream* aMediaStream) override;
+  virtual void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
 
   nsresult GetRemoteTrackId(const std::string streamId,
                             const dom::MediaStreamTrack& track,
                             std::string* trackId) const;
 #endif
 
   static std::string GetStreamId(const DOMMediaStream& aStream);
   static std::string GetTrackId(const dom::MediaStreamTrack& track);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -1227,34 +1227,37 @@ PeerConnectionMedia::UpdateRemoteStreamP
   ASSERT_ON_THREAD(mMainThread);
 
   for (uint32_t u = 0; u < mRemoteSourceStreams.Length(); u++) {
     mRemoteSourceStreams[u]->UpdatePrincipal_m(aPrincipal);
   }
 }
 
 void
-PeerConnectionMedia::UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
+PeerConnectionMedia::UpdateSinkIdentity_m(MediaStreamTrack* aTrack,
+                                          nsIPrincipal* aPrincipal,
                                           const PeerIdentity* aSinkIdentity)
 {
   ASSERT_ON_THREAD(mMainThread);
 
   for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
-    mLocalSourceStreams[u]->UpdateSinkIdentity_m(aPrincipal, aSinkIdentity);
+    mLocalSourceStreams[u]->UpdateSinkIdentity_m(aTrack, aPrincipal,
+                                                 aSinkIdentity);
   }
 }
 
 void
-LocalSourceStreamInfo::UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
+LocalSourceStreamInfo::UpdateSinkIdentity_m(MediaStreamTrack* aTrack,
+                                            nsIPrincipal* aPrincipal,
                                             const PeerIdentity* aSinkIdentity)
 {
   for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) {
     MediaPipelineTransmit* pipeline =
       static_cast<MediaPipelineTransmit*>((*it).second.get());
-    pipeline->UpdateSinkIdentity_m(aPrincipal, aSinkIdentity);
+    pipeline->UpdateSinkIdentity_m(aTrack, aPrincipal, aSinkIdentity);
   }
 }
 
 void RemoteSourceStreamInfo::UpdatePrincipal_m(nsIPrincipal* aPrincipal)
 {
   // This blasts away the existing principal.
   // We only do this when we become certain that the all tracks are safe to make
   // accessible to the script principal.
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -92,16 +92,20 @@ public:
   }
   size_t GetTrackCount() const { return mTracks.size(); }
 
   // This method exists for stats and the unittests.
   // It allows visibility into the pipelines and flows.
   const std::map<std::string, RefPtr<MediaPipeline>>&
   GetPipelines() const { return mPipelines; }
   RefPtr<MediaPipeline> GetPipelineByTrackId_m(const std::string& trackId);
+  // This is needed so PeerConnectionImpl can unregister itself as
+  // PrincipalChangeObserver from each track.
+  const std::map<std::string, RefPtr<dom::MediaStreamTrack>>&
+  GetMediaStreamTracks() const { return mTracks; }
   dom::MediaStreamTrack* GetTrackById(const std::string& trackId)
   {
     auto it = mTracks.find(trackId);
     if (it == mTracks.end()) {
       return nullptr;
     }
 
     return it->second;
@@ -137,17 +141,18 @@ public:
      : SourceStreamInfo(aMediaStream, aParent, aId) {}
 
   nsresult TakePipelineFrom(RefPtr<LocalSourceStreamInfo>& info,
                             const std::string& oldTrackId,
                             dom::MediaStreamTrack& aNewTrack,
                             const std::string& newTrackId);
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
-  void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
+  void UpdateSinkIdentity_m(dom::MediaStreamTrack* aTrack,
+                            nsIPrincipal* aPrincipal,
                             const PeerIdentity* aSinkIdentity);
 #endif
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo)
 
 private:
   already_AddRefed<MediaPipeline> ForgetPipelineByTrackId_m(
       const std::string& trackId);
@@ -359,17 +364,19 @@ class PeerConnectionMedia : public sigsl
                         dom::MediaStreamTrack& aNewTrack,
                         const std::string& aNewStreamId,
                         const std::string& aNewTrackId);
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   // In cases where the peer isn't yet identified, we disable the pipeline (not
   // the stream, that would potentially affect others), so that it sends
   // black/silence.  Once the peer is identified, re-enable those streams.
-  void UpdateSinkIdentity_m(nsIPrincipal* aPrincipal,
+  // aTrack will be set if this update came from a principal change on aTrack.
+  void UpdateSinkIdentity_m(dom::MediaStreamTrack* aTrack,
+                            nsIPrincipal* aPrincipal,
                             const PeerIdentity* aSinkIdentity);
   // this determines if any stream is peerIdentity constrained
   bool AnyLocalStreamHasPeerIdentity() const;
   // When we finally learn who is on the other end, we need to change the ownership
   // on streams
   void UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal);
 #endif
 
--- a/media/webrtc/signaling/test/FakeMediaStreams.h
+++ b/media/webrtc/signaling/test/FakeMediaStreams.h
@@ -400,16 +400,25 @@ public:
     AddListener(aListener);
     aListener->NotifyDirectListenerInstalled(
       Fake_MediaStreamTrackDirectListener::InstallationResult::STREAM_NOT_SUPPORTED);
   }
   void RemoveDirectListener(Fake_MediaStreamTrackDirectListener *aListener)
   {
     RemoveListener(aListener);
   }
+
+  class PrincipalChangeObserver
+  {
+  public:
+    virtual void PrincipalChanged(Fake_MediaStreamTrack* aMediaStreamTrack) = 0;
+  };
+  void AddPrincipalChangeObserver(void* ignoredObserver) {}
+  void RemovePrincipalChangeObserver(void* ignoredObserver) {}
+
 private:
   ~Fake_MediaStreamTrack() {}
 
   const bool mIsVideo;
   Fake_DOMMediaStream* mOwningStream;
   mozilla::TrackID mTrackID;
   std::string mID;
 };
@@ -532,24 +541,16 @@ public:
         return mVideoTrack;
       }
       default: {
         MOZ_CRASH("Unkown media type");
       }
     }
   }
 
-  class PrincipalChangeObserver
-  {
-  public:
-    virtual void PrincipalChanged(Fake_DOMMediaStream* aMediaStream) = 0;
-  };
-  void AddPrincipalChangeObserver(void* ignoredObserver) {}
-  void RemovePrincipalChangeObserver(void* ignoredObserver) {}
-
 private:
   RefPtr<Fake_MediaStream> mMediaStream;
 
   // tells the SDP generator about whether this
   // MediaStream probably has audio and/or video
   uint32_t mHintContents;
   RefPtr<Fake_MediaStreamTrack> mVideoTrack;
   RefPtr<Fake_MediaStreamTrack> mAudioTrack;