# HG changeset patch # Parent 91d25ecf8d43bcf6fe4d7065edb50d7836a2dba3 # User Matt Woodrow Bug 505115 - Part 14b - Layout support for -moz-transform-style diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -758,25 +758,39 @@ static PRBool IsContentLEQ(nsDisplayItem // These GetUnderlyingFrame calls return non-null because we're only used // in sorting return nsLayoutUtils::CompareTreePosition( aItem1->GetUnderlyingFrame()->GetContent(), aItem2->GetUnderlyingFrame()->GetContent(), static_cast(aClosure)) <= 0; } +static PRBool IsZPositionLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, + void* aClosure) { + if (!aItem1->GetUnderlyingFrame()->Preserves3D() || + !aItem1->GetUnderlyingFrame()->Preserves3D()) { + return IsContentLEQ(aItem1, aItem2, aClosure); + } + + nsIFrame* ancestor; + gfx3DMatrix matrix1 = aItem1->GetUnderlyingFrame()->GetTransformMatrix(&ancestor); + gfx3DMatrix matrix2 = aItem2->GetUnderlyingFrame()->GetTransformMatrix(&ancestor); + + return matrix1._43 < matrix2._43; +} + static PRBool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2, void* aClosure) { // These GetUnderlyingFrame calls return non-null because we're only used // in sorting. Note that we can't just take the difference of the two // z-indices here, because that might overflow a 32-bit int. PRInt32 index1 = nsLayoutUtils::GetZIndex(aItem1->GetUnderlyingFrame()); PRInt32 index2 = nsLayoutUtils::GetZIndex(aItem2->GetUnderlyingFrame()); if (index1 == index2) - return IsContentLEQ(aItem1, aItem2, aClosure); + return IsZPositionLEQ(aItem1, aItem2, aClosure); return index1 < index2; } void nsDisplayList::ExplodeAnonymousChildLists(nsDisplayListBuilder* aBuilder) { // See if there's anything to do PRBool anyAnonymousItems = PR_FALSE; nsDisplayItem* i; for (i = GetBottom(); i != nsnull; i = i->GetAbove()) { @@ -813,16 +827,21 @@ void nsDisplayList::SortByZOrder(nsDispl Sort(aBuilder, IsZOrderLEQ, aCommonAncestor); } void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor) { Sort(aBuilder, IsContentLEQ, aCommonAncestor); } +void nsDisplayList::SortByZPosition(nsDisplayListBuilder* aBuilder, + nsIContent* aCommonAncestor) { + Sort(aBuilder, IsZPositionLEQ, aCommonAncestor); +} + void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder, SortLEQ aCmp, void* aClosure) { ExplodeAnonymousChildLists(aBuilder); ::Sort(this, Count(), aCmp, aClosure); } PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) { @@ -2412,21 +2431,30 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigi /* Wraps up the -moz-transform matrix in a change-of-basis matrix pair that * translates from local coordinate space to transform coordinate space, then * hands it back. */ gfx3DMatrix nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame, const nsPoint &aOrigin, float aFactor, - const nsRect* aBoundsOverride) + const nsRect* aBoundsOverride, + nsIFrame** aOutAncestor) { NS_PRECONDITION(aFrame, "Cannot get transform matrix for a null frame!"); - NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), - "Cannot get transform matrix if frame isn't transformed!"); + //NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), + // "Cannot get transform matrix if frame isn't transformed!"); + + if (aOutAncestor) { + *aOutAncestor = aFrame->GetParent(); + } + + if (!aFrame->GetStyleDisplay()->HasTransform()) { + return GetResultingTransformMatrix(aFrame->GetParent(), aOrigin - aFrame->GetPosition(), aFactor, nsnull, aOutAncestor); + } /* Account for the -moz-transform-origin property by translating the * 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), @@ -2436,29 +2464,46 @@ nsDisplayTransform::GetResultingTransfor * bounds of the frame. */ const nsStyleDisplay* disp = aFrame->GetStyleDisplay(); nsRect bounds = (aBoundsOverride ? *aBoundsOverride : nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); /* Get the matrix, then change its basis to factor in the origin. */ PRBool dummy; - gfx3DMatrix result = - nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform, - aFrame->GetStyleContext(), - aFrame->PresContext(), - dummy, bounds, aFactor); + gfx3DMatrix result; + if (disp->mSpecifiedTransform) { + result = nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform, + aFrame->GetStyleContext(), + aFrame->PresContext(), + dummy, bounds, aFactor); + } else { + NS_ASSERTION(aFrame->Preserves3DChildren(), + "If we don't have a transform, then we must be at least preserving transforms of our children"); + } const nsStyleDisplay* parentDisp = aFrame->GetParent()->GetStyleDisplay(); if (nsLayoutUtils::Are3DTransformsEnabled() && parentDisp->mChildPerspective != 0.0) { gfx3DMatrix perspective; perspective._34 = -1.0 / parentDisp->mChildPerspective; result = result * nsLayoutUtils::ChangeMatrixBasis(toPerspectiveOrigin, perspective); } + + if (aFrame->Preserves3D() && nsLayoutUtils::Are3DTransformsEnabled()) { + // Include the transform set on our parent + NS_ASSERTION(aFrame->GetParent() && + aFrame->GetParent()->IsTransformed() && + aFrame->GetParent()->Preserves3DChildren(), + "Preserve3D mismatch!"); + gfx3DMatrix parent = GetResultingTransformMatrix(aFrame->GetParent(), aOrigin - aFrame->GetPosition(), + aFactor, nsnull, aOutAncestor); + return nsLayoutUtils::ChangeMatrixBasis(newOrigin + toMozOrigin, result) * parent; + } + return nsLayoutUtils::ChangeMatrixBasis (newOrigin + toMozOrigin, result); } const gfx3DMatrix& nsDisplayTransform::GetTransform(float aFactor) { if (mTransform.IsIdentity() || mCachedFactor != aFactor) { @@ -2726,50 +2771,50 @@ nsDisplayTransform::TryMerge(nsDisplayLi * rectangle. */ nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds, const nsIFrame* aFrame, const nsPoint &aOrigin, const nsRect* aBoundsOverride) { NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); - NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), - "Cannot transform a rectangle if there's no transformation!"); + //NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), + // "Cannot transform a rectangle if there's no transformation!"); float factor = nsPresContext::AppUnitsPerCSSPixel(); return nsLayoutUtils::MatrixTransformRect (aUntransformedBounds, GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), factor); } nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds, const nsIFrame* aFrame, const nsPoint &aOrigin, const nsRect* aBoundsOverride) { NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); - NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), - "Cannot transform a rectangle if there's no transformation!"); + //NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), + // "Cannot transform a rectangle if there's no transformation!"); float factor = nsPresContext::AppUnitsPerCSSPixel(); return nsLayoutUtils::MatrixTransformRectOut (aUntransformedBounds, GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride), factor); } PRBool nsDisplayTransform::UntransformRect(const nsRect &aUntransformedBounds, const nsIFrame* aFrame, const nsPoint &aOrigin, nsRect* aOutRect) { NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!"); - NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), - "Cannot transform a rectangle if there's no transformation!"); + //NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(), + // "Cannot transform a rectangle if there's no transformation!"); /* Grab the matrix. If the transform is degenerate, just hand back the * empty rect. */ float factor = nsPresContext::AppUnitsPerCSSPixel(); gfx3DMatrix matrix = GetResultingTransformMatrix(aFrame, aOrigin, factor, nsnull); if (matrix.IsSingular()) @@ -2778,16 +2823,21 @@ PRBool nsDisplayTransform::UntransformRe gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.y, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.width, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.height, factor)); /* We want to untransform the matrix, so invert the transformation first! */ result = matrix.Inverse().ProjectRectBounds(result); + if (!(result.width >= nscoord_MIN && result.width <= nscoord_MAX && + result.height >= nscoord_MIN && result.height <= nscoord_MAX)) { + return PR_FALSE; + } + *aOutRect = nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), NSFloatPixelsToAppUnits(float(result.y), factor), NSFloatPixelsToAppUnits(float(result.width), factor), NSFloatPixelsToAppUnits(float(result.height), factor)); return PR_TRUE; } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1004,16 +1004,18 @@ public: * GetUnderlyingFrame() on each item. z-index is ignored. * @param aCommonAncestor a common ancestor of all the content elements * associated with the display items, for speeding up tree order * checks, or nsnull if not known; it's only a hint, if it is not an * ancestor of some elements, then we lose performance but not correctness */ void SortByContentOrder(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor); + void SortByZPosition(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor); + /** * Generic stable sort. Take care, because some of the items might be nsDisplayLists * themselves. * aCmp(item1, item2) should return true if item1 <= item2. We sort the items * into increasing order. */ typedef PRBool (* SortLEQ)(nsDisplayItem* aItem1, nsDisplayItem* aItem2, void* aClosure); @@ -2075,16 +2077,23 @@ public: */ nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, nsDisplayList *aList) : nsDisplayItem(aBuilder, aFrame), mStoredList(aBuilder, aFrame, aList) { MOZ_COUNT_CTOR(nsDisplayTransform); } + nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame, + nsDisplayItem *aItem) : + nsDisplayItem(aBuilder, aFrame), mStoredList(aBuilder, aFrame, aItem) + { + MOZ_COUNT_CTOR(nsDisplayTransform); + } + #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplayTransform() { MOZ_COUNT_DTOR(nsDisplayTransform); } #endif NS_DISPLAY_DECL_NAME("nsDisplayTransform", TYPE_TRANSFORM); @@ -2179,17 +2188,18 @@ public: * computation will use the value of GetFrameBoundsForTransform(aFrame) * for the frame's bounding rectangle. Otherwise, it will use the * value of aBoundsOverride. This is mostly for internal use and in * most cases you will not need to specify a value. */ static gfx3DMatrix GetResultingTransformMatrix(const nsIFrame* aFrame, const nsPoint& aOrigin, float aFactor, - const nsRect* aBoundsOverride = nsnull); + const nsRect* aBoundsOverride = nsnull, + nsIFrame** aOutAncestor = nsnull); private: nsDisplayWrapList mStoredList; gfx3DMatrix mTransform; float mCachedFactor; }; /** diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -174,23 +174,23 @@ PrintDisplayListTo(nsDisplayListBuilder* } default: break; } nscolor color; nsRect vis = i->GetVisibleRect(); nsDisplayList* list = i->GetList(); nsRegion opaque; + if (i->GetType() == nsDisplayItem::TYPE_TRANSFORM) { + nsDisplayTransform* t = static_cast(i); + list = t->GetStoredList()->GetList(); + } if (!list || list->DidComputeVisibility()) { opaque = i->GetOpaqueRegion(aBuilder); } - if (i->GetType() == nsDisplayItem::TYPE_TRANSFORM) { - nsDisplayTransform* t = static_cast(i); - list = t->GetStoredList()->GetList(); - } fprintf(aOutput, "%s %p(%s) (%d,%d,%d,%d)(%d,%d,%d,%d)%s%s", i->Name(), (void*)f, NS_ConvertUTF16toUTF8(fName).get(), rect.x, rect.y, rect.width, rect.height, vis.x, vis.y, vis.width, vis.height, opaque.IsEmpty() ? "" : " opaque", i->IsUniform(aBuilder, &color) ? " uniform" : ""); if (f) { PRUint32 key = i->GetPerFrameKey(); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -200,16 +200,24 @@ PRBool nsFrame::GetShowEventTargetFrameB * means that you cannot perform logging before then. */ static PRLogModuleInfo* gLogModule; static PRLogModuleInfo* gStyleVerifyTreeLogModuleInfo; static PRBool gStyleVerifyTreeEnable = PRBool(0x55); +static PRBool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, + const nsIFrame* aFrame, + const nsStyleDisplay* aDisp, nsRect* aRect); + +static PRBool ApplyAbsPosClipping(nsDisplayListBuilder* aBuilder, + const nsStyleDisplay* aDisp, const nsIFrame* aFrame, + nsRect* aRect); + PRBool nsFrame::GetVerifyStyleTreeEnable() { if (gStyleVerifyTreeEnable == PRBool(0x55)) { if (nsnull == gStyleVerifyTreeLogModuleInfo) { gStyleVerifyTreeLogModuleInfo = PR_NewLogModule("styleverifytree"); gStyleVerifyTreeEnable = 0 != gStyleVerifyTreeLogModuleInfo->level; } @@ -721,16 +729,35 @@ nsIFrame::GetPaddingRect() const PRBool nsIFrame::IsTransformed() const { return (mState & NS_FRAME_MAY_BE_TRANSFORMED) && GetStyleDisplay()->HasTransform(); } +PRBool +nsIFrame::Preserves3DChildren() const +{ + return GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D && IsTransformed(); +} + +PRBool +nsIFrame::Preserves3D() const +{ + if (!GetParent() || !GetParent()->Preserves3DChildren() || !IsTransformed()) { + return PR_FALSE; + } + + nsRect temp; + return (!ApplyOverflowClipping(nsnull, this, GetStyleDisplay(), &temp) && + !ApplyAbsPosClipping(nsnull, GetStyleDisplay(), this, &temp) && + !nsSVGIntegrationUtils::UsingEffectsForFrame(this)); +} + nsRect nsIFrame::GetContentRectRelativeToSelf() const { nsMargin bp(GetUsedBorderAndPadding()); ApplySkipSides(bp); nsRect r(0, 0, mRect.width, mRect.height); r.Deflate(bp); return r; @@ -1167,17 +1194,17 @@ nsFrame::DisplayBorderBackgroundOutline( NS_ENSURE_SUCCESS(rv, rv); } return DisplayOutlineUnconditional(aBuilder, aLists); } PRBool nsIFrame::GetAbsPosClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, - const nsSize& aSize) + const nsSize& aSize) const { NS_PRECONDITION(aRect, "Must have aRect out parameter"); if (!aDisp->IsAbsolutelyPositioned() || !(aDisp->mClipFlags & NS_STYLE_CLIP_RECT)) return PR_FALSE; *aRect = aDisp->mClip; @@ -1186,30 +1213,32 @@ nsIFrame::GetAbsPosClipRect(const nsStyl } if (NS_STYLE_CLIP_BOTTOM_AUTO & aDisp->mClipFlags) { aRect->height = aSize.height - aRect->y; } return PR_TRUE; } static PRBool ApplyAbsPosClipping(nsDisplayListBuilder* aBuilder, - const nsStyleDisplay* aDisp, nsIFrame* aFrame, + const nsStyleDisplay* aDisp, const nsIFrame* aFrame, nsRect* aRect) { if (!aFrame->GetAbsPosClipRect(aDisp, aRect, aFrame->GetSize())) return PR_FALSE; - *aRect += aBuilder->ToReferenceFrame(aFrame); + if (aBuilder) { + *aRect += aBuilder->ToReferenceFrame(aFrame); + } return PR_TRUE; } /** * Returns PR_TRUE if aFrame is overflow:hidden and we should interpret * that as -moz-hidden-unscrollable. */ -static inline PRBool ApplyOverflowHiddenClipping(nsIFrame* aFrame, +static inline PRBool ApplyOverflowHiddenClipping(const nsIFrame* aFrame, const nsStyleDisplay* aDisp) { if (aDisp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) return PR_FALSE; nsIAtom* type = aFrame->GetType(); // REVIEW: these are the frame types that call IsTableClip and set up // clipping. Actually there were also table rows and the inner table frame @@ -1218,17 +1247,17 @@ static inline PRBool ApplyOverflowHidden // but we should actually clip at tableFrame (as per discussion with Hixie and // bz). return type == nsGkAtoms::tableFrame || type == nsGkAtoms::tableCellFrame || type == nsGkAtoms::bcTableCellFrame; } static PRBool ApplyOverflowClipping(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame, + const nsIFrame* aFrame, const nsStyleDisplay* aDisp, nsRect* aRect) { // REVIEW: from nsContainerFrame.cpp SyncFrameViewGeometryDependentProperties, // except that that function used the border-edge for // -moz-hidden-unscrollable which I don't think is correct... Also I've // changed -moz-hidden-unscrollable to apply to any kind of frame. // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table // frames, and any non-visible value for blocks in a paginated context). @@ -1238,18 +1267,20 @@ static PRBool ApplyOverflowClipping(nsDi PRBool clip = aDisp->mOverflowX == NS_STYLE_OVERFLOW_CLIP; if (!clip) return PR_FALSE; // We allow -moz-hidden-unscrollable to apply to any kind of frame. This // is required by comboboxes which make their display text (an inline frame) // have clipping. } - *aRect = aFrame->GetPaddingRect() - aFrame->GetPosition() + - aBuilder->ToReferenceFrame(aFrame); + *aRect = aFrame->GetPaddingRect() - aFrame->GetPosition(); + if (aBuilder) { + *aRect += aBuilder->ToReferenceFrame(aFrame); + } return PR_TRUE; } class nsOverflowClipWrapper : public nsDisplayWrapper { public: /** * Create a wrapper to apply overflow clipping for aContainer. @@ -1389,16 +1420,43 @@ DisplayDebugBorders(nsDisplayListBuilder aFrame->PresContext()->PresShell()->GetDrawEventTargetFrame() == aFrame) { aLists.Outlines()->AppendNewToTop(new (aBuilder) nsDisplayGeneric(aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder", nsDisplayItem::TYPE_EVENT_TARGET_BORDER)); } } #endif +static nsresult +WrapPreserve3DList(nsIFrame *aFrame, nsDisplayListBuilder *aBuilder, nsDisplayList *aList) +{ + nsresult rv = NS_OK; + nsDisplayList newList; + while (nsDisplayItem *item = aList->RemoveBottom()) { + if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && item->GetUnderlyingFrame()->GetParent()->Preserves3DChildren()) { + NS_ASSERTION(item->GetUnderlyingFrame()->Preserves3D(), ""); + } else if (item->GetType() == nsDisplayItem::TYPE_WRAP_LIST && item->GetUnderlyingFrame()->GetParent()->Preserves3DChildren()) { + nsDisplayWrapList *list = static_cast(item); + rv = WrapPreserve3DList(aFrame, aBuilder, list->GetList()); + } else if (item->GetType() == nsDisplayItem::TYPE_OPACITY && item->GetUnderlyingFrame()->GetParent()->Preserves3DChildren()) { + nsDisplayOpacity *opacity = static_cast(item); + rv = WrapPreserve3DList(aFrame, aBuilder, opacity->GetList()); + } else { + item = new (aBuilder) nsDisplayTransform(aBuilder, item->GetUnderlyingFrame(), item); + } + if (NS_FAILED(rv) || !item) + return rv; + + newList.AppendToTop(item); + } + + aList->AppendToTop(&newList); + return NS_OK; +} + nsresult nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, nsDisplayList* aList) { if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; // Replaced elements have their visibility handled here, because @@ -1419,17 +1477,17 @@ nsIFrame::BuildDisplayListForStackingCon PRBool inTransform = aBuilder->IsInTransform(); /* If we're being transformed, we need to invert the matrix transform so that we don't * grab points in the wrong coordinate system! */ if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform()) { /* If we have a complex transform, just grab the entire overflow rect instead. */ - if (!nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0), &dirtyRect)) { + if (Preserves3DChildren() || GetParent()->Preserves3DChildren() || !nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0), &dirtyRect)) { dirtyRect = GetVisualOverflowRectRelativeToSelf(); } inTransform = PR_TRUE; } if (applyAbsPosClipping) { dirtyRect.IntersectRect(dirtyRect, absPosClip - aBuilder->ToReferenceFrame(this)); @@ -1498,16 +1556,17 @@ nsIFrame::BuildDisplayListForStackingCon } break; } // 4: block backgrounds resultList.AppendToTop(set.BlockBorderBackgrounds()); // 5: floats resultList.AppendToTop(set.Floats()); // 7: general content + set.Content()->SortByZPosition(aBuilder, GetContent()); resultList.AppendToTop(set.Content()); // 7.5: outlines, in content tree order. We need to sort by content order // because an element with outline that breaks and has children with outline // might have placed child outline items between its own outline items. // The element's outline items need to all come before any child outline // items. set.Outlines()->SortByContentOrder(aBuilder, GetContent()); #ifdef NS_DEBUG @@ -1545,20 +1604,33 @@ nsIFrame::BuildDisplayListForStackingCon return rv; } /* If we're going to apply a transformation, wrap everything in an * nsDisplayTransform. If there's nothing in the list, don't add anything. */ if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) && disp->HasTransform() && !resultList.IsEmpty()) { - rv = resultList.AppendNewToTop( + if (Preserves3DChildren()) { + rv = WrapPreserve3DList(this, aBuilder, &resultList); + if (NS_FAILED(rv)) + return rv; + + if (resultList.Count() > 1) { + rv = resultList.AppendNewToTop( + new (aBuilder) nsDisplayWrapList(aBuilder, this, &resultList)); + if (NS_FAILED(rv)) + return rv; + } + } else { + rv = resultList.AppendNewToTop( new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList)); - if (NS_FAILED(rv)) - return rv; + if (NS_FAILED(rv)) + return rv; + } } aList->AppendToTop(&resultList); return rv; } nsresult nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, @@ -4456,16 +4528,23 @@ nsIFrame::InvalidateInternalAfterResize( * coordinate space for the frame, and sometimes its in the transformed * coordinate space. If we get it wrong, we'll display incorrectly. Until I * find a better fix for this problem, we'll invalidate the union of the two * rectangles (original rectangle and transformed rectangle). At least one of * these will be correct. * * See bug #452496 for more details. */ + + // Check the transformed flags and remove it + PRBool isTransformed = (aFlags & INVALIDATE_ALREADY_TRANSFORMED); + if (!Preserves3D()) { + aFlags &= ~INVALIDATE_ALREADY_TRANSFORMED; + } + if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) && !(aFlags & INVALIDATE_NO_THEBES_LAYERS)) { // XXX for now I'm going to assume this is in the local coordinate space // This only matters for frames with transforms and retained layers, // which can't happen right now since transforms trigger fallback // rendering and the display items that trigger layers are nested inside // the nsDisplayTransform // XXX need to set INVALIDATE_NO_THEBES_LAYERS for certain kinds of @@ -4473,20 +4552,25 @@ nsIFrame::InvalidateInternalAfterResize( FrameLayerBuilder::InvalidateThebesLayerContents(this, aDamageRect + nsPoint(aX, aY)); // Don't need to invalidate any more Thebes layers aFlags |= INVALIDATE_NO_THEBES_LAYERS; if (aFlags & INVALIDATE_ONLY_THEBES_LAYERS) { return; } } - if (IsTransformed()) { + if (IsTransformed() && !isTransformed) { nsRect newDamageRect; newDamageRect.UnionRect(nsDisplayTransform::TransformRectOut (aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect); + + if (Preserves3D()) { + aFlags |= INVALIDATE_ALREADY_TRANSFORMED; + } + GetParent()-> InvalidateInternal(newDamageRect, aX + mRect.x, aY + mRect.y, this, aFlags); } else GetParent()-> InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aFlags); } @@ -4513,33 +4597,33 @@ nsIFrame::InvalidateInternal(const nsRec gfx3DMatrix nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) { NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); /* If we're transformed, the matrix will be relative to our * cross-doc parent frame. */ - *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this); + //*aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this); /* If we're transformed, we want to hand back the combination * transform/translate matrix that will apply our current transform, then * shift us to our parent. */ if (IsTransformed()) { /* Compute the delta to the parent, which we need because we are converting * coordinates to our parent. */ - NS_ASSERTION(*aOutAncestor, "Cannot transform the viewport frame!"); - nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); + NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), "Cannot transform the viewport frame!"); PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel(); gfx3DMatrix result = nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0), - scaleFactor); + scaleFactor, nsnull, aOutAncestor); + nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor); /* Combine the raw transform with a translation to our parent. */ result *= gfx3DMatrix::Translation (NSAppUnitsToFloatPixels(delta.x, scaleFactor), NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f); return result; } @@ -6574,19 +6658,50 @@ nsIFrame::FinishAndStoreOverflow(nsOverf Properties().Set(nsIFrame::PreTransformBBoxProperty(), new nsRect(aOverflowAreas.VisualOverflow())); /* Since our size might not actually have been computed yet, we need to make sure that we use the * correct dimensions by overriding the stored bounding rectangle with the value the caller has * ensured us we'll use. */ nsRect newBounds(nsPoint(0, 0), aNewSize); // Transform affects both overflow areas. - NS_FOR_FRAME_OVERFLOW_TYPES(otype) { - nsRect& o = aOverflowAreas.Overflow(otype); - o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds); + if (!Preserves3DChildren()) { + NS_FOR_FRAME_OVERFLOW_TYPES(otype) { + nsRect& o = aOverflowAreas.Overflow(otype); + o = nsDisplayTransform::TransformRect(o, this, nsPoint(0, 0), &newBounds); + } + } else { + // When we are preserving 3d we need to iterate over all children separately. + // If the child also preserves 3d then their overflow will already been in our + // coordinate space, otherwise we need to transform. + PRInt32 index = 0; + nsIAtom *childList; + nsRect childVisual; + nsRect childScrollable; + do { + childList = GetAdditionalChildListName(index++); + nsFrameList children = GetChildList(childList); + for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) { + nsIFrame* child = e.get(); + if (child->Preserves3D()) { + childVisual = childVisual.Union(child->GetVisualOverflowRect()); + childScrollable = childScrollable.Union(child->GetScrollableOverflowRect()); + } else { + childVisual = + childVisual.Union(nsDisplayTransform::TransformRect(child->GetVisualOverflowRect(), + this, nsPoint(0,0), &newBounds)); + childScrollable = + childScrollable.Union(nsDisplayTransform::TransformRect(child->GetScrollableOverflowRect(), + this, nsPoint(0,0), &newBounds)); + } + } + } while (childList); + + aOverflowAreas.Overflow(eVisualOverflow) = childVisual; + aOverflowAreas.Overflow(eScrollableOverflow) = childScrollable; } } PRBool visualOverflowChanged = !GetVisualOverflowRect().IsEqualInterior(aOverflowAreas.VisualOverflow()); if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) { SetOverflowAreas(aOverflowAreas); diff --git a/layout/generic/nsFrame.h b/layout/generic/nsFrame.h --- a/layout/generic/nsFrame.h +++ b/layout/generic/nsFrame.h @@ -569,17 +569,17 @@ public: // frame. // If aScrollLock is true, don't break outside scrollframes when looking for a // containing block frame. static PRInt32 GetLineNumber(nsIFrame *aFrame, PRBool aLockScroll, nsIFrame** aContainingBlock = nsnull); // test whether aFrame should apply paginated overflow clipping. - static PRBool ApplyPaginatedOverflowClipping(nsIFrame* aFrame) + static PRBool ApplyPaginatedOverflowClipping(const nsIFrame* aFrame) { // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW // set, then we want to clip our overflow. return aFrame->PresContext()->IsPaginated() && aFrame->GetType() == nsGkAtoms::blockFrame && (aFrame->GetStateBits() & NS_BLOCK_CLIP_PAGINATED_OVERFLOW) != 0; } diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1185,16 +1185,27 @@ public: /** * Returns whether this frame has a transform matrix applied to it. This is true * if we have the -moz-transform property or if we're an SVGForeignObjectFrame. */ virtual PRBool IsTransformed() const; /** + * Returns whether this frame will attempt to preserve the 3d transforms of its + * children. This is a direct indicator of -moz-transform-style: preserve-3d. + */ + virtual PRBool Preserves3DChildren() const; + + /** + * Returns whether this child frame will preserve 3d transforms. + */ + virtual PRBool Preserves3D() const; + + /** * Event handling of GUI events. * * @param aEvent event structure describing the type of event and rge widget * where the event originated * The |point| member of this is in the coordinate system of the * view returned by GetOffsetFromView. * @param aEventStatus a return value indicating whether the event was handled * and whether default processing should be done @@ -2096,17 +2107,18 @@ public: INVALIDATE_CROSS_DOC = 0x02, INVALIDATE_REASON_SCROLL_BLIT = 0x04, INVALIDATE_REASON_SCROLL_REPAINT = 0x08, INVALIDATE_REASON_MASK = INVALIDATE_REASON_SCROLL_BLIT | INVALIDATE_REASON_SCROLL_REPAINT, INVALIDATE_NO_THEBES_LAYERS = 0x10, INVALIDATE_ONLY_THEBES_LAYERS = 0x20, INVALIDATE_EXCLUDE_CURRENT_PAINT = 0x40, - INVALIDATE_NO_UPDATE_LAYER_TREE = 0x80 + INVALIDATE_NO_UPDATE_LAYER_TREE = 0x80, + INVALIDATE_ALREADY_TRANSFORMED = 0x100 }; virtual void InvalidateInternal(const nsRect& aDamageRect, nscoord aOffsetX, nscoord aOffsetY, nsIFrame* aForChild, PRUint32 aFlags); /** * Helper function that funnels an InvalidateInternal request up to the * parent. This function is used so that if MOZ_SVG is not defined, we still @@ -2483,17 +2495,17 @@ NS_PTR_TO_INT32(frame->Properties().Get( /** * Returns PR_TRUE if the frame is absolutely positioned and has a clip * rect set via the 'clip' property. If true, then we also set aRect * to the computed clip rect coordinates relative to this frame's origin. * aRect must not be null! */ PRBool GetAbsPosClipRect(const nsStyleDisplay* aDisp, nsRect* aRect, - const nsSize& aSize); + const nsSize& aSize) const; /** * Check if this frame is focusable and in the current tab order. * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable. * For example, only the selected radio button in a group is in the * tab order, unless the radio group has no selection in which case * all of the visible, non-disabled radio buttons in the group are * in the tab order. On the other hand, all of the visible, non-disabled diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1589,17 +1589,17 @@ struct nsStyleDisplay { PRBool IsTableClip() const { return mOverflowX == NS_STYLE_OVERFLOW_CLIP || (mOverflowX == NS_STYLE_OVERFLOW_HIDDEN && mOverflowY == NS_STYLE_OVERFLOW_HIDDEN); } /* Returns whether the element has the -moz-transform property. */ PRBool HasTransform() const { - return mSpecifiedTransform != nsnull; + return mSpecifiedTransform != nsnull || mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D; } }; struct nsStyleTable { nsStyleTable(void); nsStyleTable(const nsStyleTable& aOther); ~nsStyleTable(void);