Bug 1295921 - P2: Mark element tainted when DrawImage is used. r?jwwang, mattwoodrow draft
authorDan Glastonbury <dglastonbury@mozilla.com>
Mon, 29 Aug 2016 16:25:22 +1000
changeset 450822 f7de938ddd61070049bdceee1adb10faf41efd91
parent 450821 016e870f2a6adc2847d0594827c02ccc844e527e
child 450823 de02b8d96f9d6a1525fa7e0c81613e26e6445750
push id38957
push userbmo:dglastonbury@mozilla.com
push dateMon, 19 Dec 2016 02:15:56 +0000
reviewersjwwang, mattwoodrow
bugs1295921
milestone53.0a1
Bug 1295921 - P2: Mark element tainted when DrawImage is used. r?jwwang, mattwoodrow Mark video element as tainted (stored on the decoder owned by video element) when the video is used as source to drawImage() on canvas. MozReview-Commit-ID: DkDgXflTN49
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
layout/base/nsLayoutUtils.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1461,16 +1461,37 @@ HTMLMediaElement::SetVisible(bool aVisib
 {
   if (!mDecoder) {
     return;
   }
 
   mDecoder->SetForcedHidden(!aVisible);
 }
 
+layers::Image*
+HTMLMediaElement::GetCurrentImage()
+{
+  // Mark the decoder owned by the element as tainted so that the video decode
+  // isn't suspended.
+  mHasSuspendTaint = true;
+  if (mDecoder) {
+    mDecoder->SetSuspendTaint(true);
+  }
+
+  // TODO(djg): In a follow-up patch, handle case when video decode is
+  // suspended.
+  ImageContainer* container = GetImageContainer();
+  if (!container) {
+    return nullptr;
+  }
+
+  AutoLockImage lockImage(container);
+  return lockImage.GetImage();
+}
+
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::GetSrcObject() const
 {
   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetPlaybackStream(),
                "MediaStream should have been set up properly");
   RefPtr<DOMMediaStream> stream = mSrcAttrStream;
   return stream.forget();
 }
@@ -3574,16 +3595,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mWaitingForKey(NOT_WAITING_FOR_KEY),
     mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
     mAudioChannel(AudioChannelService::GetDefaultAudioChannel()),
     mDisableVideo(false),
     mHasUserInteraction(false),
     mFirstFrameLoaded(false),
     mDefaultPlaybackStartPosition(0.0),
     mIsAudioTrackAudible(false),
+    mHasSuspendTaint(false),
     mVisibilityState(Visibility::APPROXIMATELY_NONVISIBLE),
     mErrorSink(new ErrorSink(this)),
     mAudioChannelWrapper(new AudioChannelAgentCallback(this, mAudioChannel))
 {
   ErrorResult rv;
 
   double defaultVolume = Preferences::GetFloat("media.default_volume", 1.0);
   SetVolume(defaultVolume, rv);
@@ -4608,16 +4630,19 @@ nsresult HTMLMediaElement::FinishDecoder
 
   if (mChannelLoader) {
     mChannelLoader->Done();
     mChannelLoader = nullptr;
   }
 
   AddMediaElementToURITable();
 
+  // Notify the decoder of suspend taint.
+  mDecoder->SetSuspendTaint(mHasSuspendTaint);
+
   // We may want to suspend the new stream now.
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   if (!mPaused) {
     SetPlayedOrSeeked(true);
     if (!mPausedForInactiveDocumentOrChannel) {
       rv = mDecoder->Play();
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -602,16 +602,24 @@ public:
   // Returns a string describing the state of the media player internal
   // data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   void MozDumpDebugInfo();
 
   void SetVisible(bool aVisible);
 
+  // Synchronously, return the next video frame and mark the element
+  // unable to participate in decode suspending.
+  //
+  // This function is synchronous for cases where decoding has been suspended
+  // and JS needs a frame to use in, eg., nsLayoutUtils::SurfaceFromElement()
+  // via drawImage().
+  layers::Image* GetCurrentImage();
+
   already_AddRefed<DOMMediaStream> GetSrcObject() const;
   void SetSrcObject(DOMMediaStream& aValue);
   void SetSrcObject(DOMMediaStream* aValue);
 
   // TODO: remove prefixed versions soon (1183495).
   already_AddRefed<DOMMediaStream> GetMozSrcObject() const;
   void SetMozSrcObject(DOMMediaStream& aValue);
   void SetMozSrcObject(DOMMediaStream* aValue);
@@ -1237,17 +1245,17 @@ protected:
   class nsResolveOrRejectPendingPlayPromisesRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // Open unsupported types media with the external app when the media element
   // triggers play() after loaded fail. eg. preload the data before start play.
   void OpenUnsupportedMediaWithExternalAppIfNeeded() const;
-  
+
   // This method moves the mPendingPlayPromises into a temperate object. So the
   // mPendingPlayPromises is cleared after this method call.
   nsTArray<RefPtr<Promise>> TakePendingPlayPromises();
 
   // This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
   // and queues a task to resolve them.
   void AsyncResolvePendingPlayPromises();
 
@@ -1680,25 +1688,29 @@ private:
   // Media elements also have a default playback start position, which must
   // initially be set to zero seconds. This time is used to allow the element to
   // be seeked even before the media is loaded.
   double mDefaultPlaybackStartPosition;
 
   // True if the audio track is not silent.
   bool mIsAudioTrackAudible;
 
+  // True if media element has been marked as 'tainted' and can't
+  // participate in video decoder suspending.
+  bool mHasSuspendTaint;
+
   Visibility mVisibilityState;
 
   UniquePtr<ErrorSink> mErrorSink;
 
   // This wrapper will handle all audio channel related stuffs, eg. the operations
   // of tab audio indicator, Fennec's media control.
   // Note: mAudioChannelWrapper might be null after GC happened.
   RefPtr<AudioChannelAgentCallback> mAudioChannelWrapper;
-  
+
   // A list of pending play promises. The elements are pushed during the play()
   // method call and are resolved/rejected during further playback steps.
   nsTArray<RefPtr<Promise>> mPendingPlayPromises;
 
   // A list of already-dispatched but not yet run
   // nsResolveOrRejectPendingPlayPromisesRunners.
   // Runners whose Run() method is called remove themselves from this list.
   // We keep track of these because the load algorithm resolves/rejects all
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7234,23 +7234,17 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi
     return result;
   }
 
   // If it doesn't have a principal, just bail
   nsCOMPtr<nsIPrincipal> principal = aElement->GetCurrentVideoPrincipal();
   if (!principal)
     return result;
 
-  ImageContainer* container = aElement->GetImageContainer();
-  if (!container)
-    return result;
-
-  AutoLockImage lockImage(container);
-
-  result.mLayersImage = lockImage.GetImage();
+  result.mLayersImage = aElement->GetCurrentImage();
   if (!result.mLayersImage)
     return result;
 
   if (aTarget) {
     // They gave us a DrawTarget to optimize for, so even though we have a layers::Image,
     // we should unconditionally grab a SourceSurface and try to optimize it.
     result.mSourceSurface = result.mLayersImage->GetAsSourceSurface();
     if (!result.mSourceSurface)