[mq]: about-comment.diff
authorThinker K.F. Li <thinker@codemud.net>
Wed, 13 May 2015 16:08:40 +0800
changeset 463345 72b6fc114a92107c62e05575976aec4a27825138
parent 462399 d38d00288df9b4260c06e7b428d3a6b425fb01d9
child 463346 93e3238e7d46adf7cc63bada76216762fda7255b
push id68897
push usertlee@mozilla.com
push dateWed, 13 May 2015 08:09:23 +0000
treeherdertry@6e745037066b [default view] [failures only]
milestone40.0a1
[mq]: about-comment.diff
gfx/layers/LayerSorter.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicContainerLayer.cpp
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/client/ClientContainerLayer.h
gfx/layers/client/ClientLayerManager.h
gfx/layers/client/ClientPaintedLayer.cpp
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/composite/PaintedLayerComposite.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutDebugger.cpp
layout/generic/nsFrame.cpp
layout/reftests/transform-3d/preserves3d-nested-ref.html
layout/reftests/transform-3d/preserves3d-nested.html
--- a/gfx/layers/LayerSorter.cpp
+++ b/gfx/layers/LayerSorter.cpp
@@ -53,54 +53,24 @@ static gfxFloat RecoverZDepth(const gfx3
 
     if (!d) {
         return 0;
     }
 
     return n/d;
 }
 
-static gfx3DMatrix
-GetAccumulatedTransformPreserve3DSlow(Layer* aLayer) {
-  gfx::Matrix4x4 transform = aLayer->GetTransform();
-  for (Layer *ancester = aLayer->GetParent();
-       ancester && ancester->Extend3DContext();
-       ancester = ancester->GetParent()) {
-    transform *= ancester->GetTransform();
-  }
-  return To3DMatrix(transform);
-}
-
 /**
  * Get accumulated transform in preserves3d chain.
  */
 static gfx3DMatrix
-GetAccumulatedTransformPreverse3D(Layer* aLayer) {
-  // Context root is the parent of the container that closest to root
-  // of consecutive preserves3d containers.
-  Layer *ctxroot;
-  int depth = 0;
-  for (ctxroot = aLayer->GetParent();
-       ctxroot && ctxroot->Extend3DContext();
-       ctxroot = ctxroot->GetParent()) {
-    depth++;
-  }
-  MOZ_ASSERT(ctxroot != nullptr, "There is always one or more ancestor");
-
-  if (depth < 3) {
-    // It is not really slow for shallow preserves3d context.
-    return GetAccumulatedTransformPreserve3DSlow(aLayer);
-  }
-
-  gfx::Matrix4x4 root_invert = ctxroot->GetEffectiveTransform();
-  if (!root_invert.Invert()) {
-    return GetAccumulatedTransformPreserve3DSlow(aLayer);
-  }
-
-  return To3DMatrix(aLayer->GetEffectiveTransform() * root_invert);
+GetAccumulatedTransformPreserve3D(Layer* aLayer) {
+  MOZ_ASSERT(aLayer->GetParent() && aLayer->GetParent()->Extend3DContext()); 
+  return To3DMatrix(aLayer->GetLocalTransform() *
+                    aLayer->GetParent()->GetEffectiveTransform());
 }
 
 /**
  * Determine if this transform layer should be drawn before another when they 
  * are both preserve-3d children.
  *
  * We want to find the relative z depths of the 2 layers at points where they
  * intersect when projected onto the 2d screen plane. Intersections are defined
@@ -114,18 +84,18 @@ GetAccumulatedTransformPreverse3D(Layer*
  * in particular) and this generates the 'correct' looking ordering. For planes
  * that truely intersect, then there is no correct ordering and this remains
  * unsolved without changing our rendering code.
  */
 static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
   gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
   gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
 
-  gfx3DMatrix ourTransform = GetAccumulatedTransformPreverse3D(aOne);
-  gfx3DMatrix otherTransform = GetAccumulatedTransformPreverse3D(aTwo);
+  gfx3DMatrix ourTransform = GetAccumulatedTransformPreserve3D(aOne);
+  gfx3DMatrix otherTransform = GetAccumulatedTransformPreserve3D(aTwo);
 
   // Transform both rectangles and project into 2d space.
   gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
   gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
 
   gfxRect ourBounds = ourTransformedRect.GetBounds();
   gfxRect otherBounds = otherTransformedRect.GetBounds();
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -714,46 +714,51 @@ Layer::MayResample()
   return !GetEffectiveTransform().Is2D(&transform2d) ||
          ThebesMatrix(transform2d).HasNonIntegerTranslation() ||
          AncestorLayerMayChangeTransform(this);
 }
 
 RenderTargetIntRect
 Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect)
 {
-  ContainerLayer* container = nullptr;
-  NS_ASSERTION(GetParent(), "This can't be called on the root!");
+  ContainerLayer* container;
 
-  // Find the layer
+  container = GetParent();
+  NS_ASSERTION(container, "This can't be called on the root!");
+  while (container->Extend3DContext()) {
+    container = container->GetParent();
+    MOZ_ASSERT(container);
+  }
+
   Layer *clipLayer = this;
-  for (Layer *l = GetParent();
-       l && l->Extend3DContext();
-       l = l->GetParent()) {
-    if (l->GetEffectiveClipRect()) {
-      clipLayer = l;
+  while (container != clipLayer) {
+    if (clipLayer->GetEffectiveClipRect()) {
       break;
     }
+    clipLayer = clipLayer->GetParent();
   }
-  container = clipLayer->GetParent();
+  if (container == clipLayer) {
+    clipLayer = this;
+  }
 
   // Establish initial clip rect: it's either the one passed in, or
   // if the parent has an intermediate surface, it's the extents of that surface.
   RenderTargetIntRect currentClip;
   if (container->UseIntermediateSurface()) {
     currentClip.SizeTo(container->GetIntermediateSurfaceRect().Size());
   } else {
     currentClip = aCurrentScissorRect;
   }
 
   if (!clipLayer->GetEffectiveClipRect()) {
     return currentClip;
   }
 
   const RenderTargetIntRect clipRect =
-    ViewAs<RenderTargetPixel>(*GetEffectiveClipRect(),
+    ViewAs<RenderTargetPixel>(*clipLayer->GetEffectiveClipRect(),
                               PixelCastJustification::RenderTargetIsParentLayerForRoot);
   if (clipRect.IsEmpty()) {
     // We might have a non-translation transform in the container so we can't
     // use the code path below.
     return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
   }
 
   RenderTargetIntRect scissor = clipRect;
@@ -1221,38 +1226,56 @@ ContainerLayer::SortChildrenBy3DZOrder(n
     }
   }
   if (toSort.Length() > 0) {
     SortLayersBy3DZOrder(toSort);
     aArray.MoveElementsFrom(toSort);
   }
 }
 
+bool
+ContainerLayer::IsCreating3DContextAndExtended() {
+  if (Extend3DContext()) {
+    return false;
+  }
+  for (Layer* child = GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+      if (child->Extend3DContext()) {
+        return true;
+      }
+  }
+  return false;
+}
+
 void
 ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+
   if (!Extend3DContext() && !Is3DContextLeaf()) {
+    // Keep 3D transforms for leave to keep z-order sorting correct.
     idealTransform.ProjectTo2D();
   }
   mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
 
   bool useIntermediateSurface;
   if (GetMaskLayer() ||
       GetForceIsolatedGroup()) {
     useIntermediateSurface = true;
 #ifdef MOZ_DUMP_PAINTING
   } else if (gfxUtils::sDumpPainting && !Extend3DContext()) {
     useIntermediateSurface = true;
 #endif
   } else {
     float opacity = GetEffectiveOpacity();
     CompositionOp blendMode = GetEffectiveMixBlendMode();
-    if ((opacity != 1.0f || blendMode != CompositionOp::OP_OVER) && HasMultipleChildren()) {
+    if (((opacity != 1.0f || blendMode != CompositionOp::OP_OVER) && HasMultipleChildren()) ||
+        (!idealTransform.Is2D() && IsCreating3DContextAndExtended())) {
       useIntermediateSurface = true;
     } else {
       useIntermediateSurface = false;
       gfx::Matrix contTransform;
       if (!mEffectiveTransform.Is2D(&contTransform) ||
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         !contTransform.PreservesAxisAlignedRectangles()) {
 #else
@@ -1270,16 +1293,19 @@ ContainerLayer::DefaultComputeEffectiveT
             useIntermediateSurface = true;
             break;
           }
         }
       }
     }
   }
 
+  if (!Extend3DContext()) {
+    idealTransform.ProjectTo2D();
+  }
   mUseIntermediateSurface = useIntermediateSurface && !GetEffectiveVisibleRegion().IsEmpty();
   if (useIntermediateSurface) {
     ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
   } else {
     ComputeEffectiveTransformsForChildren(idealTransform);
   }
 
   if (idealTransform.CanDraw2D()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -2038,16 +2038,22 @@ protected:
    * Loops over the children calling ComputeEffectiveTransforms on them.
    */
   void ComputeEffectiveTransformsForChildren(const gfx::Matrix4x4& aTransformToSurface);
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
   virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
 
+  /**
+   * Test if this container creates a 3D context and be extended by
+   * any child.
+   */
+  bool IsCreating3DContextAndExtended();
+
   Layer* mFirstChild;
   Layer* mLastChild;
   float mPreXScale;
   float mPreYScale;
   // The resolution scale inherited from the parent layer. This will already
   // be part of mTransform.
   float mInheritedXScale;
   float mInheritedYScale;
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -34,42 +34,42 @@ BasicContainerLayer::~BasicContainerLaye
 void
 BasicContainerLayer::ComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   // We push groups for container layers if we need to, which always
   // are aligned in device space, so it doesn't really matter how we snap
   // containers.
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+  if (!Extend3DContext() && !Is3DContextLeaf()) {
+    // For 3D transform leaked from extended parent layer.
+    idealTransform.ProjectTo2D();
+  }
 
-  if (Extend3DContext()) {
+  if (!idealTransform.CanDraw2D()) {
+    if ((!Extend3DContext() ||
+         (!idealTransform.Is2D() && IsCreating3DContextAndExtended()))) {
+      if (!IsCreating3DContextAndExtended()) {
+        idealTransform.ProjectTo2D();
+      }
+      mEffectiveTransform = idealTransform;
+      ComputeEffectiveTransformsForChildren(Matrix4x4());
+      ComputeEffectiveTransformForMaskLayer(Matrix4x4());
+      mUseIntermediateSurface = true;
+      return;
+    }
+
     mEffectiveTransform = idealTransform;
     ComputeEffectiveTransformsForChildren(idealTransform);
     ComputeEffectiveTransformForMaskLayer(idealTransform);
-    // Non-leaf Prserves3D containers never use intermediate surface
-    // since all children are composited separated in the the context.
+    mUseIntermediateSurface = false;
     return;
   }
 
-  idealTransform.ProjectTo2D();
-
-  if (!idealTransform.CanDraw2D()) {
-    mEffectiveTransform = idealTransform;
-    ComputeEffectiveTransformsForChildren(Matrix4x4());
-    ComputeEffectiveTransformForMaskLayer(Matrix4x4());
-    mUseIntermediateSurface = true;
-    return;
-  }
-
-  mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
-  // We always pass the ideal matrix down to our children, so there is no
-  // need to apply any compensation using the residual from SnapTransformTranslation.
-  ComputeEffectiveTransformsForChildren(idealTransform);
-
-  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
+  // With 2D transform or extended 3D context.
 
   Layer* child = GetFirstChild();
   bool hasSingleBlendingChild = false;
   if (!HasMultipleChildren() && child) {
     hasSingleBlendingChild = child->GetMixBlendMode() != CompositionOp::OP_OVER;
   }
 
   /* If we have a single childand it is not blending,, it can just inherit our opacity,
@@ -79,16 +79,30 @@ BasicContainerLayer::ComputeEffectiveTra
    * Having a mask layer always forces our own push group
    * Having a blend mode also always forces our own push group
    */
   mUseIntermediateSurface =
     GetMaskLayer() ||
     GetForceIsolatedGroup() ||
     (GetMixBlendMode() != CompositionOp::OP_OVER && HasMultipleChildren()) ||
     (GetEffectiveOpacity() != 1.0 && (HasMultipleChildren() || hasSingleBlendingChild));
+
+  if (!Extend3DContext()) {
+    idealTransform.ProjectTo2D();
+  }
+  mEffectiveTransform =
+    !mUseIntermediateSurface ?
+    idealTransform : SnapTransformTranslation(idealTransform, &residual);
+  Matrix4x4 childTransformToSurface =
+    (!mUseIntermediateSurface ||
+     (mUseIntermediateSurface && !Extend3DContext() /* 2D */)) ?
+    idealTransform : Matrix4x4::From2D(residual);
+  ComputeEffectiveTransformsForChildren(childTransformToSurface);
+
+  ComputeEffectiveTransformForMaskLayer(aTransformToSurface);
 }
 
 bool
 BasicContainerLayer::ChildrenPartitionVisibleRegion(const gfx::IntRect& aInRect)
 {
   Matrix transform;
   if (!GetEffectiveTransform().CanDraw2D(&transform) ||
       ThebesMatrix(transform).HasNonIntegerTranslation())
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -149,19 +149,16 @@ public:
       ClearOpaqueRect();
     }
   }
 
   // Gets the effective transform and returns true if it is a 2D
   // transform.
   bool Setup2DTransform()
   {
-    if (mLayer->Extend3DContext() || mLayer->Is3DContextLeaf()) {
-      return false;
-    }
     // Will return an identity matrix for 3d transforms.
     return mLayer->GetEffectiveTransformForBuffer().CanDraw2D(&mTransform);
   }
 
   // Applies the effective transform if it's 2D. If it's a 3D transform then
   // it applies an identity.
   void Apply2DTransform()
   {
@@ -847,17 +844,17 @@ BasicLayerManager::FlushGroup(PaintLayer
     PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
                   aPaintContext.mLayer->GetMaskLayer());
   }
 }
 
 static void
 InstallLayerClipPreserves3D(gfxContext* aTarget, Layer *aLayer)
 {
-  const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
+  const Maybe<ParentLayerIntRect> &clipRect = aLayer->GetEffectiveClipRect();
 
   if (!clipRect) {
     return;
   }
 
   Layer *parent = aLayer->GetParent();
   gfx3DMatrix transform3d =
     parent && parent->Extend3DContext() ?
@@ -967,20 +964,22 @@ BasicLayerManager::PaintLayer(gfxContext
       nsRefPtr<gfxContext> groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(),
                                       &needsClipToVisibleRegion);
       PaintSelfOrChildren(paintLayerContext, groupTarget);
       aTarget->PopGroupToSource();
       FlushGroup(paintLayerContext, needsClipToVisibleRegion);
     } else {
       PaintSelfOrChildren(paintLayerContext, aTarget);
     }
-  } else if ((aLayer->Extend3DContext() || aLayer->Is3DContextLeaf())
-             && !needsGroup) {
-    PaintSelfOrChildren(paintLayerContext, aTarget);
   } else {
+    if (!needsGroup && container) {
+      PaintSelfOrChildren(paintLayerContext, aTarget);
+      return;
+    }
+
     const nsIntRect& bounds = visibleRegion.GetBounds();
     RefPtr<DrawTarget> untransformedDT =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(bounds.width, bounds.height),
                                                                    SurfaceFormat::B8G8R8A8);
     if (!untransformedDT) {
       return;
     }
 
--- a/gfx/layers/client/ClientContainerLayer.h
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -42,33 +42,36 @@ protected:
     }
 
     MOZ_COUNT_DTOR(ClientContainerLayer);
   }
 
 public:
   virtual void RenderLayer() override
   {
+    printf("ClientContainerLayer::RenderLayer %p\n", this);
     if (GetMaskLayer()) {
       ToClientLayer(GetMaskLayer())->RenderLayer();
     }
     
     DefaultComputeSupportsComponentAlphaChildren();
 
     nsAutoTArray<Layer*, 12> children;
     SortChildrenBy3DZOrder(children);
 
     ReadbackProcessor readback;
     readback.BuildUpdates(this);
 
     for (uint32_t i = 0; i < children.Length(); i++) {
       Layer* child = children.ElementAt(i);
       if (!child->IsVisible()) {
+        printf("child %p of %p skept\n", child, this);
         continue;
       }
+      printf("child %p of %p\n", child, this);
 
       ToClientLayer(child)->RenderLayerWithReadback(&readback);
 
       if (!ClientManager()->GetRepeatTransaction() &&
           !child->GetInvalidRegion().IsEmpty()) {
         child->Mutated();
       }
     }
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -202,17 +202,18 @@ public:
   virtual void Composite() override;
   virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
   virtual void RunOverfillCallback(const uint32_t aOverfill) override;
 
   virtual void DidComposite(uint64_t aTransactionId);
 
   virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) override
   {
-   return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
+    printf("SupportsMixBlendModes %d %p\n", aMixBlendModes.serialize(), mForwarder.get());
+    return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
   }
 
   virtual bool AreComponentAlphaLayersEnabled() override;
 
   // Log APZ test data for the current paint. We supply the paint sequence
   // number ourselves, and take care of calling APZTestData::StartNewPaint()
   // when a new paint is started.
   void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -20,24 +20,27 @@
 #include "mozilla/Preferences.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for nsIntRect
 #include "gfx2DGlue.h"
 #include "ReadbackProcessor.h"
 
+#include "gfxUtils.h"
+
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
 ClientPaintedLayer::PaintThebes()
 {
+  printf("ClientPaintedLayer::PaintThebes %p\n", this);
   PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_ASSERTION(ClientManager()->InDrawing(),
                "Can only draw in drawing phase");
   
   uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
 #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
@@ -76,16 +79,17 @@ ClientPaintedLayer::PaintThebes()
     ClientManager()->GetPaintedLayerCallback()(this,
                                               ctx,
                                               iter.mDrawRegion,
                                               state.mClip,
                                               state.mRegionToInvalidate,
                                               ClientManager()->GetPaintedLayerCallbackData());
 
     ctx = nullptr;
+    gfxUtils::DumpAsDataURI(target, stdout);
     mContentClient->ReturnDrawTargetToBuffer(target);
     didUpdate = true;
   }
 
   if (didUpdate) {
     Mutated();
 
     mValidRegion.Or(mValidRegion, state.mRegionToDraw);
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -33,18 +33,18 @@
 #include "nsTArray.h"                   // for nsAutoTArray
 #include "TextRenderer.h"               // for TextRenderer
 #include <vector>
 #include "GeckoProfiler.h"              // for GeckoProfiler
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "ProfilerMarkers.h"            // for ProfilerMarkers
 #endif
 
-#define CULLING_LOG(...)
-// #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
+// #define CULLING_LOG(...)
+#define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
 static bool
 LayerHasCheckerboardingAPZC(Layer* aLayer, gfxRGBA* aOutColor)
@@ -307,16 +307,17 @@ ContainerPrepare(ContainerT* aContainer,
       if (lastSurf && !aContainer->mChildrenChanged && lastSurf->GetRect().IsEqualEdges(surfaceRect)) {
         surface = lastSurf;
       }
 
       if (!surface) {
         // If we don't need a copy we can render to the intermediate now to avoid
         // unecessary render target switching. This brings a big perf boost on mobile gpus.
         surface = CreateOrRecycleTarget(aContainer, aManager);
+        MOZ_ASSERT(surface);
 
         MOZ_PERFORMANCE_WARNING("gfx", "[%p] Container layer requires intermediate surface rendering\n", aContainer);
         RenderIntermediate(aContainer, aManager, RenderTargetPixel::ToUntyped(aClipRect), surface);
         aContainer->SetChildrenChanged(false);
       }
 
       aContainer->mPrepared->mTmpTarget = surface;
     } else {
@@ -487,16 +488,17 @@ ContainerRender(ContainerT* aContainer,
                  const gfx::IntRect& aClipRect)
 {
   MOZ_ASSERT(aContainer->mPrepared);
 
   gfx::VRHMDInfo *hmdInfo = aContainer->GetVRHMDInfo();
   if (hmdInfo && hmdInfo->GetConfiguration().IsValid()) {
     ContainerRenderVR(aContainer, aManager, aClipRect, hmdInfo);
     aContainer->mPrepared = nullptr;
+    printf("ContainerRender %p 1\n", aContainer);
     return;
   }
 
   if (aContainer->UseIntermediateSurface()) {
     RefPtr<CompositingRenderTarget> surface;
 
     if (aContainer->mPrepared->mNeedsSurfaceCopy) {
       // we needed to copy the background so we waited until now to render the intermediate
@@ -525,16 +527,17 @@ ContainerRender(ContainerT* aContainer,
 #endif
 
     EffectChain effectChain(aContainer);
     LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(aContainer->GetMaskLayer(),
                                                             effectChain,
                                                             !aContainer->GetTransform().CanDraw2D());
     if (autoMaskEffect.Failed()) {
       NS_WARNING("Failed to apply a mask effect.");
+      printf("ContainerRender %p 2\n", aContainer);
       return;
     }
 
     aContainer->AddBlendModeEffect(effectChain);
     effectChain.mPrimaryEffect = new EffectRenderTarget(surface);
 
     gfx::Rect rect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height);
     gfx::Rect clipRect(aClipRect.x, aClipRect.y, aClipRect.width, aClipRect.height);
@@ -558,16 +561,17 @@ ContainerRender(ContainerT* aContainer,
         if (!apzc->GetAsyncTransformAppliedToContent()
             && !Matrix4x4(apzc->GetCurrentAsyncTransform()).IsIdentity()) {
           aManager->UnusedApzTransformWarning();
           break;
         }
       }
     }
   }
+  printf("ContainerRender %p 3\n", aContainer);
 }
 
 ContainerLayerComposite::ContainerLayerComposite(LayerManagerComposite *aManager)
   : ContainerLayer(aManager, nullptr)
   , LayerComposite(aManager)
 {
   MOZ_COUNT_CTOR(ContainerLayerComposite);
   mImplData = static_cast<LayerComposite*>(this);
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -636,17 +636,17 @@ LayerManagerComposite::Render()
   bool grayscaleVal = gfxPrefs::LayersEffectGrayscale();
   float contrastVal = gfxPrefs::LayersEffectContrast();
   bool haveLayerEffects = (invertVal || grayscaleVal || contrastVal != 0.0);
 
   // Set LayerScope begin/end frame
   LayerScopeAutoFrame frame(PR_Now());
 
   // Dump to console
-  if (gfxPrefs::LayersDump()) {
+  if (gfxPrefs::LayersDump() || true) {
     this->Dump();
   } else if (profiler_feature_active("layersdump")) {
     std::stringstream ss;
     Dump(ss);
     profiler_log(ss.str().c_str());
   }
 
   // Dump to LayerScope Viewer
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -109,18 +109,20 @@ PaintedLayerComposite::GetRenderState()
   }
   return mBuffer->GetRenderState();
 }
 
 void
 PaintedLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
 {
   if (!mBuffer || !mBuffer->IsAttached()) {
+    printf("PaintedLayerComposite::RenderLayer %p !mBuffer(%p) || !mBuffer->IsAttached()\n", this, mBuffer.get());
     return;
   }
+  printf("PaintedLayerComposite::RenderLayer %p\n", this);
   PROFILER_LABEL("PaintedLayerComposite", "RenderLayer",
     js::ProfileEntry::Category::GRAPHICS);
 
   MOZ_ASSERT(mBuffer->GetCompositor() == mCompositeManager->GetCompositor() &&
              mBuffer->GetLayer() == this,
              "buffer is corrupted");
 
   const nsIntRegion& visibleRegion = GetEffectiveVisibleRegion();
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -2189,26 +2189,44 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem
 static void
 SetOuterVisibleRegion(Layer* aLayer, nsIntRegion* aOuterVisibleRegion,
                       const nsIntRect* aLayerContentsVisibleRect = nullptr,
                       bool aOuterUntransformed = false)
 {
   Matrix4x4 transform = aLayer->GetTransform();
   Matrix transform2D;
   if (aOuterUntransformed) {
+    printf("SetOuterVisibleRegion 1 (%d %d %d %d) (%d %d %d %d)\n",
+           aOuterVisibleRegion->GetBounds().x,
+           aOuterVisibleRegion->GetBounds().y,
+           aOuterVisibleRegion->GetBounds().width,
+           aOuterVisibleRegion->GetBounds().height,
+           aLayerContentsVisibleRect->x,
+           aLayerContentsVisibleRect->y,
+           aLayerContentsVisibleRect->width,
+           aLayerContentsVisibleRect->height);
+    const nsIntRect *currentRect;
+    nsIntRegionRectIterator itr(*aOuterVisibleRegion);
+    for (currentRect = itr.Next(); currentRect; currentRect = itr.Next()) {
+      printf("region rect (%d %d %d %d)\n",
+             currentRect->x, currentRect->y,
+             currentRect->width, currentRect->height);
+    }
     if (aLayerContentsVisibleRect) {
       aOuterVisibleRegion->And(*aOuterVisibleRegion,
                                *aLayerContentsVisibleRect);
     }
   } else if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
+    printf("SetOuterVisibleRegion 2\n");
     aOuterVisibleRegion->MoveBy(-int(transform2D._31), -int(transform2D._32));
     if (aLayerContentsVisibleRect) {
       aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
     }
   } else {
+    printf("SetOuterVisibleRegion 3\n");
     nsIntRect outerRect = aOuterVisibleRegion->GetBounds();
     // if 'transform' is not invertible, then nothing will be displayed
     // for the layer, so it doesn't really matter what we do here
     Rect outerVisible(outerRect.x, outerRect.y, outerRect.width, outerRect.height);
     transform.Invert();
 
     Rect layerContentsVisible(-float(INT32_MAX) / 2, -float(INT32_MAX) / 2,
                               float(INT32_MAX), float(INT32_MAX));
@@ -2228,27 +2246,31 @@ SetOuterVisibleRegion(Layer* aLayer, nsI
     nsIntRect visRect;
     if (gfxUtils::GfxRectToIntRect(layerVisible, &visRect)) {
       *aOuterVisibleRegion = visRect;
     } else  {
       aOuterVisibleRegion->SetEmpty();
     }
   }
 
+  printf("SetOuterVisibleRegion %p (%d %d %d %d)\n", aLayer,
+         aOuterVisibleRegion->GetBounds().x, aOuterVisibleRegion->GetBounds().y,
+         aOuterVisibleRegion->GetBounds().width, aOuterVisibleRegion->GetBounds().height);
   aLayer->SetVisibleRegion(*aOuterVisibleRegion);
 }
 
 void
 ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
                                               const nsIntRegion& aOuterVisibleRegion,
                                               const nsIntRect* aLayerContentsVisibleRect,
                                               bool aOuterUntransformed) const
 {
   nsIntRegion visRegion = aOuterVisibleRegion;
   if (!aOuterUntransformed) {
+    printf("MoveBy %d %d\n", mParameters.mOffset.x, mParameters.mOffset.y);
     visRegion.MoveBy(mParameters.mOffset);
   }
   SetOuterVisibleRegion(aLayer, &visRegion, aLayerContentsVisibleRect,
                         aOuterUntransformed);
 }
 
 nscolor
 ContainerState::FindOpaqueBackgroundColorInLayer(const PaintedLayerData* aData,
@@ -3302,50 +3324,70 @@ PaintInactiveLayer(nsDisplayListBuilder*
 
   RefPtr<DrawTarget> tempDT;
   if (gfxUtils::sDumpPainting) {
     tempDT = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
                                       itemVisibleRect.Size(),
                                       SurfaceFormat::B8G8R8A8);
     if (tempDT) {
       context = new gfxContext(tempDT);
-      context->SetMatrix(gfxMatrix::Translation(-itemVisibleRect.x,
-                                                -itemVisibleRect.y));
+      context->SetMatrix(gfxMatrix::Translation(-itemVisibleRect.x * 1,
+                                                -itemVisibleRect.y * 1));
+      fprintf(stderr, "PaintInactiveLayer visible %d %d\n",
+              itemVisibleRect.x, itemVisibleRect.y);
     }
   }
 #endif
   basic->BeginTransaction();
   basic->SetTarget(context);
 
   if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) {
+    fprintf(stderr, "PaintInactiveLayer nsDisplayItem::TYPE_SVG_EFFECTS\n");
     static_cast<nsDisplaySVGEffects*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic);
     if (basic->InTransaction()) {
       basic->AbortTransaction();
     }
   } else {
     basic->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
   }
   FrameLayerBuilder *builder = static_cast<FrameLayerBuilder*>(basic->GetUserData(&gLayerManagerLayerBuilder));
   if (builder) {
     builder->DidEndTransaction();
   }
 
   basic->SetTarget(nullptr);
 
 #ifdef MOZ_DUMP_PAINTING
   if (gfxUtils::sDumpPainting && tempDT) {
+#if 0
+    RefPtr<SourceSurface> surfacet = tempDT->Snapshot();
+    DumpPaintedImage(aItem, surfacet);
+
+    nsRefPtr<gfxASurface> surface = context->CurrentSurface();
+    aContext->Save();
+    aContext->SetSource(surface, gfxPoint(itemVisibleRect.x,
+                                          itemVisibleRect.y));
+    gfxRect rect(itemVisibleRect.x, itemVisibleRect.y,
+                 itemVisibleRect.width, itemVisibleRect.height);
+    aContext->Rectangle(rect);
+    aContext->Fill();
+    aContext->Restore();
+#else
     RefPtr<SourceSurface> surface = tempDT->Snapshot();
     DumpPaintedImage(aItem, surface);
 
     DrawTarget* drawTarget = aContext->GetDrawTarget();
     Rect rect(itemVisibleRect.x, itemVisibleRect.y,
               itemVisibleRect.width, itemVisibleRect.height);
     drawTarget->DrawSurface(surface, rect, Rect(Point(0,0), rect.Size()));
+#endif
 
     aItem->SetPainted();
+    fprintf(stderr, "\nInactive layer manager\n");
+    aManager->Dump();
   }
 #endif
 }
 
 /**
  * Chooses a single active scrolled root for the entire display list, used
  * when we are flattening layers.
  */
@@ -3735,16 +3777,26 @@ ContainerState::ProcessDisplayItems(nsDi
         newLayerEntry->mVisibleRegion = itemVisibleRect;
         if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
             (item->Frame()->Extend3DContext() ||
              item->Frame()->Combines3DTransformWithAncestors())) {
           // Give untransformed visible region as outer visible region
           // to avoid failure caused by singular transforms.
           newLayerEntry->mChildrenVisibleRegion =
             item->GetVisibleRectForChildren().ToNearestPixels(mAppUnitsPerDevPixel);
+          printf("mChildrenVisibleRegion %p (%d %d %d %d) (%d %d %d %d)\n",
+                 ownLayer.get(),
+                 newLayerEntry->mChildrenVisibleRegion.GetBounds().x,
+                 newLayerEntry->mChildrenVisibleRegion.GetBounds().y,
+                 newLayerEntry->mChildrenVisibleRegion.GetBounds().width,
+                 newLayerEntry->mChildrenVisibleRegion.GetBounds().height,
+                 item->GetVisibleRectForChildren().x,
+                 item->GetVisibleRectForChildren().y,
+                 item->GetVisibleRectForChildren().width,
+                 item->GetVisibleRectForChildren().height);
         }
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
           animatedGeometryRoot, fixedPosFrame, itemClip, aList,
           &newLayerEntry->mHideAllLayersBelow,
           &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
       } else {
         bool useChildrenVisible =
           item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
@@ -4331,16 +4383,19 @@ ContainerState::PostprocessRetainedLayer
       if (clipRect && opaqueRegionForContainer >= 0 &&
           opaqueRegions[opaqueRegionForContainer].mOpaqueRegion.Contains(ParentLayerIntRect::ToUntyped(*clipRect))) {
         e->mVisibleRegion.SetEmpty();
       } else if (data) {
         e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
       }
     }
 
+    printf("ContainerState::PostprocessRetainedLayers 1 %d %d\n",
+           (int)e->mChildrenVisibleRegion.IsEmpty(),
+           (int)(e->mLayerContentsVisibleRect.width >= 0));
     SetOuterVisibleRegionForLayer(e->mLayer,
       e->mChildrenVisibleRegion.IsEmpty() ? e->mVisibleRegion : e->mChildrenVisibleRegion,
       e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr,
       !e->mChildrenVisibleRegion.IsEmpty());
 
     if (!e->mOpaqueRegion.IsEmpty()) {
       const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness;
       if (e->mOpaqueForAnimatedGeometryRootParent &&
@@ -4888,16 +4943,21 @@ FrameLayerBuilder::BuildContainerLayerFo
   }
   containerLayer->SetContentFlags(flags);
   // If aContainerItem is non-null some BuildContainerLayer further up the
   // call stack is responsible for setting containerLayer's visible region.
   if (!aContainerItem) {
     containerLayer->SetVisibleRegion(pixBounds);
   }
   if (aParameters.mLayerContentsVisibleRect) {
+    printf("%p pixBounds (%d %d %d %d) offset (%d %d) bound (%d %d %d %d)\n",
+           aContainerFrame,
+           pixBounds.x, pixBounds.y, pixBounds.width, pixBounds.height,
+           scaleParameters.mOffset.x, scaleParameters.mOffset.y,
+           bounds.x, bounds.y, bounds.width, bounds.height);
     *aParameters.mLayerContentsVisibleRect = pixBounds + scaleParameters.mOffset;
   }
 
   mContainerLayerGeneration = oldGeneration;
   nsPresContext::ClearNotifySubDocInvalidationData(containerLayer);
 
   return containerLayer.forget();
 }
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -905,16 +905,19 @@ nsDisplayListBuilder::MarkPreserve3DFram
           NS_WARNING("Unable to untransform dirty rect!");
           dirty.SetEmpty();
         } else {
           // Dirty rect is related to the top-left corner of the child.
           dirty = nsDisplayTransform::TransformRect(dirty, child,
                                                     nsPoint(0, 0),
                                                     nullptr, false);
         }
+        printf("Mark p3d dirty %p (%d %d %d %d)\n",
+               child,
+               dirty.x, dirty.y, dirty.width, dirty.height);
         child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(),
                            new nsRect(dirty));
 
         MarkFrameForDisplay(child, aDirtyFrame);
       }
     }
   }
 }
@@ -951,17 +954,17 @@ nsDisplayListBuilder::FindReferenceFrame
   if (aFrame == mCurrentFrame) {
     if (aOffset) {
       *aOffset = mCurrentOffsetToReferenceFrame;
     }
     return mCurrentReferenceFrame;
   }
   for (const nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f))
   {
-    if (f == mReferenceFrame || f->IsTransformed() || f->Preserves3DLeaf()) {
+    if (f == mReferenceFrame || f->IsTransformed()) {
       if (aOffset) {
         *aOffset = aFrame->GetOffsetToCrossDoc(f);
       }
       return f;
     }
   }
   if (aOffset) {
     *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
@@ -3521,16 +3524,17 @@ void nsDisplayWrapList::Paint(nsDisplayL
  */
 static LayerState
 RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               const ContainerLayerParameters& aParameters,
                               const nsDisplayList& aList,
                               nsIFrame* aExpectedAnimatedGeometryRootForChildren)
 {
+  printf("RequiredLayerStateForChildren\n");
   LayerState result = LAYER_INACTIVE;
   for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
     if (result == LAYER_INACTIVE &&
         nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder, aManager) !=
           aExpectedAnimatedGeometryRootForChildren) {
       result = LAYER_ACTIVE;
     }
 
@@ -4531,20 +4535,21 @@ nsDisplayTransform::nsDisplayTransform(n
                                        const nsRect& aChildrenVisibleRect,
                                        ComputeTransformFunction aTransformGetter,
                                        uint32_t aIndex) 
   : nsDisplayItem(aBuilder, aFrame)
   , mStoredList(aBuilder, aFrame, aList)
   , mTransformGetter(aTransformGetter)
   , mChildrenVisibleRect(aChildrenVisibleRect)
   , mIndex(aIndex)
+  , mContextSeparator(false)
+  , mHasPresetTransform(false)
 {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
-  MOZ_ASSERT(!aFrame->IsTransformed(), "Can't specify a transform getter for a transformed frame!");
   Init(aBuilder);
 }
 
 void
 nsDisplayTransform::SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder)
 {
   nsIFrame *outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
   mReferenceFrame =
@@ -4565,50 +4570,75 @@ nsDisplayTransform::Init(nsDisplayListBu
     // We will only pre-render if this will-change is on budget.
     mMaybePrerender = true;
   }
 
   if (mMaybePrerender) {
     bool snap;
     mVisibleRect = GetBounds(aBuilder, &snap);
   }
+
+  RemoveBackfaceHiddenChildren();
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame *aFrame, nsDisplayList *aList,
                                        const nsRect& aChildrenVisibleRect,
                                        uint32_t aIndex)
   : nsDisplayItem(aBuilder, aFrame)
   , mStoredList(aBuilder, aFrame, aList)
   , mTransformGetter(nullptr)
   , mChildrenVisibleRect(aChildrenVisibleRect)
   , mIndex(aIndex)
+  , mContextSeparator(false)
+  , mHasPresetTransform(false)
 {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   SetReferenceFrameToAncestor(aBuilder);
   Init(aBuilder);
 }
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame *aFrame, nsDisplayItem *aItem,
                                        const nsRect& aChildrenVisibleRect,
                                        uint32_t aIndex)
   : nsDisplayItem(aBuilder, aFrame)
   , mStoredList(aBuilder, aFrame, aItem)
   , mTransformGetter(nullptr)
   , mChildrenVisibleRect(aChildrenVisibleRect)
   , mIndex(aIndex)
+  , mContextSeparator(false)
+  , mHasPresetTransform(false)
 {
   MOZ_COUNT_CTOR(nsDisplayTransform);
   MOZ_ASSERT(aFrame, "Must have a frame!");
   SetReferenceFrameToAncestor(aBuilder);
   Init(aBuilder);
 }
 
+nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
+                                       nsIFrame *aFrame, nsDisplayList *aList,
+                                       const nsRect& aChildrenVisibleRect,
+                                       const Matrix4x4& aTransform,
+                                       uint32_t aIndex) 
+  : nsDisplayItem(aBuilder, aFrame)
+  , mStoredList(aBuilder, aFrame, aList)
+  , mTransform(aTransform)
+  , mTransformGetter(nullptr)
+  , mChildrenVisibleRect(aChildrenVisibleRect)
+  , mIndex(aIndex)
+  , mContextSeparator(false)
+  , mHasPresetTransform(true)
+{
+  MOZ_COUNT_CTOR(nsDisplayTransform);
+  MOZ_ASSERT(aFrame, "Must have a frame!");
+  Init(aBuilder);
+}
+
 /* Returns the delta specified by the -moz-transform-origin property.
  * This is a positive delta, meaning that it indicates the direction to move
  * to get from (0, 0) of the frame to the transform origin.  This function is
  * called off the main thread.
  */
 /* static */ Point3D
 nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                               float aAppUnitsPerPixel,
@@ -5072,17 +5102,17 @@ nsDisplayTransform::GetTransform()
     float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
     Point3D newOrigin =
       Point3D(NSAppUnitsToFloatPixels(mToReferenceFrame.x, scale),
               NSAppUnitsToFloatPixels(mToReferenceFrame.y, scale),
               0.0f);
     if (mTransformGetter) {
       mTransform = mTransformGetter(mFrame, scale);
       mTransform.ChangeBasis(newOrigin.x, newOrigin.y, newOrigin.z);
-    } else {
+    } else if (!mHasPresetTransform) {
       bool isReference =
         mFrame->IsTransformed() ||
         mFrame->Combines3DTransformWithAncestors() || mFrame->Extend3DContext();
       /**
        * Passing true as the final argument means that we want to shift the
        * coordinates to be relative to our reference frame instead of relative
        * to this frame.
        * When we have preserve-3d, our reference frame is already guaranteed
@@ -5126,18 +5156,26 @@ nsDisplayTransform::ShouldBuildLayerEven
   // rendering correctly.
   return ShouldPrerender(aBuilder) || mFrame->Combines3DTransformWithAncestors();
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerLayerParameters& aContainerParameters)
 {
+  // XXX: For frames without transform, it would not be removed for
+  // backface visibility hidden.  We could ethier wrapping frames with
+  // an transform item or removing it out of list while running
+  // FrameLayerBuilder.
   const Matrix4x4& newTransformMatrix = GetTransform();
 
+  printf("nsDisplayTransform::BuildLayer %p backface %d backface visible %d\n",
+         this,
+         (int)mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
+         (int)GetAccumulatedPreserved3DTransform().IsBackfaceVisible());
   if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
       GetAccumulatedPreserved3DTransform().IsBackfaceVisible()) {
     return nullptr;
   }
 
   uint32_t flags = ShouldPrerender(aBuilder) ?
     FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
   flags |= FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
@@ -5146,17 +5184,17 @@ already_AddRefed<Layer> nsDisplayTransfo
                            aContainerParameters, &newTransformMatrix, flags);
 
   if (!container) {
     return nullptr;
   }
 
   // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
   // so we never need to explicitely unset this flag.
-  if (mFrame->Extend3DContext()) {
+  if (mFrame->Extend3DContext() && !mContextSeparator) {
     container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_EXTEND_3D_CONTEXT);
   } else {
     container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_EXTEND_3D_CONTEXT);
   }
 
   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
                                                            this, mFrame,
                                                            eCSSProperty_transform);
@@ -5614,16 +5652,36 @@ bool nsDisplayTransform::UntransformVisi
 }
 
 void
 nsDisplayTransform::WriteDebugInfo(std::stringstream& aStream)
 {
   AppendToString(aStream, GetTransform());
 }
 
+/**
+ * Remove all children that is backface hidden if backface of current
+ * transform is visible.
+ */
+void
+nsDisplayTransform::RemoveBackfaceHiddenChildren()
+{
+  if (!GetAccumulatedPreserved3DTransform().IsBackfaceVisible()) {
+    return;
+  }
+
+  nsDisplayList tmp;
+  while (nsDisplayItem *item = GetChildren()->RemoveBottom()) {
+    if (!item->BackfaceIsHidden()) {
+      tmp.AppendToTop(item);
+    }
+  }
+  GetChildren()->AppendToTop(&tmp);
+}
+
 nsDisplayItemGeometry*
 nsCharClipDisplayItem::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsCharClipGeometry(this, aBuilder);
 }
 
 void
 nsCharClipDisplayItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -603,17 +603,17 @@ public:
         mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame),
         mPrevAnimatedGeometryRoot(mBuilder->mCurrentAnimatedGeometryRoot),
         mPrevLayerEventRegions(aBuilder->mLayerEventRegions),
         mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame),
         mPrevDirtyRect(aBuilder->mDirtyRect),
         mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext),
         mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler)
     {
-      if (aForChild->IsTransformed() || aForChild->Preserves3DLeaf()) {
+      if (aForChild->IsTransformed()) {
         aBuilder->mCurrentOffsetToReferenceFrame = nsPoint();
         aBuilder->mCurrentReferenceFrame = aForChild;
       } else if (aBuilder->mCurrentFrame == aForChild->GetParent()) {
         aBuilder->mCurrentOffsetToReferenceFrame += aForChild->GetPosition();
       } else {
         aBuilder->mCurrentReferenceFrame =
           aBuilder->FindReferenceFrameFor(aForChild,
               &aBuilder->mCurrentOffsetToReferenceFrame);
@@ -1615,16 +1615,20 @@ public:
       DisplayItemClip temp = *mClip;
       temp.IntersectWith(aClip);
       SetClip(aBuilder, temp);
     } else {
       SetClip(aBuilder, aClip);
     }
   }
 
+  bool BackfaceIsHidden() {
+    return mFrame->StyleDisplay()->BackfaceIsHidden();
+  }
+
 protected:
   friend class nsDisplayList;
 
   nsDisplayItem() { mAbove = nullptr; }
 
   nsIFrame* mFrame;
   const DisplayItemClip* mClip;
   // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
@@ -3121,18 +3125,20 @@ public:
     virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                                LayerManager* aManager,
                                                const ContainerLayerParameters& aContainerParameters) override;
     virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      const ContainerLayerParameters& aParameters) override
     {
       if (mCanBeActive && aManager->SupportsMixBlendModes(mContainedBlendModes)) {
+        printf("nsDisplayBlendContainer %p %d %x %p active\n", this, (int)mCanBeActive, mContainedBlendModes.serialize(), aManager);
         return mozilla::LAYER_ACTIVE;
       }
+      printf("nsDisplayBlendContainer %p %d %x %p inactive\n", this, (int)mCanBeActive, mContainedBlendModes.serialize(), aManager);
       return mozilla::LAYER_INACTIVE;
     }
     virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) override;
     virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
       return false;
     }
     NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)
 
@@ -3504,16 +3510,19 @@ public:
                      nsDisplayList *aList, const nsRect& aChildrenVisibleRect,
                      uint32_t aIndex = 0);
   nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
                      nsDisplayItem *aItem, const nsRect& aChildrenVisibleRect,
                      uint32_t aIndex = 0);
   nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
                      nsDisplayList *aList, const nsRect& aChildrenVisibleRect,
                      ComputeTransformFunction aTransformGetter, uint32_t aIndex = 0);
+  nsDisplayTransform(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame,
+                     nsDisplayList *aList, const nsRect& aChildrenVisibleRect,
+                     const Matrix4x4& aTransform, uint32_t aIndex = 0);
 
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTransform()
   {
     MOZ_COUNT_DTOR(nsDisplayTransform);
   }
 #endif
 
@@ -3720,19 +3729,22 @@ public:
   /**
    * Check if this element will be prerendered. This must be done after the
    * display list has been fully built.
    */
   bool ShouldPrerender(nsDisplayListBuilder* aBuilder);
 
   virtual void WriteDebugInfo(std::stringstream& aStream) override;
 
+  void SetContextSeparator() { mContextSeparator = true; }
+
 private:
   void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
   void Init(nsDisplayListBuilder* aBuilder);
+  void RemoveBackfaceHiddenChildren();
 
   static gfx3DMatrix GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
                                                          const nsPoint& aOrigin,
                                                          float aAppUnitsPerPixel,
                                                          const nsRect* aBoundsOverride,
                                                          nsIFrame** aOutAncestor,
                                                          bool aOffsetByOrigin,
                                                          bool aDoPreserves3D);
@@ -3742,16 +3754,19 @@ private:
   // Accumulated transform of ancestors on the preserves-3d chain.
   Matrix4x4 mTransformP3D;
   ComputeTransformFunction mTransformGetter;
   nsRect mChildrenVisibleRect;
   uint32_t mIndex;
   // We wont know if we pre-render until the layer building phase where we can
   // check layers will-change budget.
   bool mMaybePrerender;
+  // True for that this item is for 3D context separator.
+  bool mContextSeparator;
+  bool mHasPresetTransform;
 };
 
 /**
  * This class adds basic support for limiting the rendering (in the inline axis
  * of the writing mode) to the part inside the specified edges.  It's a base
  * class for the display item classes that do the actual work.
  * The two members, mVisIStartEdge and mVisIEndEdge, are relative to the edges
  * of the frame's scrollable overflow rectangle and are the amount to suppress
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -174,18 +174,18 @@ PrintDisplayItemTo(nsDisplayListBuilder*
 #ifdef MOZ_DUMP_PAINTING
   if (aDumpHtml && aItem->Painted()) {
     nsCString string(aItem->Name());
     string.Append('-');
     string.AppendInt((uint64_t)aItem);
     aStream << nsPrintfCString("<a href=\"javascript:ViewImage('%s')\">", string.BeginReading());
   }
 #endif
-  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s",
-          aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(),
+  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s)0x%p %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s",
+          aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(), aItem->ReferenceFrame(),
           (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
           layerRect.x, layerRect.y, layerRect.width, layerRect.height,
           vis.x, vis.y, vis.width, vis.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
           aItem->IsUniform(aBuilder, &color) ? " uniform" : "");
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1128,17 +1128,17 @@ nsIFrame::Extend3DContext() const
 }
 
 bool
 nsIFrame::Combines3DTransformWithAncestors() const
 {
   if (!GetParent() || !GetParent()->Extend3DContext()) {
     return false;
   }
-  return true;
+  return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden();
 }
 
 bool
 nsIFrame::HasPerspective() const
 {
   if (!IsTransformed()) {
     return false;
   }
@@ -1849,57 +1849,59 @@ nsIFrame::BuildDisplayListForStackingCon
     // multiple transforms along the chain.
     aBuilder->SetPreserves3DDirtyRect(aDirtyRect);
   }
 
   // For preserves3d, use the dirty rect already installed on the
   // builder, since aDirtyRect maybe distorted for transforms along
   // the chain.
   nsRect dirtyRect = aDirtyRect;
+  int path = 0;
+#define REC(x) path |= 1 << x;
 
   bool inTransform = aBuilder->IsInTransform();
-  // Assume preserves-3d leaf layers are always transformed to make
-  // sure there is a container layer for each of them.
-  bool isTransformed = IsTransformed() ||
-    Extend3DContext() || Combines3DTransformWithAncestors();
+  bool isTransformed = IsTransformed();
   // reset blend mode so we can keep track if this stacking context needs have
   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
   // so we keep track if the parent stacking context needs a container too.
   AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder);
   aBuilder->SetContainsBlendModes(BlendModeSet());
  
   nsRect dirtyRectOutsideTransform = dirtyRect;
   if (isTransformed) {
     const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
     if (aBuilder->IsForPainting() &&
         (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder,
                                                                this) ||
          Extend3DContext() || Combines3DTransformWithAncestors())) {
+      REC(0);
       dirtyRect = overflow;
     } else {
       if (overflow.IsEmpty()) {
         return;
       }
 
       nsRect untransformedDirtyRect;
       if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
             nsPoint(0,0), &untransformedDirtyRect, false)) {
         dirtyRect = untransformedDirtyRect;
+        REC(1);
       } else {
         NS_WARNING("Unable to untransform dirty rect!");
         // This should only happen if the transform is singular, in which case nothing is visible anyway
         dirtyRect.SetEmpty();
       }
     }
     inTransform = true;
   }
 
   bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
   nsRect dirtyRectOutsideSVGEffects = dirtyRect;
   if (usingSVGEffects) {
+    REC(3);
     dirtyRect =
       nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
   }
 
   bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
   bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
   bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
     IsScrollFrameActive(aBuilder,
@@ -1932,16 +1934,17 @@ nsIFrame::BuildDisplayListForStackingCon
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
     CheckForApzAwareEventHandlers(aBuilder, this);
 
     nsRect clipPropClip;
     if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
                               nestedClipState)) {
       dirtyRect.IntersectRect(dirtyRect, clipPropClip);
+      REC(4);
     }
 
     MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
 
     // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false
     // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
     if (Extend3DContext()) {
       nsRect dirty = aBuilder->GetPreserves3DDirtyRect(this);
@@ -2065,24 +2068,69 @@ nsIFrame::BuildDisplayListForStackingCon
   if (isTransformed && !resultList.IsEmpty()) {
     // Restore clip state now so nsDisplayTransform is clipped properly.
     clipState.Restore();
     // Revert to the dirtyrect coming in from the parent, without our transform
     // taken into account.
     buildingDisplayList.SetDirtyRect(dirtyRectOutsideTransform);
     // Revert to the outer reference frame and offset because all display
     // items we create from now on are outside the transform.
+    nsPoint toOuterReferenceFrame;
     const nsIFrame* outerReferenceFrame =
-      aBuilder->FindReferenceFrameFor(GetParent());
+      aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
     buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
       GetOffsetToCrossDoc(outerReferenceFrame));
 
-    resultList.AppendNewToTop(
-      new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect));
-  }
+    nsDisplayTransform *transformItem =
+      new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect);
+    printf("nsDisplayTransform dirtyRect %p %p (%d %d %d %d)\n",
+           transformItem, this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
+    resultList.AppendNewToTop(transformItem);
+    bool needAdditionalTransform = false;
+    if (Extend3DContext()) {
+      // Set an additional identity transform if outer reference frame
+      // is in another 3D context.
+      if (outerReferenceFrame->Extend3DContext()) {
+        nsIFrame *f;
+        for (f = nsLayoutUtils::GetCrossDocParentFrame(this);
+             f && f != outerReferenceFrame;
+             f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
+          if (!f->Extend3DContext()) {
+            // The outer reference frame is in another 3D context if
+            // there is an non-extending 3D frame between current
+            // frame and outer reference frame.
+            needAdditionalTransform = true;
+            break;
+          }
+        }
+      }
+    } else if (outerReferenceFrame->Extend3DContext() &&
+               outerReferenceFrame != nsLayoutUtils::GetCrossDocParentFrame(this)) {
+      // This frame is not part of the 3D context of outer reference
+      // frame.  So, an additional transform item is required as a
+      // separator.
+      needAdditionalTransform = true;
+    }
+    if (needAdditionalTransform) {
+      Matrix4x4 sepTransform;
+      nsRect sepDirty = dirtyRectOutsideTransform;
+      sepDirty.MoveBy(toOuterReferenceFrame); // Compensate the
+                                              // translation by the
+                                              // ancestors.
+      printf("%p sepDirty.MoveBy(%d, %d)\n",
+             this, toOuterReferenceFrame.x, toOuterReferenceFrame.y);
+      nsDisplayTransform *sepItem =
+        new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList,
+                                          sepDirty,
+                                          sepTransform, 1);
+      sepItem->SetContextSeparator();
+      resultList.AppendNewToTop(sepItem);
+    }
+  }
+  printf("%p dirtyRect path %x\n", this, path);
 
   /* If we're doing VR rendering, then we need to wrap everything in a nsDisplayVR
    */
   if (vrHMDInfo && !resultList.IsEmpty()) {
     resultList.AppendNewToTop(
       new (aBuilder) nsDisplayVR(aBuilder, this, &resultList, vrHMDInfo));
   }
 
@@ -2121,54 +2169,16 @@ WrapInWrapList(nsDisplayListBuilder* aBu
   nsDisplayItem* item = aList->GetBottom();
   if (!item || item->GetAbove() || item->Frame() != aFrame) {
     return new (aBuilder) nsDisplayWrapList(aBuilder, aFrame, aList);
   }
   aList->RemoveBottom();
   return item;
 }
 
-/**
- * Wrap every items with a nsDisplayTranform if it is not wrapped for
- * the given frame.
- */
-static void
-WrapTransformIfNot(nsDisplayListBuilder* aBuilder,
-                   nsIFrame *aFrame,
-                   nsDisplayList* aList) {
-  nsDisplayList list;
-  nsDisplayList tmp;
-
-  const nsRect childrenVisible = aFrame->GetVisualOverflowRectRelativeToSelf();
-  nsDisplayItem *item;
-  while ((item = aList->RemoveBottom())) {
-    if (item->GetType() != nsDisplayItem::TYPE_TRANSFORM ||
-        item->Frame() != aFrame) {
-      tmp.AppendToTop(item);
-    } else {
-      if (!tmp.IsEmpty()) {
-        nsDisplayItem *tmpItem = new (aBuilder)
-          nsDisplayTransform(aBuilder, aFrame,
-                             &tmp, childrenVisible);
-        list.AppendToTop(tmpItem);
-      }
-      list.AppendToTop(item);
-    }
-  }
-
-  if (!tmp.IsEmpty()) {
-    item = new (aBuilder)
-      nsDisplayTransform(aBuilder, aFrame,
-                         &tmp, childrenVisible);
-    list.AppendToTop(item);
-  }
-
-  aList->AppendToTop(&list);
-}
-
 void
 nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                    nsIFrame*               aChild,
                                    const nsRect&           aDirtyRect,
                                    const nsDisplayListSet& aLists,
                                    uint32_t                aFlags) {
   // If painting is restricted to just the background of the top level frame,
   // then we have nothing to do here.
@@ -2384,36 +2394,16 @@ nsIFrame::BuildDisplayListForChild(nsDis
       nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions();
       if (eventRegions) {
         eventRegions->AddFrame(aBuilder, child);
       }
       aBuilder->AdjustWindowDraggingRegion(child);
       child->BuildDisplayList(aBuilder, dirty, aLists);
       aBuilder->DisplayCaret(child, dirty, aLists.Content());
 
-      if (child->Combines3DTransformWithAncestors() ||
-          (child->Extend3DContext() &&
-           disp->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN)) {
-        if (!aLists.BlockBorderBackgrounds()->IsEmpty()) {
-          WrapTransformIfNot(aBuilder, child, aLists.BlockBorderBackgrounds());
-        }
-        if (!aLists.Floats()->IsEmpty()) {
-          WrapTransformIfNot(aBuilder, child, aLists.Floats());
-        }
-        if (!aLists.PositionedDescendants()->IsEmpty()) {
-          WrapTransformIfNot(aBuilder, child, aLists.PositionedDescendants());
-        }
-        if (!aLists.Outlines()->IsEmpty()) {
-          WrapTransformIfNot(aBuilder, child, aLists.Outlines());
-        }
-        if (!aLists.Content()->IsEmpty()) {
-          WrapTransformIfNot(aBuilder, child, aLists.Content());
-        }
-      }
-
 #ifdef DEBUG
       DisplayDebugBorders(aBuilder, child, aLists);
 #endif
       return;
     }
 
     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
     // We allow positioned descendants of the child to escape to our parent
@@ -2432,27 +2422,16 @@ nsIFrame::BuildDisplayListForChild(nsDis
 
     list.AppendToTop(pseudoStack.BorderBackground());
     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
     list.AppendToTop(pseudoStack.Floats());
     list.AppendToTop(pseudoStack.Content());
     list.AppendToTop(pseudoStack.Outlines());
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
 
-    if (child->Combines3DTransformWithAncestors() ||
-        (child->Extend3DContext() &&
-         disp->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN)) {
-      if (!list.IsEmpty()) {
-        WrapTransformIfNot(aBuilder, child, &list);
-      }
-      if (!extraPositionedDescendants.IsEmpty()) {
-        WrapTransformIfNot(aBuilder, child, &extraPositionedDescendants);
-      }
-    }
-
 #ifdef DEBUG
     DisplayDebugBorders(aBuilder, child, aLists);
 #endif
   }
     
   // Clear clip rect for the construction of the items below. Since we're
   // clipping all their contents, they themselves don't need to be clipped.
   clipState.Clear();
--- a/layout/reftests/transform-3d/preserves3d-nested-ref.html
+++ b/layout/reftests/transform-3d/preserves3d-nested-ref.html
@@ -37,52 +37,8 @@
       <div id="inner">
 	<div id="inneri">
 	  <div id="rect3" class="rect"></div>
 	</div>
       </div>
     </div>
   </div>
 </body>
-<!DOCTYPE HTML>
-<title>preserve-3d consequence nested context</title>
-<style>
-.rect {
-  width: 100px;
-  height: 100px;
-}
-
-#outeri {
-  transform-style: preserve-3d;
-  transform: rotateX(45deg);
-}
-
-#rect1 {
-  background-color: green;
-}
-
-#rect2 {
-  transform: translate(50px, 50px);
-  background-color: pink;
-}
-
-#inneri {
-  transform: rotateX(45deg);
-}
-
-#rect3 {
-  background-color: red;
-}
-
-</style>
-<body>
-  <div id="outer">
-    <div id="outeri">
-      <div id="rect1" class="rect"></div>
-      <div id="rect2" class="rect"></div>
-      <div id="inner">
-	<div id="inneri">
-	  <div id="rect3" class="rect"></div>
-	</div>
-      </div>
-    </div>
-  </div>
-</body>
--- a/layout/reftests/transform-3d/preserves3d-nested.html
+++ b/layout/reftests/transform-3d/preserves3d-nested.html
@@ -39,54 +39,8 @@
       <div id="inner">
 	<div id="inneri">
 	  <div id="rect3" class="rect"></div>
 	</div>
       </div>
     </div>
   </div>
 </body>
-<!DOCTYPE HTML>
-<title>preserve-3d consequence nested context</title>
-<style>
-.rect {
-  width: 100px;
-  height: 100px;
-  background-color: green;
-}
-
-#outeri {
-  transform-style: preserve-3d;
-  transform: rotateX(45deg);
-}
-
-#inner {
-  transform: rotateX(0deg);
-}
-
-#inneri {
-  transform-style: preserve-3d;
-  transform: rotateX(45deg);
-}
-
-#rect2 {
-  transform: translate(50px, 50px);
-  background-color: pink;
-}
-
-#rect3 {
-  background-color: red;
-}
-
-</style>
-<body>
-  <div id="outer">
-    <div id="outeri">
-      <div id="rect1" class="rect"></div>
-      <div id="rect2" class="rect"></div>
-      <div id="inner">
-	<div id="inneri">
-	  <div id="rect3" class="rect"></div>
-	</div>
-      </div>
-    </div>
-  </div>
-</body>