diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index c7d21f7e896..7e7930c5ade 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -1457,10 +1457,13 @@ public: bool Extend3DContext() { return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT; } - bool Is3DContextLeaf() { - return !Extend3DContext() && GetParent() && + bool Combines3DTransformWithAncestors() { + return GetParent() && reinterpret_cast(GetParent())->Extend3DContext(); } + bool Is3DContextLeaf() { + return !Extend3DContext() && Combines3DTransformWithAncestors(); + } /** * It is true if the user can see the back of the layer and the * backface is hidden. The compositor should skip the layer if the diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index e9ea650babd..3f7cae7c7c0 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -864,6 +864,10 @@ InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer) if (!clipRect) { return; } + MOZ_ASSERT(!aLayer->Extend3DContext() || + !aLayer->Combines3DTransformWithAncestors(), + "Layers in a preserve 3D context have no clip" + " except leaves and the estabisher!"); Layer* parent = aLayer->GetParent(); Matrix4x4 transform3d = diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 12047963b43..441074446a5 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -5414,9 +5414,11 @@ nsDisplayItem::LayerState nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager, const ContainerLayerParameters& aParameters) { - // If the transform is 3d, or the layer takes part in preserve-3d sorting - // then we *always* want this to be an active layer. - if (!GetTransform().Is2D() || mFrame->Combines3DTransformWithAncestors()) { + // If the transform is 3d, the layer takes part in preserve-3d + // sorting, or the layer is a separator then we *always* want this + // to be an active layer. + if (!GetTransform().Is2D() || mFrame->Combines3DTransformWithAncestors() || + mIsTransformSeparator) { return LAYER_ACTIVE_FORCE; } // Here we check if the *post-transform* bounds of this item are big enough @@ -5587,7 +5589,7 @@ nsDisplayTransform::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) return mBounds; } - if (mFrame->Extend3DContext()) { + if (mFrame->Extend3DContext() && !mIsTransformSeparator) { return nsRect(); } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 5880018226d..4d3fe20837a 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -37,6 +37,7 @@ class nsIContent; class nsRenderingContext; +class nsDisplayList; class nsDisplayTableItem; class nsISelection; class nsDisplayLayerEventRegions; diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 132f2b15056..7a7408e2f71 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1902,6 +1902,39 @@ CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) } } +/** + * True if aDescendant participates the context aAncestor participating. + */ +static bool +Participate3DContextFrame(nsIFrame* aAncestor, nsIFrame* aDescendant) { + MOZ_ASSERT(aAncestor != aDescendant); + MOZ_ASSERT(aAncestor->Extend3DContext()); + nsIFrame* frame; + for (frame = nsLayoutUtils::GetCrossDocParentFrame(aDescendant); + frame && aAncestor != frame; + frame = nsLayoutUtils::GetCrossDocParentFrame(frame)) { + if (!frame->Extend3DContext()) { + return false; + } + } + MOZ_ASSERT(frame == aAncestor); + return true; +} + +static void +WrapSeparatorTransform(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + nsRect& aDirtyRect, + nsDisplayList* aSource, nsDisplayList* aTarget, + int aIndex) { + if (!aSource->IsEmpty()) { + nsDisplayTransform *sepIdItem = + new (aBuilder) nsDisplayTransform(aBuilder, aFrame, aSource, + aDirtyRect, Matrix4x4(), aIndex); + sepIdItem->SetNoExtendContext(); + aTarget->AppendToTop(sepIdItem); + } +} + void nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, @@ -2161,6 +2194,36 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, * we find all the correct children. */ if (isTransformed && !resultList.IsEmpty()) { + if (!resultList.IsEmpty() && Extend3DContext()) { + // Install dummy nsDisplayTransform as a leaf containing + // descendants not participating this 3D rendering context. + nsDisplayList nonparticipants; + nsDisplayList participants; + int index = 1; + + while (nsDisplayItem* item = resultList.RemoveBottom()) { + if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && + Participate3DContextFrame(this, item->Frame())) { + // The frame of this item participates the same 3D context. + WrapSeparatorTransform(aBuilder, this, dirtyRect, + &nonparticipants, &participants, index++); + participants.AppendToTop(item); + } else { + // The frame of the item doesn't participate the current + // context, or has no transform. + // + // For items participating but not transformed, they are add + // to nonparticipants to get a separator layer for handling + // clips, if there is, on an intermediate surface. + // \see ContainerLayer::DefaultComputeEffectiveTransforms(). + nonparticipants.AppendToTop(item); + } + } + WrapSeparatorTransform(aBuilder, this, dirtyRect, + &nonparticipants, &participants, index++); + resultList.AppendToTop(&participants); + } + // Restore clip state now so nsDisplayTransform is clipped properly. clipState.Restore(); // Revert to the dirtyrect coming in from the parent, without our transform @@ -2177,51 +2240,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, nsDisplayTransform *transformItem = new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect); resultList.AppendNewToTop(transformItem); - - /* - * Create an additional transform item as a separator layer - * between current and parent's 3D context if necessary. - * - * Separator layers avoid improperly exteding 3D context by - * children. - */ - { - bool needAdditionalTransform = false; - if (Extend3DContext()) { - if (outerReferenceFrame->Extend3DContext()) { - for (nsIFrame *f = nsLayoutUtils::GetCrossDocParentFrame(this); - f && f != outerReferenceFrame && !f->IsTransformed(); - f = nsLayoutUtils::GetCrossDocParentFrame(f)) { - if (!f->Extend3DContext()) { - // The first one with transform in it's 3D context chain, - // and it is different 3D context with the outer reference - // frame. - needAdditionalTransform = true; - break; - } - } - } - } else if (outerReferenceFrame->Extend3DContext() && - outerReferenceFrame != nsLayoutUtils::GetCrossDocParentFrame(this)) { - // The content should be transformed and drawn on a buffer, - // then tranformed and drawn again for outerReferenceFrame. - // So, a separator layer is required. - needAdditionalTransform = true; - } - if (needAdditionalTransform) { - nsRect sepDirty = dirtyRectOutsideTransform; - // The separator item is with ID transform and is out of this - // frame, so it is in the coordination of the outer reference - // frame. Here translate the dirty rect back. - sepDirty.MoveBy(toOuterReferenceFrame); - nsDisplayTransform *sepIdItem = - new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, - sepDirty, - Matrix4x4(), 1); - sepIdItem->SetNoExtendContext(); - resultList.AppendNewToTop(sepIdItem); - } - } } /* If we're doing VR rendering, then we need to wrap everything in a nsDisplayVR diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 298b071a079..88fb1beb5d7 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1719,7 +1719,7 @@ skip-if(B2G||Mulet) == 729143-1.html 729143-1-ref.html # Initial mulet triage: p == 731521-1.html 731521-1-ref.html needs-focus == 731726-1.html 731726-1-ref.html == 735481-1.html 735481-1-ref.html -== 745934-1.html 745934-1-ref.html +fuzzy-if(cocoaWidget,1,300000) == 745934-1.html 745934-1-ref.html == 748692-1a.html 748692-1-ref.html == 748692-1b.html 748692-1-ref.html skip-if(B2G||Mulet) == 748803-1.html 748803-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop diff --git a/layout/reftests/outline/reftest.list b/layout/reftests/outline/reftest.list index a9c4d6ca4c8..445c4aeb5da 100644 --- a/layout/reftests/outline/reftest.list +++ b/layout/reftests/outline/reftest.list @@ -1,7 +1,7 @@ == outline-and-box-shadow.html outline-and-box-shadow-ref.html == outline-and-3d-transform-1a.html outline-and-3d-transform-1-ref.html == outline-and-3d-transform-1b.html outline-and-3d-transform-1-ref.html -fuzzy-if(Android,255,356) fuzzy-if(d2d,16,96) fuzzy-if(cocoaWidget,255,120) fuzzy-if(B2G,128,60) fuzzy-if(gtkWidget,128,120) fuzzy-if(winWidget,255,120) == outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html +fuzzy-if(Mulet||gtkWidget,136,120) fuzzy-if(Android,255,356) fuzzy-if(d2d,16,96) fuzzy-if(cocoaWidget,255,120) fuzzy-if(B2G,128,60) fuzzy-if(winWidget,255,215) == outline-and-3d-transform-2.html outline-and-3d-transform-2-ref.html == outline-overflow-block-abspos.html outline-overflow-block-ref.html == outline-overflow-block-float.html outline-overflow-block-ref.html == outline-overflow-inlineblock-abspos.html outline-overflow-inlineblock-ref.html diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index aa07ae75bcd..ed3f1e4e200 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -18,7 +18,7 @@ fuzzy-if(gtkWidget||winWidget,8,376) fuzzy-if(Android,8,441) fuzzy-if(cocoaWidge == preserve3d-2b.html preserve3d-2-ref.html == preserve3d-2c.html preserve3d-2-ref.html == preserve3d-2d.html preserve3d-2-ref.html -fuzzy(4,100) == preserve3d-3a.html preserve3d-3-ref.html +== preserve3d-3a.html preserve3d-3-ref.html skip-if(B2G||Mulet) == preserve3d-4a.html green-rect.html # Initial mulet triage: parity with B2G/B2G Desktop fuzzy-if(gtkWidget,4,200) fuzzy-if(Android&&AndroidVersion>=15,4,300) == preserve3d-5a.html preserve3d-5-ref.html == scale3d-z.html scalez-1-ref.html