Bug 1208371 - Change HTMLMediaElement video sinks to check principal for video only tracks. r?mt draft
authorAndreas Pehrson <pehrsons@gmail.com>
Tue, 26 Jan 2016 15:36:52 +0800
changeset 342135 30970d7ea3df4930afaab72b2390293472c23727
parent 342134 e38700f246f8924cc7c4635aef4d9f18fd21c268
child 342136 0192824ef8e7eee8a84a96f7e774307bf81436ff
push id13352
push userpehrsons@gmail.com
push dateFri, 18 Mar 2016 13:49:47 +0000
reviewersmt
bugs1208371
milestone47.0a1
Bug 1208371 - Change HTMLMediaElement video sinks to check principal for video only tracks. r?mt MozReview-Commit-ID: KGbyJDgpBOn
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/ImageBitmap.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
layout/base/nsLayoutUtils.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4499,17 +4499,17 @@ CanvasRenderingContext2D::DrawImage(cons
     uint16_t readyState;
     if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
         readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
       // still loading, just return
       return;
     }
 
     // If it doesn't have a principal, just bail
-    nsCOMPtr<nsIPrincipal> principal = video->GetCurrentPrincipal();
+    nsCOMPtr<nsIPrincipal> principal = video->GetCurrentVideoPrincipal();
     if (!principal) {
       aError.Throw(NS_ERROR_NOT_AVAILABLE);
       return;
     }
 
     mozilla::layers::ImageContainer* container = video->GetImageContainer();
     if (!container) {
       aError.Throw(NS_ERROR_NOT_AVAILABLE);
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -622,17 +622,17 @@ ImageBitmap::CreateInternal(nsIGlobalObj
   // Check ready state.
   // Cannot be HTMLMediaElement::HAVE_NOTHING or HTMLMediaElement::HAVE_METADATA.
   if (aVideoEl.ReadyState() <= HTMLMediaElement::HAVE_METADATA) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   // Check security.
-  nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentPrincipal();
+  nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
   bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
   if (!CheckSecurityForHTMLElements(false, CORSUsed, principal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Create ImageBitmap.
   ImageContainer *container = aVideoEl.GetImageContainer();
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3362,16 +3362,19 @@ void HTMLMediaElement::SetupSrcMediaStre
   // will continue to fire events at this element and alter its track list.
   // That's simpler than delaying the events, but probably confusing...
   ConstructMediaTracks();
 
   mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this));
   mMediaStreamTrackListener = new MediaStreamTrackListener(this);
   mSrcStream->RegisterTrackListener(mMediaStreamTrackListener);
 
+  mSrcStream->AddPrincipalChangeObserver(this);
+  mSrcStreamVideoPrincipal = mSrcStream->GetVideoPrincipal();
+
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
   ChangeDelayLoadStatus(false);
   CheckAutoplayDataReady();
 
   // FirstFrameLoaded() will be called when the stream has current data.
 }
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
@@ -3387,16 +3390,19 @@ void HTMLMediaElement::EndSrcMediaStream
     }
     mMediaStreamSizeListener->Forget();
     mMediaStreamSizeListener = nullptr;
   }
 
   mSrcStream->UnregisterTrackListener(mMediaStreamTrackListener);
   mMediaStreamTrackListener = nullptr;
 
+  mSrcStream->RemovePrincipalChangeObserver(this);
+  mSrcStreamVideoPrincipal = nullptr;
+
   mSrcStream = nullptr;
 }
 
 static already_AddRefed<AudioTrack>
 CreateAudioTrack(AudioStreamTrack* aStreamTrack)
 {
   nsAutoString id;
   nsAutoString label;
@@ -4170,16 +4176,37 @@ VideoFrameContainer* HTMLMediaElement::G
   }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS));
 
   return mVideoFrameContainer;
 }
 
+void
+HTMLMediaElement::PrincipalChanged(DOMMediaStream* aStream)
+{
+  LOG(LogLevel::Info, ("HTMLMediaElement %p Stream principal changed.", this));
+  nsContentUtils::CombineResourcePrincipals(&mSrcStreamVideoPrincipal,
+                                            aStream->GetVideoPrincipal());
+
+  LOG(LogLevel::Debug, ("HTMLMediaElement %p Stream video principal changed to "
+                        "%p. Waiting for it to reach VideoFrameContainer before "
+                        "setting.", this, aStream->GetVideoPrincipal()));
+  if (mVideoFrameContainer) {
+    UpdateSrcStreamVideoPrincipal(aStream->GetVideoPrincipal());
+  }
+}
+
+void
+HTMLMediaElement::UpdateSrcStreamVideoPrincipal(nsIPrincipal* aPrincipal)
+{
+  mSrcStreamVideoPrincipal = aPrincipal;
+}
+
 nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName)
 {
   LOG_EVENT(LogLevel::Debug, ("%p Dispatching event %s", this,
                           NS_ConvertUTF16toUTF8(aName).get()));
 
   // Save events that occur while in the bfcache. These will be dispatched
   // if the page comes out of the bfcache.
   if (mEventDeliveryPaused) {
@@ -4263,17 +4290,29 @@ bool HTMLMediaElement::IsPlaybackEnded()
 }
 
 already_AddRefed<nsIPrincipal> HTMLMediaElement::GetCurrentPrincipal()
 {
   if (mDecoder) {
     return mDecoder->GetCurrentPrincipal();
   }
   if (mSrcStream) {
-    RefPtr<nsIPrincipal> principal = mSrcStream->GetPrincipal();
+    nsCOMPtr<nsIPrincipal> principal = mSrcStream->GetPrincipal();
+    return principal.forget();
+  }
+  return nullptr;
+}
+
+already_AddRefed<nsIPrincipal> HTMLMediaElement::GetCurrentVideoPrincipal()
+{
+  if (mDecoder) {
+    return mDecoder->GetCurrentPrincipal();
+  }
+  if (mSrcStream) {
+    nsCOMPtr<nsIPrincipal> principal = mSrcStreamVideoPrincipal;
     return principal.forget();
   }
   return nullptr;
 }
 
 void HTMLMediaElement::NotifyDecoderPrincipalChanged()
 {
   RefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -19,16 +19,17 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrackManager.h"
 #include "MediaDecoder.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeys.h"
 #endif
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
+#include "PrincipalChangeObserver.h"
 
 // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
@@ -71,17 +72,18 @@ class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIDOMHTMLMediaElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
-                         public nsIAudioChannelAgentCallback
+                         public nsIAudioChannelAgentCallback,
+                         public PrincipalChangeObserver<DOMMediaStream>
 {
   friend AutoNotifyAudioChannelAgent;
 
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
   typedef mozilla::MediaStream MediaStream;
@@ -221,16 +223,21 @@ public:
 
   virtual bool IsHidden() const final override;
 
   // Called by the media decoder and the video frame to get the
   // ImageContainer containing the video data.
   B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override;
   layers::ImageContainer* GetImageContainer();
 
+  // From PrincipalChangeObserver<DOMMediaStream>.
+  void PrincipalChanged(DOMMediaStream* aStream) override;
+
+  void UpdateSrcStreamVideoPrincipal(nsIPrincipal* aPrincipal);
+
   // Dispatch events
   virtual nsresult DispatchAsyncEvent(const nsAString& aName) final override;
 
   // Triggers a recomputation of readyState.
   void UpdateReadyState() override { UpdateReadyStateInternal(); }
 
   // Dispatch events that were raised while in the bfcache
   nsresult DispatchPendingMediaEvents();
@@ -259,16 +266,23 @@ public:
   // http://www.whatwg.org/specs/web-apps/current-work/#ended
   bool IsPlaybackEnded() const;
 
   // principal of the currently playing resource. Anything accessing the contents
   // of this element must have a principal that subsumes this principal.
   // Returns null if nothing is playing.
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
 
+  // Principal of the currently playing video resource. Anything accessing the
+  // image container of this element must have a principal that subsumes this
+  // principal. If there are no live video tracks but content has been rendered
+  // to the image container, we return the last video principal we had. Should
+  // the image container be empty with no live video tracks, we return nullptr.
+  already_AddRefed<nsIPrincipal> GetCurrentVideoPrincipal();
+
   // called to notify that the principal of the decoder's media resource has changed.
   void NotifyDecoderPrincipalChanged() final override;
 
   // An interface for observing principal changes on the media elements
   // MediaDecoder. This will also be notified if the active CORSMode changes.
   class DecoderPrincipalChangeObserver
   {
   public:
@@ -1485,16 +1499,20 @@ protected:
   RefPtr<TextTrackManager> mTextTrackManager;
 
   RefPtr<AudioTrackList> mAudioTrackList;
 
   RefPtr<VideoTrackList> mVideoTrackList;
 
   RefPtr<MediaStreamTrackListener> mMediaStreamTrackListener;
 
+  // The principal guarding mVideoFrameContainer access when playing a
+  // MediaStream.
+  nsCOMPtr<nsIPrincipal> mSrcStreamVideoPrincipal;
+
   enum ElementInTreeState {
     // The MediaElement is not in the DOM tree now.
     ELEMENT_NOT_INTREE,
     // The MediaElement is in the DOM tree now.
     ELEMENT_INTREE,
     // The MediaElement is not in the DOM tree now but had been binded to the
     // tree before.
     ELEMENT_NOT_INTREE_HAD_INTREE
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -871,21 +871,31 @@ DOMMediaStream::PrincipalChanged(MediaSt
                        this, aTrack));
   RecomputePrincipal();
 }
 
 void
 DOMMediaStream::RecomputePrincipal()
 {
   nsCOMPtr<nsIPrincipal> previousPrincipal = mPrincipal.forget();
+  nsCOMPtr<nsIPrincipal> previousVideoPrincipal = mVideoPrincipal.forget();
   for (const RefPtr<TrackPort>& info : mTracks) {
+    if (info->GetTrack()->Ended()) {
+      continue;
+    }
     nsContentUtils::CombineResourcePrincipals(&mPrincipal,
                                               info->GetTrack()->GetPrincipal());
+    if (info->GetTrack()->AsVideoStreamTrack()) {
+      nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal,
+                                                info->GetTrack()->GetPrincipal());
+    }
   }
-  if (previousPrincipal != mPrincipal) {
+
+  if (previousPrincipal != mPrincipal ||
+      previousVideoPrincipal != mVideoPrincipal) {
     NotifyPrincipalChanged();
   }
 }
 
 void
 DOMMediaStream::NotifyPrincipalChanged()
 {
   if (!mPrincipal) {
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -441,16 +441,22 @@ public:
   bool IsFinished();
   /**
    * Returns a principal indicating who may access this stream. The stream contents
    * can only be accessed by principals subsuming this principal.
    */
   nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
   /**
+   * Returns a principal indicating who may access video data of this stream.
+   * The video principal will be a combination of all live video tracks.
+   */
+  nsIPrincipal* GetVideoPrincipal() { return mVideoPrincipal; }
+
+  /**
    * These are used in WebRTC.  A peerIdentity constrained MediaStream cannot be sent
    * across the network to anything other than a peer with the provided identity.
    * If this is set, then mPrincipal should be an instance of nsNullPrincipal.
    */
   PeerIdentity* GetPeerIdentity() const { return mPeerIdentity; }
   void SetPeerIdentity(PeerIdentity* aPeerIdentity)
   {
     mPeerIdentity = aPeerIdentity;
@@ -671,16 +677,19 @@ protected:
   // The track listeners subscribe to changes in this stream's track set.
   nsTArray<RefPtr<TrackListener>> mTrackListeners;
 
 private:
   void NotifyPrincipalChanged();
   // Principal identifying who may access the collected contents of this stream.
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  // Video principal is used by video element as access is requested to its
+  // image data.
+  nsCOMPtr<nsIPrincipal> mVideoPrincipal;
   nsTArray<dom::PrincipalChangeObserver<DOMMediaStream>*> mPrincipalChangeObservers;
   // this is used in gUM and WebRTC to identify peers that this stream
   // is allowed to be sent to
   nsAutoPtr<PeerIdentity> mPeerIdentity;
   CORSMode mCORSMode;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMMediaStream,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7252,17 +7252,17 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi
   if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
       (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
        readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
     result.mIsStillLoading = true;
     return result;
   }
 
   // If it doesn't have a principal, just bail
-  nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentPrincipal();
+  nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentVideoPrincipal();
   if (!principal)
     return result;
 
   ImageContainer* container = aElement->GetImageContainer();
   if (!container)
     return result;
 
   AutoLockImage lockImage(container);