Bug 1208371 - Add a PrincipalChangeObserver interface to MediaStreamTrack. r?roc draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 05 Nov 2015 15:42:23 +0800
changeset 306650 0e0e553f75f863a88afa21fd67de1ed76f4e14e1
parent 306649 f175af9a98ee5e84a5605cb1b708fccc1abb830c
child 306651 532526a4a7d8e4671a73987915f1c4d7999ce570
push id7183
push userpehrsons@gmail.com
push dateThu, 05 Nov 2015 07:42:40 +0000
reviewersroc
bugs1208371
milestone45.0a1
Bug 1208371 - Add a PrincipalChangeObserver interface to MediaStreamTrack. r?roc Also adds MediaStreamTrack::GetPrincipal, so we can start moving consumers towards checking principals per consumed track, and producers to set principals per track instead of per stream. For compatibility with modules consuming whole streams we can move DOMMediaStream over to listening for principal changes on all its tracks, plus update its principal when its set of tracks changes.
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaStreamTrack.cpp
dom/media/MediaStreamTrack.h
dom/media/imagecapture/CaptureTask.cpp
dom/media/imagecapture/CaptureTask.h
dom/media/imagecapture/ImageCapture.cpp
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -531,27 +531,22 @@ DOMMediaStream::AddTrack(MediaStreamTrac
     return;
   }
 
   if (HasTrack(aTrack)) {
     LOG(LogLevel::Debug, ("DOMMediaStream %p already contains track %p", this, &aTrack));
     return;
   }
 
-  RefPtr<DOMMediaStream> addedDOMStream = aTrack.GetStream();
-  MOZ_RELEASE_ASSERT(addedDOMStream);
-
-  RefPtr<MediaStream> owningStream = addedDOMStream->GetOwnedStream();
-  MOZ_RELEASE_ASSERT(owningStream);
-
-  CombineWithPrincipal(addedDOMStream->mPrincipal);
+  CombineWithPrincipal(aTrack.GetPrincipal());
 
   // Hook up the underlying track with our underlying playback stream.
   RefPtr<MediaInputPort> inputPort =
-    GetPlaybackStream()->AllocateInputPort(owningStream, aTrack.GetTrackID());
+    GetPlaybackStream()->AllocateInputPort(aTrack.GetOwnedStream(),
+                                           aTrack.GetTrackID());
   RefPtr<TrackPort> trackPort =
     new TrackPort(inputPort, &aTrack, TrackPort::InputPortOwnership::OWNED);
   mTracks.AppendElement(trackPort.forget());
   NotifyTrackAdded(&aTrack);
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Added track %p", this, &aTrack));
 }
 
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -451,25 +451,40 @@ public:
   /**
    * This is used in WebRTC to move from a protected state (nsNullPrincipal) to
    * one where the stream is accessible to script.  Don't call this.
    * CombineWithPrincipal is almost certainly more appropriate.
    */
   void SetPrincipal(nsIPrincipal* aPrincipal);
 
   /**
-   * Used to learn about dynamic changes in principal occur.
+   * Used to learn about dynamic changes to a stream's principal.
    * Operations relating to these observers must be confined to the main thread.
    */
   class PrincipalChangeObserver
   {
   public:
     virtual void PrincipalChanged(DOMMediaStream* aMediaStream) = 0;
   };
+
+  /**
+   * Add a PrincipalChangeObserver to this stream.
+   *
+   * Returns true if it was successfully added.
+   *
+   * Ownership of the PrincipalChangeObserver remains with the caller, and it's
+   * the caller's responsibility to remove the observer before it dies.
+   */
   bool AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver);
+
+  /**
+   * Remove an added PrincipalChangeObserver from this stream.
+   *
+   * Returns true if it was successfully removed.
+   */
   bool RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver);
 
   /**
    * Called when this stream's MediaStreamGraph has been shut down. Normally
    * MSGs are only shut down when all streams have been removed, so this
    * will only be called during a forced shutdown due to application exit.
    */
   void NotifyMediaStreamGraphShutdown();
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -49,16 +49,18 @@ MediaStreamTrack::MediaStreamTrack(DOMMe
   mID = NS_ConvertASCIItoUTF16(chars);
 }
 
 MediaStreamTrack::~MediaStreamTrack()
 {
   if (!mStopped) {
     mSource->UnregisterSink();
   }
+
+  MOZ_ASSERT(mPrincipalChangeObservers.IsEmpty());
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamTrack, DOMEventTargetHelper,
                                    mOwningStream, mOriginalTrack)
 
 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamTrack)
@@ -114,16 +116,22 @@ MediaStreamTrack::ApplyConstraints(const
 
     LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with "
                          "constraints %s", this, NS_ConvertUTF16toUTF8(str).get()));
   }
 
   return GetStream()->ApplyConstraintsToTrack(mTrackID, aConstraints, aRv);
 }
 
+nsIPrincipal*
+MediaStreamTrack::GetPrincipal()
+{
+  return mOwningStream->GetPrincipal();
+}
+
 MediaStreamGraph*
 MediaStreamTrack::Graph()
 {
   return GetOwnedStream()->Graph();
 }
 
 DOMMediaStream*
 MediaStreamTrack::GetInputDOMStream()
@@ -143,10 +151,34 @@ MediaStreamTrack::GetInputStream()
 }
 
 ProcessedMediaStream*
 MediaStreamTrack::GetOwnedStream()
 {
   return GetStream()->GetOwnedStream();
 }
 
+bool
+MediaStreamTrack::AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver)
+{
+  PrincipalChangeObserverForwarder* observer =
+    mPrincipalChangeObservers.AppendElement();
+  observer->Init(aObserver, this);
+  return mOwningStream->AddPrincipalChangeObserver(observer);
+}
+
+bool
+MediaStreamTrack::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver)
+{
+  for (int i = mPrincipalChangeObservers.Length() - 1; i >= 0; --i) {
+    PrincipalChangeObserverForwarder& fwd = mPrincipalChangeObservers[i];
+    if (aObserver == fwd.Observer()) {
+      bool result = mOwningStream->RemovePrincipalChangeObserver(&fwd);
+      mPrincipalChangeObservers.RemoveElementAt(i);
+      return result;
+    }
+  }
+  NS_ASSERTION(false, "Double-removal of MediaStreamTrack PrincipalChangeObserver");
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -1,25 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
 /* 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/. */
 
 #ifndef MEDIASTREAMTRACK_H_
 #define MEDIASTREAMTRACK_H_
 
+#include "DOMMediaStream.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsError.h"
 #include "nsID.h"
 #include "StreamBuffer.h"
 #include "MediaTrackConstraints.h"
 
 namespace mozilla {
 
-class DOMMediaStream;
 class MediaEnginePhotoCallback;
 class MediaStream;
 class MediaStreamGraph;
 class ProcessedMediaStream;
 
 namespace dom {
 
 class AudioStreamTrack;
@@ -127,16 +127,20 @@ protected:
 
   const MediaSourceEnum mMediaSource;
 };
 
 /**
  * Class representing a track in a DOMMediaStream.
  */
 class MediaStreamTrack : public DOMEventTargetHelper {
+  // DOMMediaStream owns MediaStreamTrack instances, and requires access to
+  // some internal state, e.g., GetInputStream(), GetOwnedStream().
+  friend class mozilla::DOMMediaStream;
+
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
    * MediaStream owned by aStream.
    */
   MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                    TrackID aInputTrackID,
                    MediaStreamTrackSource* aSource);
@@ -176,41 +180,119 @@ public:
   void Stop();
   already_AddRefed<Promise>
   ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
 
   bool Ended() const { return mEnded; }
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
+  /**
+   * Get this track's principal.
+   *
+   * This is currently the same principal as for the track's owning
+   * DOMMediaStream.
+   */
+  nsIPrincipal* GetPrincipal();
+
   MediaStreamGraph* Graph();
 
   MediaStreamTrackSource& GetSource()
   {
     MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction");
     return *mSource;
   }
 
   // Webrtc allows the remote side to name tracks whatever it wants, and we
   // need to surface this to content.
   void AssignId(const nsAString& aID) { mID = aID; }
 
+  /**
+   * A PrincipalChangeObserver for MediaStreamTrack, inspired by the one with
+   * the same name in DOMMediaStream.
+   *
+   * Used to learn about dynamic changes to a track's principal.
+   * Operations relating to these observers must be confined to the main thread.
+   */
+  class PrincipalChangeObserver
+  {
+  public:
+    virtual void PrincipalChanged(MediaStreamTrack* aTrack) = 0;
+  };
+
+  /**
+   * Add a PrincipalChangeObserver to this track.
+   *
+   * Returns true if it was successfully added.
+   *
+   * Ownership of the PrincipalChangeObserver remains with the caller, and it's
+   * the caller's responsibility to remove the observer before it dies.
+   */
+  bool AddPrincipalChangeObserver(PrincipalChangeObserver* aObserver);
+
+  /**
+   * Remove an added PrincipalChangeObserver from this track.
+   *
+   * Returns true if it was successfully removed.
+   */
+  bool RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver);
+
 protected:
   virtual ~MediaStreamTrack();
 
   // 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();
 
+  /**
+   * A helper class to MediaStreamTrack::PrincipalChangeObserver.
+   * A MediaStreamTrack currently has the same Principal as its owning
+   * DOMMediaStream. This class is the glue connecting
+   * DOMMediaStream::PrincipalChangeObserver to
+   * MediaStreamTrack::PrincipalChangeObserver.
+   */
+  class PrincipalChangeObserverForwarder :
+    public DOMMediaStream::PrincipalChangeObserver
+  {
+    typedef MediaStreamTrack::PrincipalChangeObserver TrackObserver;
+  public:
+    PrincipalChangeObserverForwarder()
+      : mTrackObserver(nullptr), mTrack(nullptr) {}
+
+    void Init(TrackObserver* aTrackObserver,
+              MediaStreamTrack* aTrack)
+    {
+      MOZ_ASSERT(!mTrackObserver);
+      MOZ_ASSERT(!mTrack);
+
+      mTrackObserver = aTrackObserver;
+      mTrack = aTrack;
+    }
+
+    TrackObserver* Observer() { return mTrackObserver; }
+
+    void PrincipalChanged(DOMMediaStream* aMediaStream) override
+    {
+      mTrackObserver->PrincipalChanged(mTrack);
+    }
+  private:
+    // The observer a user of MediaStreamTrack added to its track.
+    TrackObserver* mTrackObserver;
+
+    // mTrack owns us.
+    MediaStreamTrack* mTrack;
+  };
+  nsTArray<PrincipalChangeObserverForwarder> mPrincipalChangeObservers;
+
   RefPtr<DOMMediaStream> mOwningStream;
   TrackID mTrackID;
   TrackID mInputTrackID;
   const RefPtr<MediaStreamTrackSource> mSource;
   RefPtr<MediaStreamTrack> mOriginalTrack;
   nsString mID;
   bool mEnded;
   bool mEnabled;
--- a/dom/media/imagecapture/CaptureTask.cpp
+++ b/dom/media/imagecapture/CaptureTask.cpp
@@ -47,41 +47,41 @@ CaptureTask::TaskComplete(already_AddRef
   return rv;
 }
 
 void
 CaptureTask::AttachStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<dom::VideoStreamTrack> track = mImageCapture->GetVideoStreamTrack();
+  dom::VideoStreamTrack* track = mImageCapture->GetVideoStreamTrack();
+  track->AddPrincipalChangeObserver(this);
 
   RefPtr<DOMMediaStream> domStream = track->GetStream();
-  domStream->AddPrincipalChangeObserver(this);
-
+  MOZ_RELEASE_ASSERT(domStream);
   RefPtr<MediaStream> stream = domStream->GetPlaybackStream();
   stream->AddListener(this);
 }
 
 void
 CaptureTask::DetachStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<dom::VideoStreamTrack> track = mImageCapture->GetVideoStreamTrack();
+  dom::VideoStreamTrack* track = mImageCapture->GetVideoStreamTrack();
+  track->RemovePrincipalChangeObserver(this);
 
-  RefPtr<DOMMediaStream> domStream = track->GetStream();
-  domStream->RemovePrincipalChangeObserver(this);
-
+  DOMMediaStream* domStream = track->GetStream();
+  MOZ_RELEASE_ASSERT(domStream);
   RefPtr<MediaStream> stream = domStream->GetPlaybackStream();
   stream->RemoveListener(this);
 }
 
 void
-CaptureTask::PrincipalChanged(DOMMediaStream* aMediaStream)
+CaptureTask::PrincipalChanged(dom::MediaStreamTrack* aTrack)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mPrincipalChanged = true;
 }
 
 void
 CaptureTask::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                       StreamTime aTrackOffset,
--- a/dom/media/imagecapture/CaptureTask.h
+++ b/dom/media/imagecapture/CaptureTask.h
@@ -2,18 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #ifndef CAPTURETASK_H
 #define CAPTURETASK_H
 
-#include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
+#include "mozilla/dom/MediaStreamTrack.h"
 
 namespace mozilla {
 
 namespace dom {
 class Blob;
 class ImageCapture;
 } // namespace dom
 
@@ -23,32 +23,32 @@ class ImageCapture;
  * class into MediaStream and retrieves an image in MediaStreamGraph thread.
  * Once the image is retrieved, it will be sent to ImageEncoder and the encoded
  * blob will be sent out via encoder callback in main thread.
  *
  * CaptureTask holds a reference of ImageCapture to ensure ImageCapture won't be
  * released during the period of the capturing process described above.
  */
 class CaptureTask : public MediaStreamListener,
-                    public DOMMediaStream::PrincipalChangeObserver
+                    public dom::MediaStreamTrack::PrincipalChangeObserver
 {
 public:
   // MediaStreamListener methods.
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         StreamTime aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia,
                                         MediaStream* aInputStream,
                                         TrackID aInputTrackID) override;
 
   virtual void NotifyEvent(MediaStreamGraph* aGraph,
                            MediaStreamGraphEvent aEvent) override;
 
-  // DOMMediaStream::PrincipalChangeObserver method.
-  virtual void PrincipalChanged(DOMMediaStream* aMediaStream) override;
+  // MediaStreamTrack::PrincipalChangeObserver method.
+  virtual void PrincipalChanged(dom::MediaStreamTrack* aTrack) override;
 
   // CaptureTask methods.
 
   // It is called when aBlob is ready to post back to script in company with
   // aRv == NS_OK. If aRv is not NS_OK, it will post an error event to script.
   //
   // Note:
   //   this function should be called on main thread.
--- a/dom/media/imagecapture/ImageCapture.cpp
+++ b/dom/media/imagecapture/ImageCapture.cpp
@@ -76,30 +76,29 @@ ImageCapture::GetVideoStreamTrack() cons
 }
 
 nsresult
 ImageCapture::TakePhotoByMediaEngine()
 {
   // Callback for TakPhoto(), it also monitor the principal. If principal
   // changes, it returns PHOTO_ERROR with security error.
   class TakePhotoCallback : public MediaEnginePhotoCallback,
-                            public DOMMediaStream::PrincipalChangeObserver
+                            public MediaStreamTrack::PrincipalChangeObserver
   {
   public:
     TakePhotoCallback(VideoStreamTrack* aVideoTrack, ImageCapture* aImageCapture)
       : mVideoTrack(aVideoTrack)
       , mImageCapture(aImageCapture)
       , mPrincipalChanged(false)
     {
       MOZ_ASSERT(NS_IsMainThread());
-      MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
-      mVideoTrack->GetStream()->AddPrincipalChangeObserver(this);
+      mVideoTrack->AddPrincipalChangeObserver(this);
     }
 
-    void PrincipalChanged(DOMMediaStream* aMediaStream) override
+    void PrincipalChanged(MediaStreamTrack* aMediaStream) override
     {
       mPrincipalChanged = true;
     }
 
     nsresult PhotoComplete(already_AddRefed<Blob> aBlob) override
     {
       RefPtr<Blob> blob = aBlob;
 
@@ -113,18 +112,17 @@ ImageCapture::TakePhotoByMediaEngine()
     {
       return mImageCapture->PostErrorEvent(ImageCaptureError::PHOTO_ERROR, aRv);
     }
 
   protected:
     ~TakePhotoCallback()
     {
       MOZ_ASSERT(NS_IsMainThread());
-      MOZ_RELEASE_ASSERT(mVideoTrack->GetStream());
-      mVideoTrack->GetStream()->RemovePrincipalChangeObserver(this);
+      mVideoTrack->RemovePrincipalChangeObserver(this);
     }
 
     RefPtr<VideoStreamTrack> mVideoTrack;
     RefPtr<ImageCapture> mImageCapture;
     bool mPrincipalChanged;
   };
 
   RefPtr<MediaEnginePhotoCallback> callback =
@@ -211,21 +209,17 @@ ImageCapture::PostErrorEvent(uint16_t aE
   return DispatchTrustedEvent(event);
 }
 
 bool
 ImageCapture::CheckPrincipal()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<DOMMediaStream> ms = mVideoStreamTrack->GetStream();
-  if (!ms) {
-    return false;
-  }
-  nsCOMPtr<nsIPrincipal> principal = ms->GetPrincipal();
+  nsCOMPtr<nsIPrincipal> principal = mVideoStreamTrack->GetPrincipal();
 
   if (!GetOwner()) {
     return false;
   }
   nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
   if (!doc || !principal) {
     return false;
   }