Bug 1538969 - Report nsDisplayVideo as being opaque when possible so that we can occlude content behind it. r=jya
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 10 Apr 2019 08:08:26 +0000
changeset 468756 c2b1753a416b3201552108700ecd3d17686d5d4f
parent 468755 3f2ce3a4c4e695b26a2f351a728edb6d33e562e9
child 468757 caff7c30502713c949800eb950d836e3f55e5a65
push id112755
push userdvarga@mozilla.com
push dateWed, 10 Apr 2019 22:06:41 +0000
treeherdermozilla-inbound@606f85641d0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1538969
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1538969 - Report nsDisplayVideo as being opaque when possible so that we can occlude content behind it. r=jya Differential Revision: https://phabricator.services.mozilla.com/D25189
dom/html/HTMLVideoElement.h
gfx/layers/composite/ImageLayerComposite.cpp
layout/generic/nsVideoFrame.cpp
--- a/dom/html/HTMLVideoElement.h
+++ b/dom/html/HTMLVideoElement.h
@@ -100,16 +100,18 @@ class HTMLVideoElement final : public HT
     }
     return 0;
   }
 
   VideoInfo::Rotation RotationDegrees() const {
     return mMediaInfo.mVideo.mRotation;
   }
 
+  bool HasAlpha() const { return mMediaInfo.mVideo.HasAlpha(); }
+
   void GetPoster(nsAString& aValue) {
     GetURIAttr(nsGkAtoms::poster, nullptr, aValue);
   }
   void SetPoster(const nsAString& aValue, ErrorResult& aRv) {
     SetHTMLAttr(nsGkAtoms::poster, aValue, aRv);
   }
 
   uint32_t MozParsedFrames() const;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -128,17 +128,22 @@ void ImageLayerComposite::ComputeEffecti
   ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 }
 
 bool ImageLayerComposite::IsOpaque() {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return false;
   }
 
+  // TODO: Handle ScaleMode::NONE where the image
+  // still covers the whole Layer.
   if (mScaleMode == ScaleMode::STRETCH) {
+    if ((GetContentFlags() & CONTENT_OPAQUE) && !mImageHost->IsOpaque()) {
+      NS_WARNING("Must have an opaque ImageHost if we reported CONTENT_OPAQUE");
+    }
     return mImageHost->IsOpaque();
   }
   return false;
 }
 
 nsIntRegion ImageLayerComposite::GetFullyRenderedRegion() {
   if (!mImageHost || !mImageHost->IsAttached()) {
     return GetShadowVisibleRegion().ToUnknownRegion();
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -225,16 +225,20 @@ already_AddRefed<Layer> nsVideoFrame::Bu
 
   Matrix preTransform = ComputeRotationMatrix(
       destGFXRect.Width(), destGFXRect.Height(), rotationDeg);
 
   Matrix transform = preTransform * Matrix::Translation(p.x, p.y);
 
   layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
   layer->SetScaleToSize(scaleHint, ScaleMode::STRETCH);
+
+  uint32_t flags = element->HasAlpha() ? 0 : Layer::CONTENT_OPAQUE;
+  layer->SetContentFlags(flags);
+
   RefPtr<Layer> result = layer.forget();
   return result.forget();
 }
 
 class DispatchResizeToControls : public Runnable {
  public:
   explicit DispatchResizeToControls(nsIContent* aContent)
       : mozilla::Runnable("DispatchResizeToControls"), mContent(aContent) {}
@@ -464,23 +468,27 @@ class nsDisplayVideo : public nsDisplayI
     // help us. Hence we can ignore the return value from PushImage.
     LayoutDeviceRect rect(destGFXRect.x, destGFXRect.y, destGFXRect.width,
                           destGFXRect.height);
     aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources,
                                          aSc, rect, rect);
     return true;
   }
 
-  // It would be great if we could override GetOpaqueRegion to return nonempty
-  // here, but it's probably not safe to do so in general. Video frames are
-  // updated asynchronously from decoder threads, and it's possible that
-  // we might have an opaque video frame when GetOpaqueRegion is called, but
-  // when we come to paint, the video frame is transparent or has gone
-  // away completely (e.g. because of a decoder error). The problem would
-  // be especially acute if we have off-main-thread rendering.
+  nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+                           bool* aSnap) const override {
+    *aSnap = false;
+
+    HTMLVideoElement* element =
+        static_cast<HTMLVideoElement*>(Frame()->GetContent());
+    if (element->HasAlpha()) {
+      return nsRegion();
+    }
+    return GetBounds(aBuilder, aSnap);
+  }
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override {
     *aSnap = true;
     nsIFrame* f = Frame();
     return f->GetContentRectRelativeToSelf() + ToReferenceFrame();
   }