diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index dd85ac58700..bfe8c785b60 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2449,6 +2449,8 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, NS_PRECONDITION(aFrame, "Can't get delta for a null frame!"); NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), "Can't get a delta for an untransformed frame!"); + NS_PRECONDITION(aFrame->GetParentStyleContextFrame(), + "Can't get delta without a style parent!"); /* For both of the coordinates, if the value of -moz-perspective-origin is a * percentage, it's relative to the size of the frame. Otherwise, if it's @@ -2457,9 +2459,10 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, //TODO: Should this be using our bounds or the parent's bounds? // How do we handle aBoundsOverride in the latter case? + nsIFrame* parent = aFrame->GetParentStyleContextFrame(); const nsStyleDisplay* display = aFrame->GetParent()->GetStyleDisplay(); nsRect boundingRect = (aBoundsOverride ? *aBoundsOverride : - nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); + nsDisplayTransform::GetFrameBoundsForTransform(parent)); /* Allows us to access named variables by index. */ gfxPoint3D result; @@ -2487,7 +2490,12 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame, } } - return result; + nsPoint parentOffset = aFrame->GetOffsetTo(parent); + gfxPoint3D gfxOffset(NSAppUnitsToFloatPixels(parentOffset.x, aFactor), + NSAppUnitsToFloatPixels(parentOffset.y, aFactor), + 0); + + return result - gfxOffset; } /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that @@ -2511,7 +2519,6 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, * coordinate space to the new origin. */ gfxPoint3D toMozOrigin = GetDeltaToMozTransformOrigin(aFrame, aFactor, aBoundsOverride); - gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aFactor, aBoundsOverride); gfxPoint3D newOrigin = gfxPoint3D(NSAppUnitsToFloatPixels(aOrigin.x, aFactor), NSAppUnitsToFloatPixels(aOrigin.y, aFactor), 0.0f); @@ -2552,6 +2559,7 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, /* At the point when perspective is applied, we have been translated to the transform origin. * The translation to the perspective origin is the difference between these values. */ + gfxPoint3D toPerspectiveOrigin = GetDeltaToMozPerspectiveOrigin(aFrame, aFactor, aBoundsOverride); result = result * nsLayoutUtils::ChangeMatrixBasis(toPerspectiveOrigin - toMozOrigin, perspective); } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index ae17d828775..23669adc1ce 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -975,6 +975,26 @@ nsIFrame::Preserves3D() const return true; } +bool +nsIFrame::HasPerspective() const +{ + if (!IsTransformed()) { + return false; + } + const nsStyleDisplay* parentDisp = nsnull; + nsStyleContext* parentStyleContext = GetStyleContext()->GetParent(); + if (parentStyleContext) { + parentDisp = parentStyleContext->GetStyleDisplay(); + } + + if (parentDisp && + parentDisp->mChildPerspective.GetUnit() == eStyleUnit_Coord && + parentDisp->mChildPerspective.GetCoordValue() > 0.0) { + return true; + } + return false; +} + nsRect nsIFrame::GetContentRectRelativeToSelf() const { @@ -6634,7 +6654,7 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, nsRect bounds(nsPoint(0, 0), aNewSize); // Store the passed in overflow area if we are a preserve-3d frame, // and it's not just the frame bounds. - if (Preserves3D() && (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || + if ((Preserves3D() || HasPerspective()) && (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) { nsOverflowAreas* initial = static_cast(Properties().Get(nsIFrame::InitialOverflowProperty())); @@ -6749,6 +6769,8 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, } if (Preserves3DChildren()) { ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds); + } else if (HasPerspective()) { + RecomputePerspectiveChildrenOverflow(this, &newBounds); } } else { Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty()); @@ -6808,6 +6830,39 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas, return anyOverflowChanged; } +void +nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, const nsRect* aBounds) +{ + // Children may check our size when getting our transform, make sure it's valid. + nsSize oldSize = GetSize(); + if (aBounds) { + SetSize(aBounds->Size()); + } + nsIFrame::ChildListIterator lists(this); + for (; !lists.IsDone(); lists.Next()) { + nsFrameList::Enumerator childFrames(lists.CurrentList()); + for (; !childFrames.AtEnd(); childFrames.Next()) { + nsIFrame* child = childFrames.get(); + if (child->HasPerspective()) { + nsOverflowAreas* overflow = + static_cast(child->Properties().Get(nsIFrame::InitialOverflowProperty())); + nsRect bounds(nsPoint(0, 0), child->GetSize()); + if (overflow) { + child->FinishAndStoreOverflow(*overflow, bounds.Size()); + } else { + nsOverflowAreas boundsOverflow; + boundsOverflow.SetAllTo(bounds); + child->FinishAndStoreOverflow(boundsOverflow, bounds.Size()); + } + } else if (child->GetParentStyleContextFrame() != aStartFrame) { + child->RecomputePerspectiveChildrenOverflow(aStartFrame, nsnull); + } + } + } + // Restore our old size just in case something depends on this elesewhere. + SetSize(oldSize); +} + /* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on * the mRect value for their parents (since we use their transform, and transform * depends on this for transform-origin etc). These weren't necessarily correct diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index af430d5b7ec..e2ebe88c495 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1247,9 +1247,13 @@ public: */ bool Preserves3D() const; + bool HasPerspective() const; + // Calculate the overflow size of all child frames, taking preserve-3d into account void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds); + void RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame, const nsRect* aBounds); + /** * Event handling of GUI events. * diff --git a/layout/reftests/transform-3d/perspective-origin-2-ref.html b/layout/reftests/transform-3d/perspective-origin-2-ref.html new file mode 100644 index 00000000000..7c9e0d58ae8 --- /dev/null +++ b/layout/reftests/transform-3d/perspective-origin-2-ref.html @@ -0,0 +1,31 @@ + + + + + +
+
+
+ + diff --git a/layout/reftests/transform-3d/perspective-origin-2a.html b/layout/reftests/transform-3d/perspective-origin-2a.html new file mode 100644 index 00000000000..ff809381677 --- /dev/null +++ b/layout/reftests/transform-3d/perspective-origin-2a.html @@ -0,0 +1,31 @@ + + + + + +
+
+
+ + diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index c481a9b7cac..050155715ae 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -33,6 +33,7 @@ fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == preserve3d-1a.html preser == backface-visibility-1b.html about:blank != perspective-origin-1a.html rotatex-perspective-1a.html == perspective-origin-1b.html perspective-origin-1a.html +== perspective-origin-2a.html perspective-origin-2-ref.html != sorting-1a.html sorting-1-ref.html # Parallel planes, different z depth == sorting-2a.html sorting-2-ref.html