Bug 1103184 part 1: Honor "object-fit" & "object-position" on <canvas> element. r=roc
authorDaniel Holbert <dholbert@cs.stanford.edu>
Wed, 26 Nov 2014 19:43:36 -0800
changeset 217818 052e9649b6ba9df9e9cfd9619f87f417b8823a12
parent 217817 14f75afaad1448f43068f6a254360675b9297594
child 217819 b5c23a7a6d8c30b453415ab899a404869bdf2184
push id27890
push usercbook@mozilla.com
push dateThu, 27 Nov 2014 11:55:59 +0000
treeherdermozilla-central@8d185a31024e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1103184
milestone36.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 1103184 part 1: Honor "object-fit" & "object-position" on <canvas> element. r=roc
layout/generic/nsHTMLCanvasFrame.cpp
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -7,16 +7,17 @@
 
 #include "nsHTMLCanvasFrame.h"
 
 #include "nsGkAtoms.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "nsDisplayList.h"
 #include "nsLayoutUtils.h"
+#include "nsStyleUtil.h"
 #include "Layers.h"
 #include "ActiveLayerTracker.h"
 
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
@@ -70,22 +71,38 @@ public:
   }
 #endif
 
   NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS)
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) MOZ_OVERRIDE {
     *aSnap = false;
-    nsIFrame* f = Frame();
-    HTMLCanvasElement *canvas =
+    nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
+    HTMLCanvasElement* canvas =
       HTMLCanvasElement::FromContent(f->GetContent());
     nsRegion result;
     if (canvas->GetIsOpaque()) {
-      result = GetBounds(aBuilder, aSnap);
+      // OK, the entire region painted by the canvas is opaque. But what is
+      // that region? It's the canvas's "dest rect" (controlled by the
+      // object-fit/object-position CSS properties), clipped to the container's
+      // content box (which is what GetBounds() returns). So, we grab those
+      // rects and intersect them.
+      nsRect constraintRect = GetBounds(aBuilder, aSnap);
+
+      // Need intrinsic size & ratio, for ComputeObjectDestRect:
+      nsIntSize canvasSize = f->GetCanvasSize();
+      IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSize);
+      nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSize);
+
+      const nsRect destRect =
+        nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+                                             intrinsicSize, intrinsicRatio,
+                                             f->StylePosition());
+      return nsRegion(destRect.Intersect(constraintRect));
     }
     return result;
   }
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) MOZ_OVERRIDE {
     *aSnap = true;
     nsHTMLCanvasFrame* f = static_cast<nsHTMLCanvasFrame*>(Frame());
@@ -301,58 +318,66 @@ nsHTMLCanvasFrame::GetInnerArea() const
 already_AddRefed<Layer>
 nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               nsDisplayItem* aItem,
                               const ContainerLayerParameters& aContainerParameters)
 {
   nsRect area = GetContentRectRelativeToSelf() + aItem->ToReferenceFrame();
   HTMLCanvasElement* element = static_cast<HTMLCanvasElement*>(GetContent());
-  nsIntSize canvasSize = GetCanvasSize();
+  nsIntSize canvasSizeInPx = GetCanvasSize();
 
   nsPresContext* presContext = PresContext();
   element->HandlePrintCallback(presContext->Type());
 
-  if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
+  if (canvasSizeInPx.width <= 0 || canvasSizeInPx.height <= 0 || area.IsEmpty())
     return nullptr;
 
   CanvasLayer* oldLayer = static_cast<CanvasLayer*>
     (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, aItem));
   nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aBuilder, oldLayer, aManager);
   if (!layer)
     return nullptr;
 
-  gfxRect r = gfxRect(presContext->AppUnitsToGfxUnits(area.x),
-                      presContext->AppUnitsToGfxUnits(area.y),
-                      presContext->AppUnitsToGfxUnits(area.width),
-                      presContext->AppUnitsToGfxUnits(area.height));
+  IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
+  nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
+
+  nsRect dest =
+    nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio,
+                                         StylePosition());
+
+  gfxRect destGFXRect = presContext->AppUnitsToGfxUnits(dest);
 
   // Transform the canvas into the right place
-  gfxPoint p = r.TopLeft() + aContainerParameters.mOffset;
+  gfxPoint p = destGFXRect.TopLeft() + aContainerParameters.mOffset;
   Matrix transform = Matrix::Translation(p.x, p.y);
-  transform.PreScale(r.Width() / canvasSize.width,
-                     r.Height() / canvasSize.height);
+  transform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
+                     destGFXRect.Height() / canvasSizeInPx.height);
   layer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
   layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
 
   return layer.forget();
 }
 
 void
 nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   if (!IsVisibleForPainting(aBuilder))
     return;
 
   DisplayBorderBackgroundOutline(aBuilder, aLists);
 
+  uint32_t clipFlags =
+    nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition()) ?
+    0 : DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT;
+
   DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
-    clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT);
+    clip(aBuilder, this, clipFlags);
 
   aLists.Content()->AppendNewToTop(
     new (aBuilder) nsDisplayCanvas(aBuilder, this));
 
   DisplaySelectionOverlay(aBuilder, aLists.Content(),
                           nsISelectionDisplay::DISPLAY_IMAGES);
 }