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
--- 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)