diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 27ce7cd5d3b9..0df2afce2e30 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1561,7 +1561,10 @@ SetVisibleRegionForLayer(Layer* aLayer, const nsIntRegion& aLayerVisibleRegion, // for the layer, so it doesn't really matter what we do here gfxRect itemVisible(aRestrictToRect.x, aRestrictToRect.y, aRestrictToRect.width, aRestrictToRect.height); - gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible); + nsIntRect childBounds = aLayerVisibleRegion.GetBounds(); + gfxRect childGfxBounds(childBounds.x, childBounds.y, + childBounds.width, childBounds.height); + gfxRect layerVisible = transform.UntransformBounds(itemVisible, childGfxBounds); layerVisible.RoundOut(); nsIntRect visibleRect; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 8835b620c26f..fc1119235a5e 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1400,7 +1400,7 @@ void nsDisplayList::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, if (aRect.width != 1 || aRect.height != 1) { point = aRect.Center(); } - temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(point))); + temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point))); writeFrames = &temp[temp.Length() - 1].mFrames; } } else { @@ -4460,7 +4460,7 @@ bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder, * If we can't untransform, take the entire overflow rect */ nsRect untransformedVisibleRect; if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || - !UntransformVisibleRect(&untransformedVisibleRect)) + !UntransformVisibleRect(aBuilder, &untransformedVisibleRect)) { untransformedVisibleRect = mFrame->GetVisualOverflowRectRelativeToSelf(); } @@ -4501,14 +4501,24 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, * Thus we have to invert the matrix, which normally does * the reverse operation (e.g. regular->transformed) */ + bool snap; + nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); + gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), + NSAppUnitsToFloatPixels(childBounds.y, factor), + NSAppUnitsToFloatPixels(childBounds.width, factor), + NSAppUnitsToFloatPixels(childBounds.height, factor)); /* Now, apply the transform and pass it down the channel. */ nsRect resultingRect; if (aRect.width == 1 && aRect.height == 1) { // Magic width/height indicating we're hit testing a point, not a rect - gfxPoint point = matrix.Inverse().ProjectPoint( - gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor), - NSAppUnitsToFloatPixels(aRect.y, factor))); + gfxPoint point; + if (!matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aRect.x, factor), + NSAppUnitsToFloatPixels(aRect.y, factor)), + childGfxBounds, + &point)) { + return; + } resultingRect = nsRect(NSFloatPixelsToAppUnits(float(point.x), factor), NSFloatPixelsToAppUnits(float(point.y), factor), @@ -4520,7 +4530,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, NSAppUnitsToFloatPixels(aRect.width, factor), NSAppUnitsToFloatPixels(aRect.height, factor)); - gfxRect rect = matrix.Inverse().ProjectRectBounds(originalRect);; + gfxRect rect = matrix.UntransformBounds(originalRect, childGfxBounds); resultingRect = nsRect(NSFloatPixelsToAppUnits(float(rect.X()), factor), NSFloatPixelsToAppUnits(float(rect.Y()), factor), @@ -4528,6 +4538,10 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, NSFloatPixelsToAppUnits(float(rect.Height()), factor)); } + if (resultingRect.IsEmpty()) { + return; + } + #ifdef DEBUG_HIT printf("Frame: %p\n", dynamic_cast(mFrame)); @@ -4547,7 +4561,7 @@ void nsDisplayTransform::HitTest(nsDisplayListBuilder *aBuilder, } float -nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint) +nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint) { // GetTransform always operates in dev pixels. float factor = mFrame->PresContext()->AppUnitsPerDevPixel(); @@ -4555,9 +4569,19 @@ nsDisplayTransform::GetHitDepthAtPoint(const nsPoint& aPoint) NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!"); - gfxPoint point = - matrix.Inverse().ProjectPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor), - NSAppUnitsToFloatPixels(aPoint.y, factor))); + bool snap; + nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); + gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), + NSAppUnitsToFloatPixels(childBounds.y, factor), + NSAppUnitsToFloatPixels(childBounds.width, factor), + NSAppUnitsToFloatPixels(childBounds.height, factor)); + + gfxPoint point; + DebugOnly result = matrix.UntransformPoint(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, factor), + NSAppUnitsToFloatPixels(aPoint.y, factor)), + childGfxBounds, + &point); + NS_ASSERTION(result, "Why are we trying to get the depth for a point we didn't hit?"); gfxPoint3D transformed = matrix.Transform3D(gfxPoint3D(point.x, point.y, 0)); return transformed.z; @@ -4608,7 +4632,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, // updated extremely cheaply, without invalidating any other // content. if (ShouldPrerenderTransformedContent(aBuilder, mFrame) || - !UntransformVisibleRect(&untransformedVisible)) { + !UntransformVisibleRect(aBuilder, &untransformedVisible)) { return nsRegion(); } @@ -4632,7 +4656,7 @@ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, bool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) { nsRect untransformedVisible; - if (!UntransformVisibleRect(&untransformedVisible)) { + if (!UntransformVisibleRect(aBuilder, &untransformedVisible)) { return false; } const gfx3DMatrix& matrix = GetTransform(); @@ -4728,7 +4752,8 @@ nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds, factor); } -bool nsDisplayTransform::UntransformVisibleRect(nsRect *aOutRect) +bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder, + nsRect *aOutRect) { const gfx3DMatrix& matrix = GetTransform(); if (matrix.IsSingular()) @@ -4741,8 +4766,14 @@ bool nsDisplayTransform::UntransformVisibleRect(nsRect *aOutRect) NSAppUnitsToFloatPixels(mVisibleRect.width, factor), NSAppUnitsToFloatPixels(mVisibleRect.height, factor)); - /* We want to untransform the matrix, so invert the transformation first! */ - result = matrix.Inverse().ProjectRectBounds(result); + bool snap; + nsRect childBounds = mStoredList.GetBounds(aBuilder, &snap); + gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), + NSAppUnitsToFloatPixels(childBounds.y, factor), + NSAppUnitsToFloatPixels(childBounds.width, factor), + NSAppUnitsToFloatPixels(childBounds.height, factor)); + + result = matrix.UntransformBounds(result, childGfxBounds); *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 9ad2b73cf83b..38859afcac25 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3051,7 +3051,7 @@ public: const gfx3DMatrix& GetTransform(); - float GetHitDepthAtPoint(const nsPoint& aPoint); + float GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint); /** * TransformRect takes in as parameters a rectangle (in aFrame's coordinate @@ -3084,7 +3084,8 @@ public: /* UntransformRect is like TransformRect, except that it inverts the * transform. */ - bool UntransformVisibleRect(nsRect* aOutRect); + bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder, + nsRect* aOutRect); static gfxPoint3D GetDeltaToTransformOrigin(const nsIFrame* aFrame, float aAppUnitsPerPixel, diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 303da3d56dcc..b3deeb3b5bbb 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1833,13 +1833,21 @@ nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame, return true; } -static gfxPoint +static bool TransformGfxPointFromAncestor(nsIFrame *aFrame, const gfxPoint &aPoint, - nsIFrame *aAncestor) + nsIFrame *aAncestor, + gfxPoint* aOut) { gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor); - return ctm.Inverse().ProjectPoint(aPoint); + + float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); + nsRect childBounds = aFrame->GetVisualOverflowRectRelativeToSelf(); + gfxRect childGfxBounds(NSAppUnitsToFloatPixels(childBounds.x, factor), + NSAppUnitsToFloatPixels(childBounds.y, factor), + NSAppUnitsToFloatPixels(childBounds.width, factor), + NSAppUnitsToFloatPixels(childBounds.height, factor)); + return ctm.UntransformPoint(aPoint, childGfxBounds, aOut); } static gfxRect @@ -1881,10 +1889,14 @@ nsLayoutUtils::TransformAncestorPointToFrame(nsIFrame* aFrame, NSAppUnitsToFloatPixels(aPoint.y, factor)); if (text) { - result = TransformGfxPointFromAncestor(text, result, aAncestor); + if (!TransformGfxPointFromAncestor(text, result, aAncestor, &result)) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } result = text->TransformFramePointToTextChild(result, aFrame); } else { - result = TransformGfxPointFromAncestor(aFrame, result, nullptr); + if (!TransformGfxPointFromAncestor(aFrame, result, nullptr, &result)) { + return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + } } return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor), diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 482433e14b33..d715a8b725c4 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -698,6 +698,9 @@ public: /** * Transform aPoint relative to aAncestor down to the coordinate system of * aFrame. + * + * Returns nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE) if no equivalent + * point exists in the child frame (can happen with projective transforms). */ static nsPoint TransformAncestorPointToFrame(nsIFrame* aFrame, const nsPoint& aPoint,