Bug 1208371 - Add a PrincipalHandleListener to MediaStreamTrack. r?mt,jesup draft
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 15 Mar 2016 10:52:25 +0100
changeset 347659 ccf85fe39b56a8e6c503c6d89ed2f46d6b394edf
parent 347658 444b27339e853f2b5d64cc7971d23446c609327a
child 347660 600c27cd585f9c191fec2c6c35d8a0deb95a184f
push id14642
push userpehrsons@gmail.com
push dateTue, 05 Apr 2016 16:45:34 +0000
reviewersmt, jesup
bugs1208371
milestone47.0a1
Bug 1208371 - Add a PrincipalHandleListener to MediaStreamTrack. r?mt,jesup MozReview-Commit-ID: JvhXrlKPZAC
dom/media/MediaStreamTrack.cpp
dom/media/MediaStreamTrack.h
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -39,30 +39,89 @@ MediaStreamTrackSource::ApplyConstraints
   promise->MaybeReject(new MediaStreamError(
     aWindow,
     NS_LITERAL_STRING("OverconstrainedError"),
     NS_LITERAL_STRING(""),
     NS_LITERAL_STRING("")));
   return promise.forget();
 }
 
+/**
+ * PrincipalHandleListener monitors changes in PrincipalHandle of the media flowing
+ * through the MediaStreamGraph.
+ *
+ * When the main thread principal for a MediaStreamTrack changes, its principal
+ * will be set to the combination of the previous principal and the new one.
+ *
+ * As a PrincipalHandle change later happens on the MediaStreamGraph thread, we will
+ * be notified. If the latest principal on main thread matches the PrincipalHandle
+ * we just saw on MSG thread, we will set the track's principal to the new one.
+ *
+ * We know at this point that the old principal has been flushed out and data
+ * under it cannot leak to consumers.
+ *
+ * In case of multiple changes to the main thread state, the track's principal
+ * will be a combination of its old principal and all the new ones until the
+ * latest main thread principal matches the PrincipalHandle on the MSG thread.
+ */
+class MediaStreamTrack::PrincipalHandleListener : public MediaStreamTrackListener
+{
+public:
+  explicit PrincipalHandleListener(MediaStreamTrack* aTrack)
+    : mTrack(aTrack) {}
+
+  void Forget()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mTrack = nullptr;
+  }
+
+  void DoNotifyPrincipalHandleChanged(const PrincipalHandle& aNewPrincipalHandle)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (!mTrack) {
+      return;
+    }
+
+    mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle);
+  }
+
+  void NotifyPrincipalHandleChanged(MediaStreamGraph* aGraph,
+                                    const PrincipalHandle& aNewPrincipalHandle) override
+  {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethodWithArgs<StoreCopyPassByConstLRef<PrincipalHandle>>(
+        this, &PrincipalHandleListener::DoNotifyPrincipalHandleChanged, aNewPrincipalHandle);
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+  }
+
+protected:
+  // These fields may only be accessed on the main thread
+  MediaStreamTrack* mTrack;
+};
+
 MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                                    TrackID aInputTrackID, const nsString& aLabel,
                                    MediaStreamTrackSource* aSource)
   : mOwningStream(aStream), mTrackID(aTrackID),
-    mInputTrackID(aInputTrackID), mSource(aSource), mLabel(aLabel),
+    mInputTrackID(aInputTrackID), mSource(aSource),
+    mPrincipal(aSource->GetPrincipal()), mLabel(aLabel),
     mEnded(false), mEnabled(true), mRemote(aSource->IsRemote()), mStopped(false)
 {
 
   if (!gMediaStreamTrackLog) {
     gMediaStreamTrackLog = PR_NewLogModule("MediaStreamTrack");
   }
 
   GetSource().RegisterSink(this);
 
+  mPrincipalHandleListener = new PrincipalHandleListener(this);
+  AddListener(mPrincipalHandleListener);
+
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
 
   nsID uuid;
   memset(&uuid, 0, sizeof(uuid));
   if (uuidgen) {
     uuidgen->GenerateUUIDInPlace(&uuid);
@@ -70,35 +129,53 @@ MediaStreamTrack::MediaStreamTrack(DOMMe
 
   char chars[NSID_LENGTH];
   uuid.ToProvidedString(chars);
   mID = NS_ConvertASCIItoUTF16(chars);
 }
 
 MediaStreamTrack::~MediaStreamTrack()
 {
+  Destroy();
+}
+
+void
+MediaStreamTrack::Destroy()
+{
+  if (mSource) {
+    mSource->UnregisterSink(this);
+  }
+  if (mPrincipalHandleListener) {
+    if (GetOwnedStream()) {
+      RemoveListener(mPrincipalHandleListener);
+    }
+    mPrincipalHandleListener->Forget();
+    mPrincipalHandleListener = nullptr;
+  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
                                                 DOMEventTargetHelper)
+  tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwningStream)
-  if (tmp->mSource) {
-    tmp->mSource->UnregisterSink(tmp);
-  }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalTrack)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPrincipal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwningStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalTrack)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 nsPIDOMWindowInner*
@@ -172,30 +249,65 @@ MediaStreamTrack::Graph()
 
 MediaStreamGraphImpl*
 MediaStreamTrack::GraphImpl()
 {
   return GetOwnedStream()->GraphImpl();
 }
 
 void
-MediaStreamTrack::PrincipalChanged()
+MediaStreamTrack::SetPrincipal(nsIPrincipal* aPrincipal)
 {
-  LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed. Now: "
-                       "null=%d, codebase=%d, expanded=%d, system=%d", this,
-                       GetPrincipal()->GetIsNullPrincipal(),
-                       GetPrincipal()->GetIsCodebasePrincipal(),
-                       GetPrincipal()->GetIsExpandedPrincipal(),
-                       GetPrincipal()->GetIsSystemPrincipal()));
+  if (aPrincipal == mPrincipal) {
+    return;
+  }
+  mPrincipal = aPrincipal;
+
+  LOG(LogLevel::Info, ("MediaStreamTrack %p principal changed to %p. Now: "
+                       "null=%d, codebase=%d, expanded=%d, system=%d",
+                       this, mPrincipal.get(),
+                       mPrincipal->GetIsNullPrincipal(),
+                       mPrincipal->GetIsCodebasePrincipal(),
+                       mPrincipal->GetIsExpandedPrincipal(),
+                       mPrincipal->GetIsSystemPrincipal()));
   for (PrincipalChangeObserver<MediaStreamTrack>* observer
       : mPrincipalChangeObservers) {
     observer->PrincipalChanged(this);
   }
 }
 
+void
+MediaStreamTrack::PrincipalChanged()
+{
+  mPendingPrincipal = GetSource().GetPrincipal();
+  nsCOMPtr<nsIPrincipal> newPrincipal = mPrincipal;
+  LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed on main thread "
+                       "to %p (pending). Combining with existing principal %p.",
+                       this, mPendingPrincipal.get(), mPrincipal.get()));
+  if (nsContentUtils::CombineResourcePrincipals(&newPrincipal,
+                                                mPendingPrincipal)) {
+    SetPrincipal(newPrincipal);
+  }
+}
+
+void
+MediaStreamTrack::NotifyPrincipalHandleChanged(const PrincipalHandle& aNewPrincipalHandle)
+{
+  PrincipalHandle handle(aNewPrincipalHandle);
+  LOG(LogLevel::Info, ("MediaStreamTrack %p principalHandle changed on "
+                       "MediaStreamGraph thread to %p. Current principal: %p, "
+                       "pending: %p",
+                       this, GetPrincipalFromHandle(handle),
+                       mPrincipal.get(), mPendingPrincipal.get()));
+  if (PrincipalHandleMatches(handle, mPendingPrincipal)) {
+    SetPrincipal(mPendingPrincipal);
+    mPendingPrincipal = nullptr;
+  }
+}
+
 bool
 MediaStreamTrack::AddPrincipalChangeObserver(
   PrincipalChangeObserver<MediaStreamTrack>* aObserver)
 {
   return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr;
 }
 
 bool
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -71,17 +71,17 @@ public:
   /**
    * Get this TrackSource's principal.
    */
   nsIPrincipal* GetPrincipal() const { return mPrincipal; }
 
   /**
    * Get the source's current CORSMode. If not applicable CORS_NONE is returned.
    * The sink will be notified of changes to our CORSMode through
-   * NotifyPrincipalChanged().
+   * PrincipalChanged().
    */
   virtual CORSMode GetCORSMode() const { return CORS_NONE; }
 
   /**
    * This is used in WebRTC. A peerIdentity constrained MediaStreamTrack cannot
    * be sent across the network to anything other than a peer with the provided
    * identity. If this is set, then GetPrincipal() should return an instance of
    * nsNullPrincipal.
@@ -205,16 +205,18 @@ class MediaStreamTrack : public DOMEvent
   // some internal state, e.g., GetInputStream(), GetOwnedStream().
   friend class mozilla::DOMMediaStream;
 
   // PeerConnection and friends need to know our owning DOMStream and track id.
   friend class mozilla::PeerConnectionImpl;
   friend class mozilla::PeerConnectionMedia;
   friend class mozilla::RemoteSourceStreamInfo;
 
+  class PrincipalHandleListener;
+
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
    * MediaStream owned by aStream.
    */
   MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                    TrackID aInputTrackID, const nsString& aLabel,
                    MediaStreamTrackSource* aSource);
@@ -245,17 +247,24 @@ public:
 
   bool Ended() const { return mEnded; }
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
   /**
    * Get this track's principal.
    */
-  nsIPrincipal* GetPrincipal() const { return GetSource().GetPrincipal(); }
+  nsIPrincipal* GetPrincipal() const { return mPrincipal; }
+
+  /**
+   * Called by the PrincipalHandleListener when this track's PrincipalHandle changes on
+   * the MediaStreamGraph thread. When the PrincipalHandle matches the pending
+   * principal we know that the principal change has propagated to consumers.
+   */
+  void NotifyPrincipalHandleChanged(const PrincipalHandle& aPrincipalHandle);
 
   /**
    * Get this track's CORS mode.
    */
   CORSMode GetCORSMode() const { return GetSource().GetCORSMode(); }
 
   /**
    * Get this track's PeerIdentity.
@@ -326,41 +335,51 @@ public:
    * Returns true if this track is connected to aPort and forwarded to aPort's
    * output stream.
    */
   bool IsForwardedThrough(MediaInputPort* aPort);
 
 protected:
   virtual ~MediaStreamTrack();
 
+  void Destroy();
+
   // Returns the original DOMMediaStream's underlying input stream.
   MediaStream* GetInputStream();
 
   // Returns the owning DOMMediaStream's underlying owned stream.
   ProcessedMediaStream* GetOwnedStream();
 
   // Returns the original DOMMediaStream. If this track is a clone,
   // the original track's owning DOMMediaStream is returned.
   DOMMediaStream* GetInputDOMStream();
 
   /**
+   * Sets the principal and notifies PrincipalChangeObservers if it changes.
+   */
+  void SetPrincipal(nsIPrincipal* aPrincipal);
+
+  /**
    * Creates a new MediaStreamTrack with the same type, input track ID and
    * source as this MediaStreamTrack.
    * aTrackID is the TrackID the new track will have in its owned stream.
    */
   virtual already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
                                                            TrackID aTrackID) = 0;
 
   nsTArray<PrincipalChangeObserver<MediaStreamTrack>*> mPrincipalChangeObservers;
 
   RefPtr<DOMMediaStream> mOwningStream;
   TrackID mTrackID;
   TrackID mInputTrackID;
   RefPtr<MediaStreamTrackSource> mSource;
   RefPtr<MediaStreamTrack> mOriginalTrack;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIPrincipal> mPendingPrincipal;
+  RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
   nsString mID;
   nsString mLabel;
   bool mEnded;
   bool mEnabled;
   const bool mRemote;
   bool mStopped;
 };