Bug 1208371 - Change HTMLMediaElement video sinks to check principal for video only tracks. r?mt
MozReview-Commit-ID: KGbyJDgpBOn
--- 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);