Back out bug 1097464 (move preserve-3d handling into the compositor) from aurora to allow more time to sort out regressions (for the next release cycle). a=backout
authorL. David Baron <dbaron@dbaron.org>
Fri, 25 Sep 2015 16:27:52 -0700
changeset 317111 1deef50d13971533e9f5212fa554a3d9aab56421
parent 317110 60c3a5f91464623fc0ac0e960468e1ffeee8059d
child 317112 b717b80eec62a7ba9b8842487f157b68c1419edd
push id8643
push usermconley@mozilla.com
push dateTue, 22 Dec 2015 18:28:20 +0000
reviewersbackout
bugs1097464
milestone44.0a2
Back out bug 1097464 (move preserve-3d handling into the compositor) from aurora to allow more time to sort out regressions (for the next release cycle). a=backout Backs out changesets 621ab19e86db, cb6a976ec849, ce91e635e21a, 84031c74b6fd, dc43e93fb4c2, and 37cc5cf46da4.
gfx/layers/LayerSorter.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/apz/test/reftest/reftest.list
gfx/layers/basic/BasicContainerLayer.cpp
gfx/layers/basic/BasicLayerManager.cpp
gfx/layers/client/ClientContainerLayer.h
gfx/layers/composite/ContainerLayerComposite.cpp
gfx/layers/d3d9/CompositorD3D9.cpp
layout/base/ActiveLayerTracker.cpp
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/reftests/bugs/reftest.list
layout/reftests/outline/reftest.list
layout/reftests/transform-3d/preserves3d-nested-ref.html
layout/reftests/transform-3d/preserves3d-nested.html
layout/reftests/transform-3d/reftest.list
layout/style/AnimationCommon.cpp
--- a/gfx/layers/LayerSorter.cpp
+++ b/gfx/layers/LayerSorter.cpp
@@ -73,23 +73,18 @@ static gfxFloat RecoverZDepth(const Matr
  * 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();
 
-  MOZ_ASSERT(aOne->GetParent() && aOne->GetParent()->Extend3DContext() &&
-             aTwo->GetParent() && aTwo->GetParent()->Extend3DContext());
-  // Effective transform of leaves may had been projected to 2D.
-  Matrix4x4 ourTransform =
-    aOne->GetLocalTransform() * aOne->GetParent()->GetEffectiveTransform();
-  Matrix4x4 otherTransform =
-    aTwo->GetLocalTransform() * aTwo->GetParent()->GetEffectiveTransform();
+  Matrix4x4 ourTransform = aOne->GetTransform();
+  Matrix4x4 otherTransform = aTwo->GetTransform();
 
   // Transform both rectangles and project into 2d space.
   gfxQuad ourTransformedRect = ourRect.TransformToQuad(ourTransform);
   gfxQuad otherTransformedRect = otherRect.TransformToQuad(otherTransform);
 
   gfxRect ourBounds = ourTransformedRect.GetBounds();
   gfxRect otherBounds = otherTransformedRect.GetBounds();
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -621,96 +621,37 @@ Layer::GetEffectiveVisibleRegion()
 Matrix4x4
 Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
                                 Matrix* aResidualTransform)
 {
   if (aResidualTransform) {
     *aResidualTransform = Matrix();
   }
 
-  if (!mManager->IsSnappingEffectiveTransforms()) {
-    return aTransform;
-  }
-
   Matrix matrix2D;
   Matrix4x4 result;
-  if (aTransform.Is2D(&matrix2D) &&
+  if (mManager->IsSnappingEffectiveTransforms() &&
+      aTransform.Is2D(&matrix2D) &&
       !matrix2D.HasNonTranslation() &&
       matrix2D.HasNonIntegerTranslation()) {
     IntPoint snappedTranslation = RoundedToInt(matrix2D.GetTranslation());
     Matrix snappedMatrix = Matrix::Translation(snappedTranslation.x,
                                                snappedTranslation.y);
     result = Matrix4x4::From2D(snappedMatrix);
     if (aResidualTransform) {
       // set aResidualTransform so that aResidual * snappedMatrix == matrix2D.
       // (I.e., appying snappedMatrix after aResidualTransform gives the
       // ideal transform.)
       *aResidualTransform =
         Matrix::Translation(matrix2D._31 - snappedTranslation.x,
                             matrix2D._32 - snappedTranslation.y);
     }
-    return result;
-  }
-
-  if(aTransform.IsSingular() ||
-     (aTransform._14 != 0 || aTransform._24 != 0 || aTransform._34 != 0)) {
-    // For a singular transform, there is no reversed matrix, so we
-    // don't snap it.
-    // For a perspective transform, the content is transformed in
-    // non-linear, so we don't snap it too.
-    return aTransform;
+  } else {
+    result = aTransform;
   }
-
-  // Snap for 3D Transforms
-
-  Point3D transformedOrigin = aTransform * Point3D();
-
-  // Compute the transformed snap by rounding the values of
-  // transformed origin.
-  IntPoint transformedSnapXY =
-    RoundedToInt(Point(transformedOrigin.x, transformedOrigin.y));
-  Matrix4x4 inverse = aTransform;
-  inverse.Invert();
-  // see Matrix4x4::ProjectPoint()
-  Float transformedSnapZ =
-    inverse._33 == 0 ? 0 : (-(transformedSnapXY.x * inverse._13 +
-                              transformedSnapXY.y * inverse._23 +
-                              inverse._43) / inverse._33);
-  Point3D transformedSnap =
-    Point3D(transformedSnapXY.x, transformedSnapXY.y, transformedSnapZ);
-  if (transformedOrigin == transformedSnap) {
-    return aTransform;
-  }
-
-  // Compute the snap from the transformed snap.
-  Point3D snap = inverse * transformedSnap;
-  if (snap.z > 0.001 || snap.z < -0.001) {
-    // Allow some level of accumulated computation error.
-    MOZ_ASSERT(inverse._33 == 0.0);
-    return aTransform;
-  }
-
-  // The difference between the origin and snap is the residual transform.
-  if (aResidualTransform) {
-    // The residual transform is to translate the snap to the origin
-    // of the content buffer.
-    *aResidualTransform = Matrix::Translation(-snap.x, -snap.y);
-  }
-
-  // Translate transformed origin to transformed snap since the
-  // residual transform would trnslate the snap to the origin.
-  Point3D transformedShift = transformedSnap - transformedOrigin;
-  result = aTransform;
-  result.PostTranslate(transformedShift.x,
-                       transformedShift.y,
-                       transformedShift.z);
-
-  // For non-2d transform, residual translation could be more than
-  // 0.5 pixels for every axis.
-
   return result;
 }
 
 Matrix4x4
 Layer::SnapTransform(const Matrix4x4& aTransform,
                      const gfxRect& aSnapRect,
                      Matrix* aResidualTransform)
 {
@@ -765,55 +706,40 @@ Layer::MayResample()
          ThebesMatrix(transform2d).HasNonIntegerTranslation() ||
          AncestorLayerMayChangeTransform(this);
 }
 
 RenderTargetIntRect
 Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect)
 {
   ContainerLayer* container = GetParent();
-  ContainerLayer* containerChild = nullptr;
-  NS_ASSERTION(GetParent(), "This can't be called on the root!");
-
-  // Find the layer creating the 3D context.
-  while (container->Extend3DContext()) {
-    containerChild = container;
-    container = container->GetParent();
-    MOZ_ASSERT(container);
-  }
-
-  // Find the nearest layer with a clip, or this layer.
-  // ContainerState::SetupScrollingMetadata() may install a clip on
-  // the layer.
-  Layer *clipLayer =
-    containerChild && containerChild->GetEffectiveClipRect() ?
-    containerChild : this;
+  NS_ASSERTION(container, "This can't be called on the root!");
 
   // 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()) {
+  if (!GetEffectiveClipRect()) {
     return currentClip;
   }
 
   if (GetVisibleRegion().IsEmpty()) {
     // When our visible region is empty, our parent may not have created the
     // intermediate surface that we would require for correct clipping; however,
     // this does not matter since we are invisible.
     return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
   }
 
   const RenderTargetIntRect clipRect =
-    ViewAs<RenderTargetPixel>(*clipLayer->GetEffectiveClipRect(),
+    ViewAs<RenderTargetPixel>(*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;
@@ -1266,32 +1192,16 @@ ContainerLayer::FillSpecificAttributes(S
   aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
                                     mInheritedXScale, mInheritedYScale,
                                     mPresShellResolution, mScaleToResolution,
                                     mEventRegionsOverride,
                                     reinterpret_cast<uint64_t>(mHMDInfo.get()));
 }
 
 bool
-ContainerLayer::Creates3DContextWithExtendingChildren()
-{
-  if (Extend3DContext()) {
-    return false;
-  }
-  for (Layer* child = GetFirstChild();
-       child;
-       child = child->GetNextSibling()) {
-      if (child->Extend3DContext()) {
-        return true;
-      }
-  }
-  return false;
-}
-
-bool
 ContainerLayer::HasMultipleChildren()
 {
   uint32_t count = 0;
   for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
     const Maybe<ParentLayerIntRect>& clipRect = child->GetEffectiveClipRect();
     if (clipRect && clipRect->IsEmpty())
       continue;
     if (child->GetVisibleRegion().IsEmpty())
@@ -1299,41 +1209,25 @@ ContainerLayer::HasMultipleChildren()
     ++count;
     if (count > 1)
       return true;
   }
 
   return false;
 }
 
-/**
- * Collect all leaf descendants of the current 3D context.
- */
-void
-ContainerLayer::Collect3DContextLeaves(nsTArray<Layer*>& aToSort)
-{
-  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
-    ContainerLayer* container = l->AsContainerLayer();
-    if (container && container->Extend3DContext()) {
-      container->Collect3DContextLeaves(aToSort);
-    } else {
-      aToSort.AppendElement(l);
-    }
-  }
-}
-
 void
 ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
 {
   nsAutoTArray<Layer*, 10> toSort;
 
   for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
     ContainerLayer* container = l->AsContainerLayer();
-    if (container && container->Extend3DContext()) {
-      container->Collect3DContextLeaves(toSort);
+    if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
+      toSort.AppendElement(l);
     } else {
       if (toSort.Length() > 0) {
         SortLayersBy3DZOrder(toSort);
         aArray.AppendElements(Move(toSort));
         // XXX The move analysis gets confused here, because toSort gets moved
         // here, and then gets used again outside of the loop. To clarify that
         // we realize that the array is going to be empty to the move checker,
         // we clear it again here. (This method renews toSort for the move
@@ -1349,43 +1243,39 @@ ContainerLayer::SortChildrenBy3DZOrder(n
   }
 }
 
 void
 ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToSurface)
 {
   Matrix residual;
   Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
-
-  // Keep 3D transforms for leaves to keep z-order sorting correct.
-  if (!Extend3DContext() && !Is3DContextLeaf()) {
-    idealTransform.ProjectTo2D();
-  }
+  idealTransform.ProjectTo2D();
+  mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
 
   bool useIntermediateSurface;
   if (HasMaskLayers() ||
       GetForceIsolatedGroup()) {
     useIntermediateSurface = true;
 #ifdef MOZ_DUMP_PAINTING
-  } else if (gfxUtils::sDumpPaintingIntermediate && !Extend3DContext()) {
+  } else if (gfxUtils::sDumpPaintingIntermediate) {
     useIntermediateSurface = true;
 #endif
   } else {
     float opacity = GetEffectiveOpacity();
     CompositionOp blendMode = GetEffectiveMixBlendMode();
-    if (((opacity != 1.0f || blendMode != CompositionOp::OP_OVER) && HasMultipleChildren()) ||
-        (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren())) {
+    if ((opacity != 1.0f || blendMode != CompositionOp::OP_OVER) && HasMultipleChildren()) {
       useIntermediateSurface = true;
     } else {
       useIntermediateSurface = false;
       gfx::Matrix contTransform;
       bool checkClipRect = false;
       bool checkMaskLayers = false;
 
-      if (!idealTransform.Is2D(&contTransform)) {
+      if (!mEffectiveTransform.Is2D(&contTransform)) {
         // In 3D case, always check if we should use IntermediateSurface.
         checkClipRect = true;
         checkMaskLayers = true;
       } else {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         if (!contTransform.PreservesAxisAlignedRectangles()) {
 #else
         if (gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) {
@@ -1416,29 +1306,16 @@ ContainerLayer::DefaultComputeEffectiveT
             useIntermediateSurface = true;
             break;
           }
         }
       }
     }
   }
 
-  if (useIntermediateSurface) {
-    mEffectiveTransform = SnapTransformTranslation(idealTransform, &residual);
-  } else {
-    mEffectiveTransform = idealTransform;
-  }
-
-  // For layers extending 3d context, its ideal transform should be
-  // applied on children.
-  if (!Extend3DContext()) {
-    // Without this projection, non-container children would get a 3D
-    // transform while 2D is expected.
-    idealTransform.ProjectTo2D();
-  }
   mUseIntermediateSurface = useIntermediateSurface && !GetEffectiveVisibleRegion().IsEmpty();
   if (useIntermediateSurface) {
     ComputeEffectiveTransformsForChildren(Matrix4x4::From2D(residual));
   } else {
     ComputeEffectiveTransformsForChildren(idealTransform);
   }
 
   if (idealTransform.CanDraw2D()) {
@@ -2053,33 +1930,16 @@ Layer::DumpPacket(layerscope::LayersPack
       MakeUnique<char[]>(LZ4::maxCompressedSize(mDisplayListLog.Length()));
     int compressedSize = LZ4::compress((char*)mDisplayListLog.get(),
                                        mDisplayListLog.Length(),
                                        compressedData.get());
     layer->set_displaylistlog(compressedData.get(), compressedSize);
   }
 }
 
-bool
-Layer::IsBackfaceHidden()
-{
-  if (GetContentFlags() & CONTENT_BACKFACE_HIDDEN) {
-    Layer* container = AsContainerLayer() ? this : GetParent();
-    if (container) {
-      // The effective transform can include non-preserve-3d parent
-      // transforms, since we don't always require an intermediate.
-      if (container->Extend3DContext() || container->Is3DContextLeaf()) {
-        return container->GetEffectiveTransform().IsBackfaceVisible();
-      }
-      return container->GetBaseTransform().IsBackfaceVisible();
-    }
-  }
-  return false;
-}
-
 nsAutoPtr<LayerUserData>
 Layer::RemoveUserData(void* aKey)
 {
   nsAutoPtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
   return d;
 }
 
 void
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -768,17 +768,17 @@ public:
      * CONTENT_COMPONENT_ALPHA set.
      */
     CONTENT_COMPONENT_ALPHA_DESCENDANT = 0x04,
 
     /**
      * If this is set then this layer is part of a preserve-3d group, and should
      * be sorted with sibling layers that are also part of the same group.
      */
-    CONTENT_EXTEND_3D_CONTEXT = 0x08,
+    CONTENT_PRESERVE_3D = 0x08,
     /**
      * This indicates that the transform may be changed on during an empty
      * transaction where there is no possibility of redrawing the content, so the
      * implementation should be ready for that.
      */
     CONTENT_MAY_CHANGE_TRANSFORM = 0x10,
 
     /**
@@ -788,23 +788,17 @@ public:
     CONTENT_DISABLE_SUBPIXEL_AA = 0x20,
 
     /**
      * If this is set then the layer contains content that may look objectionable
      * if not handled as an active layer (such as text with an animated transform).
      * This is for internal layout/FrameLayerBuilder usage only until flattening
      * code is obsoleted. See bug 633097
      */
-    CONTENT_DISABLE_FLATTENING = 0x40,
-
-    /**
-     * This layer is hidden if the backface of the layer is visible
-     * to user.
-     */
-    CONTENT_BACKFACE_HIDDEN = 0x80
+    CONTENT_DISABLE_FLATTENING = 0x40
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the PaintedLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(uint32_t aFlags)
@@ -1458,36 +1452,16 @@ public:
   virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
 
   // These getters can be used anytime.  They return the effective
   // values that should be used when drawing this layer to screen,
   // accounting for this layer possibly being a shadow.
   const Maybe<ParentLayerIntRect>& GetEffectiveClipRect();
   const nsIntRegion& GetEffectiveVisibleRegion();
 
-  bool Extend3DContext() {
-    return GetContentFlags() & CONTENT_EXTEND_3D_CONTEXT;
-  }
-  bool Is3DContextLeaf() {
-    return !Extend3DContext() && GetParent() &&
-      reinterpret_cast<Layer*>(GetParent())->Extend3DContext();
-  }
-  /**
-   * 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
-   * result is true.
-   */
-  bool IsBackfaceHidden();
-  bool IsVisible() {
-    // For containers extending 3D context, visible region
-    // is meaningless, since they are just intermediate result of
-    // content.
-    return !GetEffectiveVisibleRegion().IsEmpty() || Extend3DContext();
-  }
-
   /**
    * Returns the product of the opacities of this layer and all ancestors up
    * to and excluding the nearest ancestor that has UseIntermediateSurface() set.
    */
   float GetEffectiveOpacity();
 
   /**
    * Returns the blendmode of this layer.
@@ -1878,26 +1852,18 @@ public:
     mEffectiveTransform = SnapTransformTranslation(idealTransform,
         mAllowResidualTranslation ? &residual : nullptr);
     // The residual can only be a translation because SnapTransformTranslation
     // only changes the transform if it's a translation
     NS_ASSERTION(residual.IsTranslation(),
                  "Residual transform can only be a translation");
     if (!gfx::ThebesPoint(residual.GetTranslation()).WithinEpsilonOf(mResidualTranslation, 1e-3f)) {
       mResidualTranslation = gfx::ThebesPoint(residual.GetTranslation());
-      DebugOnly<mozilla::gfx::Point> transformedOrig =
-        idealTransform * mozilla::gfx::Point();
-#ifdef DEBUG
-      DebugOnly<mozilla::gfx::Point> transformed =
-        idealTransform * mozilla::gfx::Point(mResidualTranslation.x,
-                                             mResidualTranslation.y) -
-        *&transformedOrig;
-#endif
-      NS_ASSERTION(-0.5 <= (&transformed)->x && (&transformed)->x < 0.5 &&
-                   -0.5 <= (&transformed)->y && (&transformed)->y < 0.5,
+      NS_ASSERTION(-0.5 <= mResidualTranslation.x && mResidualTranslation.x < 0.5 &&
+                   -0.5 <= mResidualTranslation.y && mResidualTranslation.y < 0.5,
                    "Residual translation out of range");
       mValidRegion.SetEmpty();
     }
     ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
 
   LayerManager::PaintedLayerCreationHint GetCreationHint() const { return mCreationHint; }
 
@@ -2132,18 +2098,16 @@ public:
   }
 
 protected:
   friend class ReadbackProcessor;
 
   void DidInsertChild(Layer* aLayer);
   void DidRemoveChild(Layer* aLayer);
 
-  void Collect3DContextLeaves(nsTArray<Layer*>& aToSort);
-
   ContainerLayer(LayerManager* aManager, void* aImplData);
 
   /**
    * A default implementation of ComputeEffectiveTransforms for use by OpenGL
    * and D3D.
    */
   void DefaultComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface);
 
@@ -2159,22 +2123,16 @@ 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;
 
-  /**
-   * True for if the container start a new 3D context extended by one
-   * or more children.
-   */
-  bool Creates3DContextWithExtendingChildren();
-
   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/apz/test/reftest/reftest.list
+++ b/gfx/layers/apz/test/reftest/reftest.list
@@ -5,13 +5,13 @@ chaos-mode skip-if(!asyncPan) == async-s
 chaos-mode skip-if(!asyncPan) == async-scrollbar-1-vh.html async-scrollbar-1-vh-ref.html
 chaos-mode skip-if(!asyncPan) == async-scrollbar-1-v-rtl.html async-scrollbar-1-v-rtl-ref.html
 chaos-mode skip-if(!asyncPan) == async-scrollbar-1-h-rtl.html async-scrollbar-1-h-rtl-ref.html
 chaos-mode skip-if(!asyncPan) == async-scrollbar-1-vh-rtl.html async-scrollbar-1-vh-rtl-ref.html
 
 # Different async zoom levels. Since the scrollthumb gets async-scaled in the
 # compositor, the border-radius ends of the scrollthumb are going to be a little
 # off, hence the fuzzy-if clauses.
-chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,98,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
+chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,77,82) == async-scrollbar-zoom-1.html async-scrollbar-zoom-1-ref.html
 chaos-mode skip-if(!asyncZoom) fuzzy-if(B2G,94,146) == async-scrollbar-zoom-2.html async-scrollbar-zoom-2-ref.html
 
 # Meta-viewport tag support
 chaos-mode skip-if(!asyncZoom) == initial-scale-1.html initial-scale-1-ref.html
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -34,42 +34,32 @@ 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();
-  }
+  idealTransform.ProjectTo2D();
 
   if (!idealTransform.CanDraw2D()) {
-    if (!Extend3DContext() ||
-        (!idealTransform.Is2D() && Creates3DContextWithExtendingChildren())) {
-      if (!Creates3DContextWithExtendingChildren()) {
-        idealTransform.ProjectTo2D();
-      }
-      mEffectiveTransform = idealTransform;
-      ComputeEffectiveTransformsForChildren(Matrix4x4());
-      ComputeEffectiveTransformForMaskLayers(Matrix4x4());
-      mUseIntermediateSurface = true;
-      return;
-    }
-
     mEffectiveTransform = idealTransform;
-    ComputeEffectiveTransformsForChildren(idealTransform);
-    ComputeEffectiveTransformForMaskLayers(idealTransform);
-    mUseIntermediateSurface = false;
+    ComputeEffectiveTransformsForChildren(Matrix4x4());
+    ComputeEffectiveTransformForMaskLayers(Matrix4x4());
+    mUseIntermediateSurface = true;
     return;
   }
 
-  // With 2D transform or extended 3D context.
+  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);
+
+  ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
 
   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,30 +69,16 @@ 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);
-
-  ComputeEffectiveTransformForMaskLayers(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
@@ -805,25 +805,17 @@ BasicLayerManager::PaintSelfOrChildren(P
                   aPaintContext.mLayer->GetMaskLayer());
     }
   } else {
     ContainerLayer* container =
         static_cast<ContainerLayer*>(aPaintContext.mLayer);
     nsAutoTArray<Layer*, 12> children;
     container->SortChildrenBy3DZOrder(children);
     for (uint32_t i = 0; i < children.Length(); i++) {
-      Layer* layer = children.ElementAt(i);
-      if (layer->IsBackfaceHidden()) {
-        continue;
-      }
-      if (!layer->AsContainerLayer() && !layer->IsVisible()) {
-        continue;
-      }
-
-      PaintLayer(aGroupTarget, layer, aPaintContext.mCallback,
+      PaintLayer(aGroupTarget, children.ElementAt(i), aPaintContext.mCallback,
           aPaintContext.mCallbackData);
       if (mTransactionIncomplete)
         break;
     }
   }
 }
 
 void
@@ -847,51 +839,16 @@ BasicLayerManager::FlushGroup(PaintLayer
     CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
     AutoSetOperator setOperator(aPaintContext.mTarget, op);
 
     PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
                   aPaintContext.mLayer->GetMaskLayer());
   }
 }
 
-/**
- * Install the clip applied to the layer on the given gfxContext.  The
- * given gfxContext is the buffer that the layer will be painted to.
- */
-static void
-InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer)
-{
-  const Maybe<ParentLayerIntRect> &clipRect = aLayer->GetEffectiveClipRect();
-
-  if (!clipRect) {
-    return;
-  }
-
-  Layer* parent = aLayer->GetParent();
-  Matrix4x4 transform3d =
-    parent && parent->Extend3DContext() ?
-    parent->GetEffectiveTransform() :
-    Matrix4x4();
-  Matrix transform;
-  if (!transform3d.CanDraw2D(&transform)) {
-    MOZ_CRASH("We should not have a 3D transform that CanDraw2D() is false!");
-    return;
-  }
-  gfxMatrix oldTransform = aTarget->CurrentMatrix();
-  transform *= ToMatrix(oldTransform);
-  aTarget->SetMatrix(ThebesMatrix(transform));
-
-  aTarget->NewPath();
-  aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y,
-                                    clipRect->width, clipRect->height));
-  aTarget->Clip();
-
-  aTarget->SetMatrix(oldTransform);
-}
-
 void
 BasicLayerManager::PaintLayer(gfxContext* aTarget,
                               Layer* aLayer,
                               DrawPaintedLayerCallback aCallback,
                               void* aCallbackData)
 {
   MOZ_ASSERT(aTarget);
 
@@ -921,34 +878,27 @@ BasicLayerManager::PaintLayer(gfxContext
   NS_ASSERTION(!container || !aLayer->GetMaskLayer() ||
                container->UseIntermediateSurface(),
                "ContainerLayer with mask layer should force UseIntermediateSurface");
 
   gfxContextAutoSaveRestore contextSR;
   gfxMatrix transform;
   // Will return an identity matrix for 3d transforms, and is handled separately below.
   bool is2D = paintLayerContext.Setup2DTransform();
-  MOZ_ASSERT(is2D || needsGroup || !container ||
-             container->Extend3DContext() ||
-             container->Is3DContextLeaf(),
-             "Must PushGroup for 3d transforms!");
+  MOZ_ASSERT(is2D || needsGroup || !container, "Must PushGroup for 3d transforms!");
 
-  Layer* parent = aLayer->GetParent();
-  bool inPreserves3DChain = parent && parent->Extend3DContext();
   bool needsSaveRestore =
-    needsGroup || clipRect || needsClipToVisibleRegion || !is2D ||
-    inPreserves3DChain;
+    needsGroup || clipRect || needsClipToVisibleRegion || !is2D;
   if (needsSaveRestore) {
     contextSR.SetContext(aTarget);
 
-    // The clips on ancestors on the preserved3d chain should be
-    // installed on the aTarget before painting the layer.
-    InstallLayerClipPreserves3D(aTarget, aLayer);
-    for (Layer* l = parent; l && l->Extend3DContext(); l = l->GetParent()) {
-      InstallLayerClipPreserves3D(aTarget, l);
+    if (clipRect) {
+      aTarget->NewPath();
+      aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y, clipRect->width, clipRect->height));
+      aTarget->Clip();
     }
   }
 
   paintLayerContext.Apply2DTransform();
 
   const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
   // If needsGroup is true, we'll clip to the visible region after we've popped the group
   if (needsClipToVisibleRegion && !needsGroup) {
@@ -973,21 +923,16 @@ BasicLayerManager::PaintLayer(gfxContext
                                       &needsClipToVisibleRegion);
       PaintSelfOrChildren(paintLayerContext, groupTarget);
       aTarget->PopGroupToSource();
       FlushGroup(paintLayerContext, needsClipToVisibleRegion);
     } else {
       PaintSelfOrChildren(paintLayerContext, aTarget);
     }
   } else {
-    if (!needsGroup && container) {
-      PaintSelfOrChildren(paintLayerContext, aTarget);
-      return;
-    }
-
     const IntRect& 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
@@ -53,17 +53,17 @@ public:
     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()) {
+      if (child->GetEffectiveVisibleRegion().IsEmpty()) {
         continue;
       }
 
       ToClientLayer(child)->RenderLayerWithReadback(&readback);
 
       if (!ClientManager()->GetRepeatTransaction() &&
           !child->GetInvalidRegion().IsEmpty()) {
         child->Mutated();
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -191,28 +191,24 @@ ContainerRenderVR(ContainerT* aContainer
 
   gfx::Matrix4x4 origTransform = aContainer->GetEffectiveTransform();
 
   for (uint32_t i = 0; i < children.Length(); i++) {
     LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
     Layer* layer = layerToRender->GetLayer();
     uint32_t contentFlags = layer->GetContentFlags();
 
-    if (layer->IsBackfaceHidden()) {
+    if (layer->GetEffectiveVisibleRegion().IsEmpty() &&
+        !layer->AsContainerLayer()) {
       continue;
     }
 
-    if (!layer->IsVisible() && !layer->AsContainerLayer()) {
-      continue;
-    }
-
-    // We flip between pre-rendered and Gecko-rendered VR based on
-    // whether the child layer of this VR container layer has
-    // CONTENT_EXTEND_3D_CONTEXT or not.
-    if ((contentFlags & Layer::CONTENT_EXTEND_3D_CONTEXT) == 0) {
+    // We flip between pre-rendered and Gecko-rendered VR based on whether
+    // the child layer of this VR container layer has PRESERVE_3D or not.
+    if ((contentFlags & Layer::CONTENT_PRESERVE_3D) == 0) {
       // This layer is native VR
       DUMP("%p Switching to pre-rendered VR\n", aContainer);
 
       // XXX we still need depth test here, but we have no way of preserving
       // depth anyway in native VR layers until we have a way to save them
       // from WebGL (and maybe depth video?)
       compositor->SetRenderTarget(surface);
       aContainer->ReplaceEffectiveTransform(origTransform);
@@ -351,24 +347,20 @@ ContainerPrepare(ContainerT* aContainer,
   aContainer->SortChildrenBy3DZOrder(children);
 
   for (uint32_t i = 0; i < children.Length(); i++) {
     LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
 
     RenderTargetIntRect clipRect = layerToRender->GetLayer()->
         CalculateScissorRect(aClipRect);
 
-    if (layerToRender->GetLayer()->IsBackfaceHidden()) {
-      continue;
-    }
-
     // We don't want to skip container layers because otherwise their mPrepared
     // may be null which is not allowed.
     if (!layerToRender->GetLayer()->AsContainerLayer()) {
-      if (!layerToRender->GetLayer()->IsVisible()) {
+      if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty()) {
         CULLING_LOG("Sublayer %p has no effective visible region\n", layerToRender->GetLayer());
         continue;
       }
 
       if (clipRect.IsEmpty()) {
         CULLING_LOG("Sublayer %p has an empty world clip rect\n", layerToRender->GetLayer());
         continue;
       }
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -686,17 +686,16 @@ CompositorD3D9::PrepareViewport(const gf
   /*
    * Matrix to transform to viewport space ( <-1.0, 1.0> topleft,
    * <1.0, -1.0> bottomright)
    */
   viewMatrix._11 = 2.0f / aSize.width;
   viewMatrix._22 = -2.0f / aSize.height;
   viewMatrix._41 = -1.0f;
   viewMatrix._42 = 1.0f;
-  viewMatrix._33 = 0.0f;
 
   HRESULT hr = device()->SetVertexShaderConstantF(CBmProjection, &viewMatrix._11, 4);
 
   if (FAILED(hr)) {
     NS_WARNING("Failed to set projection matrix");
   }
 }
 
--- a/layout/base/ActiveLayerTracker.cpp
+++ b/layout/base/ActiveLayerTracker.cpp
@@ -332,17 +332,17 @@ ActiveLayerTracker::IsStyleAnimated(nsDi
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
   if (layerActivity) {
     if (layerActivity->RestyleCountForProperty(aProperty) >= 2) {
       return true;
     }
   }
-  if (aProperty == eCSSProperty_transform && aFrame->Combines3DTransformWithAncestors()) {
+  if (aProperty == eCSSProperty_transform && aFrame->Preserves3D()) {
     return IsStyleAnimated(aBuilder, aFrame->GetParent(), aProperty);
   }
   return nsLayoutUtils::HasCurrentAnimationsForProperties(aFrame, &aProperty, 1);
 }
 
 /* static */ bool
 ActiveLayerTracker::IsOffsetOrMarginStyleAnimated(nsIFrame* aFrame)
 {
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -414,17 +414,16 @@ public:
     mExclusiveToOneItem(false),
     mSingleItemFixedToViewport(false),
     mIsCaret(false),
     mNeedComponentAlpha(false),
     mForceTransparentSurface(false),
     mHideAllLayersBelow(false),
     mOpaqueForAnimatedGeometryRootParent(false),
     mDisableFlattening(false),
-    mBackfaceHidden(false),
     mImage(nullptr),
     mCommonClipCount(-1),
     mNewChildLayersIndex(-1)
   {}
 
 #ifdef MOZ_DUMP_PAINTING
   /**
    * Keep track of important decisions for debugging.
@@ -603,22 +602,16 @@ public:
    * and the PaintedLayer completely fills the displayport of the scrollframe.
    */
   bool mOpaqueForAnimatedGeometryRootParent;
   /**
    * Set if there is content in the layer that must avoid being flattened.
    */
   bool mDisableFlattening;
   /**
-   * Set if the backface of this region is hidden to the user.
-   * Content that backface is hidden should not be draw on the layer
-   * with visible backface.
-   */
-  bool mBackfaceHidden;
-  /**
    * Stores the pointer to the nsDisplayImage if we want to
    * convert this to an ImageLayer.
    */
   nsDisplayImageContainer* mImage;
   /**
    * Stores the clip that we need to apply to the image or, if there is no
    * image, a clip for SOME item in the layer. There is no guarantee which
    * item's clip will be stored here and mItemClip should not be used to clip
@@ -667,17 +660,16 @@ struct NewLayerEntry {
   NewLayerEntry()
     : mAnimatedGeometryRoot(nullptr)
     , mAnimatedGeometryRootForScrollMetadata(nullptr)
     , mFixedPosFrameForLayerData(nullptr)
     , mLayerContentsVisibleRect(0, 0, -1, -1)
     , mHideAllLayersBelow(false)
     , mOpaqueForAnimatedGeometryRootParent(false)
     , mPropagateComponentAlphaFlattening(true)
-    , mUntransformedVisibleRegion(false)
     , mIsCaret(false)
   {}
   // mLayer is null if the previous entry is for a PaintedLayer that hasn't
   // been optimized to some other form (yet).
   RefPtr<Layer> mLayer;
   const nsIFrame* mAnimatedGeometryRoot;
   // For fixed background layers, mAnimatedGeometryRoot is the animated geometry
   // root of the viewport frame it's fixed to, but we need to annotate it with
@@ -706,19 +698,16 @@ struct NewLayerEntry {
   // When this flag is set, we can treat this opaque region as covering
   // content whose animated geometry root is the animated geometry root for
   // mAnimatedGeometryRoot->GetParent().
   bool mOpaqueForAnimatedGeometryRootParent;
 
   // If true, then the content flags for this layer should contribute
   // to our decision to flatten component alpha layers, false otherwise.
   bool mPropagateComponentAlphaFlattening;
-  // mVisibleRegion is relative to the associated frame before
-  // transform.
-  bool mUntransformedVisibleRegion;
   bool mIsCaret;
 };
 
 class PaintedLayerDataTree;
 
 /**
  * This is tree node type for PaintedLayerDataTree.
  * Each node corresponds to a different animated geometry root, and contains
@@ -764,17 +753,16 @@ public:
 
   /**
    * Find a PaintedLayerData in our mPaintedLayerDataStack that aItem can be
    * added to. Creates a new PaintedLayerData by calling
    * aNewPaintedLayerCallback if necessary.
    */
   template<typename NewPaintedLayerCallbackType>
   PaintedLayerData* FindPaintedLayerFor(const nsIntRect& aVisibleRect,
-                                        bool aBackfaceHidden,
                                         NewPaintedLayerCallbackType aNewPaintedLayerCallback);
 
   /**
    * Find an opaque background color for aRegion. Pulls a color from the parent
    * geometry root if appropriate, but only if that color is present underneath
    * the whole clip of this node, so that this node's contents can animate or
    * move (possibly async) without having to change the background color.
    * @param aUnderIndex Searching will start in mPaintedLayerDataStack right
@@ -940,17 +928,16 @@ public:
    * Find a PaintedLayerData for aItem. This can either be an existing
    * PaintedLayerData from inside a node in our tree, or a new one that gets
    * created by a call out to aNewPaintedLayerCallback.
    */
   template<typename NewPaintedLayerCallbackType>
   PaintedLayerData* FindPaintedLayerFor(const nsIFrame* aAnimatedGeometryRoot,
                                         const nsIntRect& aVisibleRect,
                                         bool aForceOwnLayer,
-                                        bool aBackfaceidden,
                                         NewPaintedLayerCallbackType aNewPaintedLayerCallback);
 
   /**
    * Finish everything.
    */
   void Finish();
 
   /**
@@ -1146,28 +1133,24 @@ public:
   /**
    * Check if we are currently inside an inactive layer.
    */
   bool IsInInactiveLayer() const {
     return mLayerBuilder->GetContainingPaintedLayerData();
   }
 
   /**
-   * Sets aOuterVisibleRegion as aLayer's visible region.
-   * @param aOuterVisibleRegion
-   *   is in the coordinate space of the container reference frame.
-   * @param aLayerContentsVisibleRect, if non-null, is in the layer's own
-   *   coordinate system.
-   * @param aOuterUntransformed is true if the given aOuterVisibleRegion
-   *   is already untransformed with the matrix of the layer.
+   * Sets aOuterVisibleRegion as aLayer's visible region. aOuterVisibleRegion
+   * is in the coordinate space of the container reference frame.
+   * aLayerContentsVisibleRect, if non-null, is in the layer's own
+   * coordinate system.
    */
   void SetOuterVisibleRegionForLayer(Layer* aLayer,
                                      const nsIntRegion& aOuterVisibleRegion,
-                                     const nsIntRect* aLayerContentsVisibleRect = nullptr,
-                                     bool aOuterUntransformed = false) const;
+                                     const nsIntRect* aLayerContentsVisibleRect = nullptr) const;
 
   /**
    * Try to determine whether the PaintedLayer aData has a single opaque color
    * covering aRect. If successful, return that color, otherwise return
    * NS_RGBA(0,0,0,0).
    * If aRect turns out not to intersect any content in the layer,
    * *aOutIntersectsLayer will be set to false.
    */
@@ -2411,27 +2394,21 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem
  * Set the visible region for aLayer.
  * aOuterVisibleRegion is the visible region relative to the parent layer.
  * aLayerContentsVisibleRect, if non-null, is a rectangle in the layer's
  * own coordinate system to which the layer's visible region is restricted.
  * Consumes *aOuterVisibleRegion.
  */
 static void
 SetOuterVisibleRegion(Layer* aLayer, nsIntRegion* aOuterVisibleRegion,
-                      const nsIntRect* aLayerContentsVisibleRect = nullptr,
-                      bool aOuterUntransformed = false)
+                      const nsIntRect* aLayerContentsVisibleRect = nullptr)
 {
   Matrix4x4 transform = aLayer->GetTransform();
   Matrix transform2D;
-  if (aOuterUntransformed) {
-    if (aLayerContentsVisibleRect) {
-      aOuterVisibleRegion->And(*aOuterVisibleRegion,
-                               *aLayerContentsVisibleRect);
-    }
-  } else if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
+  if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
     aOuterVisibleRegion->MoveBy(-int(transform2D._31), -int(transform2D._32));
     if (aLayerContentsVisibleRect) {
       aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
     }
   } else {
     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
@@ -2462,25 +2439,21 @@ SetOuterVisibleRegion(Layer* aLayer, nsI
   }
 
   aLayer->SetVisibleRegion(*aOuterVisibleRegion);
 }
 
 void
 ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
                                               const nsIntRegion& aOuterVisibleRegion,
-                                              const nsIntRect* aLayerContentsVisibleRect,
-                                              bool aOuterUntransformed) const
+                                              const nsIntRect* aLayerContentsVisibleRect) const
 {
   nsIntRegion visRegion = aOuterVisibleRegion;
-  if (!aOuterUntransformed) {
-    visRegion.MoveBy(mParameters.mOffset);
-  }
-  SetOuterVisibleRegion(aLayer, &visRegion, aLayerContentsVisibleRect,
-                        aOuterUntransformed);
+  visRegion.MoveBy(mParameters.mOffset);
+  SetOuterVisibleRegion(aLayer, &visRegion, aLayerContentsVisibleRect);
 }
 
 nscolor
 ContainerState::FindOpaqueBackgroundColorInLayer(const PaintedLayerData* aData,
                                                  const nsIntRect& aRect,
                                                  bool* aOutIntersectsLayer) const
 {
   *aOutIntersectsLayer = true;
@@ -2662,34 +2635,31 @@ PaintedLayerDataNode::AddChildNodeFor(co
     MakeUnique<PaintedLayerDataNode>(mTree, this, aAnimatedGeometryRoot);
   mChildren.AppendElement(Move(child));
   return mChildren.LastElement().get();
 }
 
 template<typename NewPaintedLayerCallbackType>
 PaintedLayerData*
 PaintedLayerDataNode::FindPaintedLayerFor(const nsIntRect& aVisibleRect,
-                                          bool aBackfaceHidden,
                                           NewPaintedLayerCallbackType aNewPaintedLayerCallback)
 {
   if (!mPaintedLayerDataStack.IsEmpty()) {
     if (mPaintedLayerDataStack[0].mExclusiveToOneItem) {
       MOZ_ASSERT(mPaintedLayerDataStack.Length() == 1);
       SetAllDrawingAbove();
       MOZ_ASSERT(mPaintedLayerDataStack.IsEmpty());
     } else {
       PaintedLayerData* lowestUsableLayer = nullptr;
       for (auto& data : Reversed(mPaintedLayerDataStack)) {
         if (data.mVisibleAboveRegion.Intersects(aVisibleRect)) {
           break;
         }
         MOZ_ASSERT(!data.mExclusiveToOneItem);
-        if (data.mBackfaceHidden == aBackfaceHidden) {
-          lowestUsableLayer = &data;
-        }
+        lowestUsableLayer = &data;
         nsIntRegion visibleRegion = data.mVisibleRegion;
         // Also check whether the event-regions intersect the visible rect,
         // unless we're in an inactive layer, in which case the event-regions
         // will be hoisted out into their own layer.
         // For performance reasons, we check the intersection with the bounds
         // of the event-regions.
         if (!mTree.ContState().IsInInactiveLayer() &&
             (data.mScaledHitRegionBounds.Intersects(aVisibleRect) ||
@@ -2851,28 +2821,26 @@ PaintedLayerDataTree::AddingOwnLayer(con
   }
 }
 
 template<typename NewPaintedLayerCallbackType>
 PaintedLayerData*
 PaintedLayerDataTree::FindPaintedLayerFor(const nsIFrame* aAnimatedGeometryRoot,
                                           const nsIntRect& aVisibleRect,
                                           bool aForceOwnLayer,
-                                          bool aBackfaceHidden,
                                           NewPaintedLayerCallbackType aNewPaintedLayerCallback)
 {
   const nsIntRect* bounds = aForceOwnLayer ? nullptr : &aVisibleRect;
   FinishPotentiallyIntersectingNodes(aAnimatedGeometryRoot, bounds);
   PaintedLayerDataNode* node = EnsureNodeFor(aAnimatedGeometryRoot);
   if (aForceOwnLayer) {
     node->SetAllDrawingAbove();
   }
   PaintedLayerData* data =
-    node->FindPaintedLayerFor(aVisibleRect, aBackfaceHidden,
-                              aNewPaintedLayerCallback);
+    node->FindPaintedLayerFor(aVisibleRect, aNewPaintedLayerCallback);
   data->mExclusiveToOneItem = aForceOwnLayer;
   return data;
 }
 
 void
 PaintedLayerDataTree::FinishPotentiallyIntersectingNodes(const nsIFrame* aAnimatedGeometryRoot,
                                                          const nsIntRect* aRect)
 {
@@ -3129,28 +3097,16 @@ ContainerState::PrepareColorLayer(Painte
   colorLayer->SetClipRect(Nothing());
 
   FLB_LOG_PAINTED_LAYER_DECISION(aData,
                                  "  Selected color layer=%p\n", colorLayer.get());
 
   return colorLayer.forget();
 }
 
-static void
-SetBackfaceHiddenForLayer(bool aBackfaceHidden, Layer* aLayer)
-{
-  if (aBackfaceHidden) {
-    aLayer->SetContentFlags(aLayer->GetContentFlags() |
-                            Layer::CONTENT_BACKFACE_HIDDEN);
-  } else {
-    aLayer->SetContentFlags(aLayer->GetContentFlags() &
-                            ~Layer::CONTENT_BACKFACE_HIDDEN);
-  }
-}
-
 template<typename FindOpaqueBackgroundColorCallbackType>
 void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueBackgroundColorCallbackType aFindOpaqueBackgroundColor)
 {
   PaintedLayerData* data = &aData;
 
   if (!data->mLayer) {
     // No layer was recycled, so we create a new one.
     RefPtr<PaintedLayer> paintedLayer = CreatePaintedLayer(data);
@@ -3390,21 +3346,16 @@ void ContainerState::FinishPaintedLayerD
     regions.mHitRegion.OrWith(maybeHitRegion);
 
     Matrix mat = layer->GetTransform().As2D();
     mat.Invert();
     regions.ApplyTranslationAndScale(mat._31, mat._32, mat._11, mat._22);
 
     layer->SetEventRegions(regions);
   }
-
-  SetBackfaceHiddenForLayer(data->mBackfaceHidden, data->mLayer);
-  if (layer != data->mLayer) {
-    SetBackfaceHiddenForLayer(data->mBackfaceHidden, layer);
-  }
 }
 
 static bool
 IsItemAreaInWindowOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                nsDisplayItem* aItem,
                                const nsRect& aComponentAlphaBounds)
 {
   if (!aItem->Frame()->PresContext()->IsChrome()) {
@@ -3601,17 +3552,16 @@ ContainerState::NewPaintedLayerData(nsDi
   PaintedLayerData data;
   data.mAnimatedGeometryRoot = aAnimatedGeometryRoot;
   data.mAnimatedGeometryRootForScrollMetadata = aAnimatedGeometryRootForScrollMetadata;
   data.mAnimatedGeometryRootOffset = aTopLeft;
   data.mFixedPosFrameForLayerData =
     FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport);
   data.mReferenceFrame = aItem->ReferenceFrame();
   data.mSingleItemFixedToViewport = aShouldFixToViewport;
-  data.mBackfaceHidden = aItem->Frame()->BackfaceIsHidden();
   data.mIsCaret = aItem->GetType() == nsDisplayItem::TYPE_CARET;
 
   data.mNewChildLayersIndex = mNewChildLayers.Length();
   NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
   newLayerEntry->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
   newLayerEntry->mAnimatedGeometryRootForScrollMetadata = aAnimatedGeometryRootForScrollMetadata;
   newLayerEntry->mFixedPosFrameForLayerData = data.mFixedPosFrameForLayerData;
   newLayerEntry->mIsCaret = data.mIsCaret;
@@ -4025,18 +3975,17 @@ ContainerState::ProcessDisplayItems(nsDi
       if (itemVisibleRect.IsEmpty() &&
           !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) {
         continue;
       }
 
       // 3D-transformed layers don't necessarily draw in the order in which
       // they're added to their parent container layer.
       bool mayDrawOutOfOrder = itemType == nsDisplayItem::TYPE_TRANSFORM &&
-        (item->Frame()->Combines3DTransformWithAncestors() ||
-         item->Frame()->Extend3DContext());
+        (item->Frame()->Preserves3D() || item->Frame()->Preserves3DChildren());
 
       // Let mPaintedLayerDataTree know about this item, so that
       // FindPaintedLayerFor and FindOpaqueBackgroundColor are aware of this
       // item, even though it's not in any PaintedLayerDataStack.
       // Ideally we'd only need the "else" case here and have
       // mPaintedLayerDataTree figure out the right clip from the animated
       // geometry root that we give it, but it can't easily figure about
       // overflow:hidden clips on ancestors just by looking at the frame.
@@ -4086,24 +4035,16 @@ ContainerState::ProcessDisplayItems(nsDi
       RefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
       if (!ownLayer) {
         continue;
       }
 
       NS_ASSERTION(!ownLayer->AsPaintedLayer(),
                    "Should never have created a dedicated Painted layer!");
 
-      if (item->BackfaceIsHidden()) {
-        ownLayer->SetContentFlags(ownLayer->GetContentFlags() |
-                                  Layer::CONTENT_BACKFACE_HIDDEN);
-      } else {
-        ownLayer->SetContentFlags(ownLayer->GetContentFlags() &
-                                  ~Layer::CONTENT_BACKFACE_HIDDEN);
-      }
-
       const nsIFrame* fixedPosFrame =
         FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport);
       SetFixedPositionLayerData(ownLayer, fixedPosFrame, !shouldFixToViewport);
 
       nsRect invalid;
       if (item->IsInvalid(invalid)) {
         ownLayer->SetInvalidRectToVisibleRegion();
       }
@@ -4158,42 +4099,24 @@ ContainerState::ProcessDisplayItems(nsDi
       // nsDisplayTransform::BuildLayer must set layerContentsVisibleRect.
       // We rely on this to ensure 3D transforms compute a reasonable
       // layer visible region.
       NS_ASSERTION(itemType != nsDisplayItem::TYPE_TRANSFORM ||
                    layerContentsVisibleRect.width >= 0,
                    "Transform items must set layerContentsVisibleRect!");
       if (mLayerBuilder->IsBuildingRetainedLayers()) {
         newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
-        if (itemType == 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->mUntransformedVisibleRegion = true;
-          newLayerEntry->mVisibleRegion =
-            item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel);
-        } else {
-          newLayerEntry->mVisibleRegion = itemVisibleRect;
-        }
+        newLayerEntry->mVisibleRegion = itemVisibleRect;
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
           animatedGeometryRoot, fixedPosFrame, itemClip, aList,
           &newLayerEntry->mHideAllLayersBelow,
           &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
       } else {
-        bool useChildrenVisible =
-          itemType == nsDisplayItem::TYPE_TRANSFORM &&
-          item->Frame()->IsPreserve3DLeaf();
-        const nsIntRegion &visible = useChildrenVisible ?
-          item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel):
-          itemVisibleRect;
-
-        SetOuterVisibleRegionForLayer(ownLayer, visible,
-            layerContentsVisibleRect.width >= 0 ? &layerContentsVisibleRect : nullptr,
-            useChildrenVisible);
+        SetOuterVisibleRegionForLayer(ownLayer, itemVisibleRect,
+            layerContentsVisibleRect.width >= 0 ? &layerContentsVisibleRect : nullptr);
       }
       if (itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) {
         nsDisplayScrollInfoLayer* scrollItem = static_cast<nsDisplayScrollInfoLayer*>(item);
         newLayerEntry->mOpaqueForAnimatedGeometryRootParent = false;
         newLayerEntry->mBaseFrameMetrics =
             scrollItem->ComputeFrameMetrics(ownLayer, mParameters);
       } else if ((itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
                   itemType == nsDisplayItem::TYPE_ZOOM ||
@@ -4210,19 +4133,17 @@ ContainerState::ProcessDisplayItems(nsDi
        */
       mLayerBuilder->AddLayerDisplayItem(ownLayer, item,
                                          layerState,
                                          topLeft, nullptr);
     } else {
       bool forceOwnLayer = shouldFixToViewport || IsCaretWithCustomClip(item, itemType);
       PaintedLayerData* paintedLayerData =
         mPaintedLayerDataTree.FindPaintedLayerFor(animatedGeometryRoot, itemVisibleRect,
-                                                  forceOwnLayer,
-                                                  item->Frame()->BackfaceIsHidden(),
-                                                  [&]() {
+                                                  forceOwnLayer, [&]() {
           return NewPaintedLayerData(item, itemVisibleRect, animatedGeometryRoot,
                                      animatedGeometryRootForScrollMetadata,
                                      topLeft, shouldFixToViewport);
         });
 
       if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) {
         nsDisplayLayerEventRegions* eventRegions =
             static_cast<nsDisplayLayerEventRegions*>(item);
@@ -4887,20 +4808,18 @@ 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);
       }
     }
 
-    SetOuterVisibleRegionForLayer(e->mLayer,
-                                  e->mVisibleRegion,
-                                  e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr,
-                                  e->mUntransformedVisibleRegion);
+    SetOuterVisibleRegionForLayer(e->mLayer, e->mVisibleRegion,
+      e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr);
 
     PaintedLayer* p = e->mLayer->AsPaintedLayer();
     if (p) {
       InvalidateVisibleBoundsChangesForScrolledLayer(p);
     }
 
     if (!e->mOpaqueRegion.IsEmpty()) {
       const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness;
@@ -5120,17 +5039,17 @@ ChooseScaleAndSetTransform(FrameLayerBui
 
   if (transform.IsSingular()) {
     return false;
   }
 
   bool canDraw2D = transform.CanDraw2D(&transform2d);
   gfxSize scale;
   // XXX Should we do something for 3D transforms?
-  if (canDraw2D && !aContainerFrame->Combines3DTransformWithAncestors()) {
+  if (canDraw2D) {
     // If the container's transform is animated off main thread, fix a suitable scale size
     // for animation
     if (aContainerItem &&
         aContainerItem->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
         nsLayoutUtils::HasAnimationsForCompositor(
           aContainerFrame, eCSSProperty_transform)) {
       // Use the size of the nearest widget as the maximum size.  This
       // is important since it might be a popup that is bigger than the
@@ -5213,20 +5132,18 @@ ChooseScaleAndSetTransform(FrameLayerBui
     ContainerLayerParameters(scale.width, scale.height, -offset, aIncomingScale);
   if (aTransform) {
     aOutgoingScale.mInTransformedSubtree = true;
     if (ActiveLayerTracker::IsStyleAnimated(aDisplayListBuilder, aContainerFrame,
                                             eCSSProperty_transform)) {
       aOutgoingScale.mInActiveTransformedSubtree = true;
     }
   }
-  if ((aLayerBuilder->IsBuildingRetainedLayers() &&
-       (!canDraw2D || transform2d.HasNonIntegerTranslation())) ||
-      aContainerFrame->Extend3DContext() ||
-      aContainerFrame->Combines3DTransformWithAncestors()) {
+  if (aLayerBuilder->IsBuildingRetainedLayers() &&
+      (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
     aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true;
   }
   return true;
 }
 
 already_AddRefed<ContainerLayer>
 FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
                                           LayerManager* aManager,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -100,16 +100,22 @@ SpammyLayoutWarningsEnabled()
     Preferences::GetBool("layout.spammy_warnings.enabled", &sValue);
     sValueInitialized = true;
   }
 
   return sValue;
 }
 #endif
 
+static inline nsIFrame*
+GetTransformRootFrame(nsIFrame* aFrame)
+{
+  return nsLayoutUtils::GetTransformRootFrame(aFrame);
+}
+
 static inline CSSAngle
 MakeCSSAngle(const nsCSSValue& aValue)
 {
   return CSSAngle(aValue.GetAngleValue(), aValue.GetUnit());
 }
 
 static void AddTransformFunctions(nsCSSValueList* aList,
                                   nsStyleContext* aContext,
@@ -570,17 +576,20 @@ nsDisplayListBuilder::AddAnimationsAndTr
       if (disp && disp->mChildPerspective.GetUnit() == eStyleUnit_Coord) {
         perspective = disp->mChildPerspective.GetCoordValue();
       }
     }
     nsPoint origin;
     if (aItem) {
       origin = aItem->ToReferenceFrame();
     } else {
-      nsIFrame* referenceFrame = nsLayoutUtils::GetReferenceFrame(aFrame);
+      // transform display items used a reference frame computed from
+      // their GetTransformRootFrame().
+      nsIFrame* referenceFrame =
+        nsLayoutUtils::GetReferenceFrame(GetTransformRootFrame(aFrame));
       origin = aFrame->GetOffsetToCrossDoc(referenceFrame);
     }
 
     data = TransformData(origin, offsetToTransformOrigin,
                          offsetToPerspectiveOrigin, bounds, perspective,
                          devPixelsToAppUnits);
   } else if (aProperty == eCSSProperty_opacity) {
     data = null_t();
@@ -912,40 +921,30 @@ nsDisplayListBuilder::MarkFramesForDispl
         }
       }
     }
     mFramesMarkedForDisplay.AppendElement(e);
     MarkOutOfFlowFrameForDisplay(aDirtyFrame, e, aDirtyRect);
   }
 }
 
-/**
- * Mark all preserve-3d children with
- * NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO to make sure
- * nsFrame::BuildDisplayListForChild() would visit them.  Also compute
- * dirty rect for preserve-3d children.
- *
- * @param aDirtyFrame is the frame to mark children extending context.
- * @param aDirtyRect is the same as the dirty rect of the root of the
- *                   current 3D context, but be translated relative to
- *                   the aDirtyFrame.
- */
 void
 nsDisplayListBuilder::MarkPreserve3DFramesForDisplayList(nsIFrame* aDirtyFrame, const nsRect& aDirtyRect)
 {
   nsAutoTArray<nsIFrame::ChildList,4> childListArray;
   aDirtyFrame->GetChildLists(&childListArray);
   nsIFrame::ChildListArrayIterator lists(childListArray);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame *child = childFrames.get();
-      if (child->Combines3DTransformWithAncestors()) {
+      if (child->Preserves3D()) {
         mFramesMarkedForDisplay.AppendElement(child);
         nsRect dirty = aDirtyRect - child->GetOffsetTo(aDirtyFrame);
+
         child->Properties().Set(nsDisplayListBuilder::Preserve3DDirtyRectProperty(),
                            new nsRect(dirty));
 
         MarkFrameForDisplay(child, aDirtyFrame);
       }
     }
   }
 }
@@ -1834,28 +1833,25 @@ void nsDisplayList::HitTest(nsDisplayLis
   for (int32_t i = aState->mItemBuffer.Length() - 1; i >= itemBufferStart; --i) {
     // Pop element off the end of the buffer. We want to shorten the buffer
     // so that recursive calls to HitTest have more buffer space.
     item = aState->mItemBuffer[i];
     aState->mItemBuffer.SetLength(i);
 
     bool snap;
     nsRect r = item->GetBounds(aBuilder, &snap).Intersect(aRect);
-    bool alwaysIntersect =
-      item->Frame()->Combines3DTransformWithAncestors() &&
-      item->GetType() == nsDisplayItem::TYPE_TRANSFORM;
-    if (alwaysIntersect || item->GetClip().MayIntersect(r)) {
+    if (item->GetClip().MayIntersect(r)) {
       nsAutoTArray<nsIFrame*, 16> outFrames;
       item->HitTest(aBuilder, aRect, aState, &outFrames);
 
       // For 3d transforms with preserve-3d we add hit frames into the temp list
       // so we can sort them later, otherwise we add them directly to the output list.
       nsTArray<nsIFrame*> *writeFrames = aOutFrames;
       if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM &&
-          item->Frame()->Combines3DTransformWithAncestors()) {
+          item->Frame()->Preserves3D()) {
         if (outFrames.Length()) {
           nsDisplayTransform *transform = static_cast<nsDisplayTransform*>(item);
           nsPoint point = aRect.TopLeft();
           // A 1x1 rect means a point, otherwise use the center of the rect
           if (aRect.width != 1 || aRect.height != 1) {
             point = aRect.Center();
           }
           temp.AppendElement(FramesWithDepth(transform->GetHitDepthAtPoint(aBuilder, point)));
@@ -3600,31 +3596,44 @@ nsDisplayWrapList::nsDisplayWrapList(nsD
 
   mList.AppendToTop(aList);
   UpdateBounds(aBuilder);
 
   if (!aFrame || !aFrame->IsTransformed()) {
     return;
   }
 
-  // If we're a transformed frame, then we need to find out if we're inside
-  // the nsDisplayTransform or outside of it. Frames inside the transform
-  // need mReferenceFrame == mFrame, outside needs the next ancestor
-  // reference frame.
-  // If we're inside the transform, then the nsDisplayItem constructor
-  // will have done the right thing.
-  // If we're outside the transform, then we should have only one child
-  // (since nsDisplayTransform wraps all actual content), and that child
-  // will have the correct reference frame set (since nsDisplayTransform
-  // handles this explictly).
-  nsDisplayItem *i = mList.GetBottom();
-  if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) &&
-      i->Frame() == mFrame) {
-    mReferenceFrame = i->ReferenceFrame();
-    mToReferenceFrame = i->ToReferenceFrame();
+  // If the frame is a preserve-3d parent, then we will create transforms
+  // inside this list afterwards (see WrapPreserve3DList in nsFrame.cpp).
+  // In this case we will always be outside of the transform, so share
+  // our parents reference frame.
+  if (aFrame->Preserves3DChildren()) {
+    mReferenceFrame = 
+      aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
+    mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
+  } else {
+    // If we're a transformed frame, then we need to find out if we're inside
+    // the nsDisplayTransform or outside of it. Frames inside the transform
+    // need mReferenceFrame == mFrame, outside needs the next ancestor
+    // reference frame.
+    // If we're inside the transform, then the nsDisplayItem constructor
+    // will have done the right thing.
+    // If we're outside the transform, then we should have only one child
+    // (since nsDisplayTransform wraps all actual content), and that child
+    // will have the correct reference frame set (since nsDisplayTransform
+    // handles this explictly).
+    //
+    // Preserve-3d can cause us to have multiple nsDisplayTransform
+    // children.
+    nsDisplayItem *i = mList.GetBottom();
+    if (i && (!i->GetAbove() || i->GetType() == TYPE_TRANSFORM) &&
+        i->Frame() == mFrame) {
+      mReferenceFrame = i->ReferenceFrame();
+      mToReferenceFrame = i->ToReferenceFrame();
+    }
   }
   mVisibleRect = aBuilder->GetDirtyRect() +
       aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame, nsDisplayItem* aItem)
   : nsDisplayItem(aBuilder, aFrame)
@@ -3637,20 +3646,26 @@ nsDisplayWrapList::nsDisplayWrapList(nsD
 
   mList.AppendToTop(aItem);
   UpdateBounds(aBuilder);
   
   if (!aFrame || !aFrame->IsTransformed()) {
     return;
   }
 
-  // See the previous nsDisplayWrapList constructor
-  if (aItem->Frame() == aFrame) {
-    mReferenceFrame = aItem->ReferenceFrame();
-    mToReferenceFrame = aItem->ToReferenceFrame();
+  if (aFrame->Preserves3DChildren()) {
+    mReferenceFrame = 
+      aBuilder->FindReferenceFrameFor(GetTransformRootFrame(aFrame));
+    mToReferenceFrame = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
+  } else {
+    // See the previous nsDisplayWrapList constructor
+    if (aItem->Frame() == aFrame) {
+      mReferenceFrame = aItem->ReferenceFrame();
+      mToReferenceFrame = aItem->ToReferenceFrame();
+    }
   }
   mVisibleRect = aBuilder->GetDirtyRect() +
       aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 }
 
 nsDisplayWrapList::~nsDisplayWrapList() {
   mList.DeleteAll();
 
@@ -4701,37 +4716,34 @@ bool nsDisplayZoom::ComputeVisibility(ns
 // detection.
 #undef  UNIFIED_CONTINUATIONS
 #undef  DEBUG_HIT
 
 nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
                                        nsIFrame *aFrame, nsDisplayList *aList,
                                        const nsRect& aChildrenVisibleRect,
                                        ComputeTransformFunction aTransformGetter,
-                                       uint32_t aIndex)
+                                       uint32_t aIndex) 
   : nsDisplayItem(aBuilder, aFrame)
   , mStoredList(aBuilder, aFrame, aList)
   , mTransformGetter(aTransformGetter)
   , mChildrenVisibleRect(aChildrenVisibleRect)
   , mIndex(aIndex)
-  , mNoExtendContext(false)
-  , mHasPresetTransform(false)
-  , mTransformPreserves3DInited(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 =
-    aBuilder->FindReferenceFrameFor(outerFrame);
+    aBuilder->FindReferenceFrameFor(GetTransformRootFrame(mFrame));
   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
   mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
 }
 
 void
 nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder)
 {
   mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
@@ -4754,79 +4766,51 @@ nsDisplayTransform::nsDisplayTransform(n
                                        nsIFrame *aFrame, nsDisplayList *aList,
                                        const nsRect& aChildrenVisibleRect,
                                        uint32_t aIndex)
   : nsDisplayItem(aBuilder, aFrame)
   , mStoredList(aBuilder, aFrame, aList)
   , mTransformGetter(nullptr)
   , mChildrenVisibleRect(aChildrenVisibleRect)
   , mIndex(aIndex)
-  , mNoExtendContext(false)
-  , mHasPresetTransform(false)
-  , mTransformPreserves3DInited(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)
-  , mNoExtendContext(false)
-  , mHasPresetTransform(false)
-  , mTransformPreserves3DInited(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)
-  , mNoExtendContext(false)
-  , mHasPresetTransform(true)
-  , mTransformPreserves3DInited(false)
-{
-  MOZ_COUNT_CTOR(nsDisplayTransform);
-  MOZ_ASSERT(aFrame, "Must have a frame!");
-  Init(aBuilder);
-}
-
 /* Returns the delta specified by the 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,
                                               const nsRect* aBoundsOverride)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
-  NS_PRECONDITION(aFrame->IsTransformed() ||
-                  aFrame->StyleDisplay()->BackfaceIsHidden() ||
-                  aFrame->Combines3DTransformWithAncestors(),
+  NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
                   "Shouldn't get a delta for an untransformed frame!");
 
   if (!aFrame->IsTransformed()) {
     return Point3D();
   }
 
   /* For both of the coordinates, if the value of transform is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
@@ -4891,19 +4875,17 @@ nsDisplayTransform::GetDeltaToTransformO
  * to get from (0, 0) of the frame to the perspective origin. This function is
  * called off the main thread.
  */
 /* static */ Point3D
 nsDisplayTransform::GetDeltaToPerspectiveOrigin(const nsIFrame* aFrame,
                                                 float aAppUnitsPerPixel)
 {
   NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
-  NS_PRECONDITION(aFrame->IsTransformed() ||
-                  aFrame->StyleDisplay()->BackfaceIsHidden() ||
-                  aFrame->Combines3DTransformWithAncestors(),
+  NS_PRECONDITION(aFrame->IsTransformed() || aFrame->StyleDisplay()->BackfaceIsHidden(),
                   "Shouldn't get a delta for an untransformed frame!");
 
   if (!aFrame->IsTransformed()) {
     return Point3D();
   }
 
   /* For both of the coordinates, if the value of -moz-perspective-origin is a
    * percentage, it's relative to the size of the frame.  Otherwise, if it's
@@ -4986,61 +4968,43 @@ nsDisplayTransform::FrameTransformProper
 Matrix4x4
 nsDisplayTransform::GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
                                                 const nsPoint& aOrigin,
                                                 float aAppUnitsPerPixel,
                                                 const nsRect* aBoundsOverride,
                                                 nsIFrame** aOutAncestor)
 {
   return GetResultingTransformMatrixInternal(aProperties, aOrigin, aAppUnitsPerPixel,
-                                             aBoundsOverride, aOutAncestor, false, false);
+                                             aBoundsOverride, aOutAncestor, false);
 }
  
 Matrix4x4
 nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
                                                 const nsPoint& aOrigin,
                                                 float aAppUnitsPerPixel,
                                                 const nsRect* aBoundsOverride,
                                                 nsIFrame** aOutAncestor,
                                                 bool aOffsetByOrigin)
 {
   FrameTransformProperties props(aFrame,
                                  aAppUnitsPerPixel,
                                  aBoundsOverride);
 
   return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel,
                                              aBoundsOverride, aOutAncestor,
-                                             aOffsetByOrigin, false);
-}
-
-Matrix4x4
-nsDisplayTransform::GetResultingTransformMatrixP3D(const nsIFrame* aFrame,
-                                                   const nsPoint& aOrigin,
-                                                   float aAppUnitsPerPixel,
-                                                   const nsRect* aBoundsOverride,
-                                                   nsIFrame** aOutAncestor,
-                                                   bool aOffsetByOrigin)
-{
-  FrameTransformProperties props(aFrame,
-                                 aAppUnitsPerPixel,
-                                 aBoundsOverride);
-
-  return GetResultingTransformMatrixInternal(props, aOrigin, aAppUnitsPerPixel,
-                                             aBoundsOverride, aOutAncestor,
-                                             aOffsetByOrigin, true);
+                                             aOffsetByOrigin);
 }
 
 Matrix4x4
 nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
                                                         const nsPoint& aOrigin,
                                                         float aAppUnitsPerPixel,
                                                         const nsRect* aBoundsOverride,
                                                         nsIFrame** aOutAncestor,
-                                                        bool aOffsetByOrigin,
-                                                        bool aDoPreserves3D)
+                                                        bool aOffsetByOrigin)
 {
   const nsIFrame *frame = aProperties.mFrame;
 
   if (aOutAncestor) {
     *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(frame);
   }
 
   // Get the underlying transform matrix:
@@ -5145,36 +5109,35 @@ nsDisplayTransform::GetResultingTransfor
     if (aOffsetByOrigin) {
       result.PreTranslate(-refBoxOffset);
       result.PostTranslate(offsets);
     } else {
       result.ChangeBasis(offsets);
     }
   }
 
-  if (aDoPreserves3D && frame && frame->Combines3DTransformWithAncestors()) {
+  if (frame && frame->Preserves3D()) {
     // Include the transform set on our parent
     NS_ASSERTION(frame->GetParent() &&
                  frame->GetParent()->IsTransformed() &&
-                 frame->GetParent()->Extend3DContext(),
+                 frame->GetParent()->Preserves3DChildren(),
                  "Preserve3D mismatch!");
     FrameTransformProperties props(frame->GetParent(),
                                    aAppUnitsPerPixel,
                                    nullptr);
 
     // If this frame isn't transformed (but we exist for backface-visibility),
     // then we're not a reference frame so no offset to origin will be added. Our
     // parent transform however *is* the reference frame, so we pass true for
     // aOffsetByOrigin to convert into the correct coordinate space.
     Matrix4x4 parent =
       GetResultingTransformMatrixInternal(props,
                                           aOrigin - frame->GetPosition(),
                                           aAppUnitsPerPixel, nullptr,
-                                          aOutAncestor, !frame->IsTransformed(),
-                                          aDoPreserves3D);
+                                          aOutAncestor, !frame->IsTransformed());
     result = result * parent;
   }
 
   return result;
 }
 
 bool
 nsDisplayOpacity::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
@@ -5304,94 +5267,67 @@ 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 if (!mHasPresetTransform) {
-      bool isReference =
-        mFrame->IsTransformed() ||
-        mFrame->Combines3DTransformWithAncestors() || mFrame->Extend3DContext();
+    } else {
       /**
        * 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
        * to be an ancestor of the preserve-3d chain, so we only need to do
        * this once.
-       * For preserve-3d leaf, itself is a refrence frame.
        */
       mTransform = GetResultingTransformMatrix(mFrame, ToReferenceFrame(),
                                                scale, nullptr, nullptr,
-                                               isReference);
+                                               mFrame->IsTransformed());
     }
   }
   return mTransform;
 }
 
-const Matrix4x4&
-nsDisplayTransform::GetAccumulatedPreserved3DTransform()
-{
-  // XXX: should go back to fix mTransformGetter.
-  if (!mTransformPreserves3DInited) {
-    mTransformPreserves3DInited = true;
-    if (!mFrame->Combines3DTransformWithAncestors()) {
-      mTransformPreserves3D = GetTransform();
-      return mTransformPreserves3D;
-    }
-    float scale = mFrame->PresContext()->AppUnitsPerDevPixel();
-    bool isReference =
-      mFrame->IsTransformed() ||
-      mFrame->Combines3DTransformWithAncestors() || mFrame->Extend3DContext();
-    mTransformPreserves3D =
-      GetResultingTransformMatrixP3D(mFrame, ToReferenceFrame(), scale,
-                                     nullptr, nullptr, isReference);
-  }
-  return mTransformPreserves3D;
-}
-
 bool
 nsDisplayTransform::ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder)
 {
-  // The visible rect of a Preserves-3D frame is just an intermediate
-  // result.  It should always build a layer to make sure it is
-  // rendering correctly.
-  return ShouldPrerender(aBuilder) || mFrame->Combines3DTransformWithAncestors();
+  return ShouldPrerender(aBuilder);
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerLayerParameters& aContainerParameters)
 {
-  /* For frames without transform, it would not be removed for
-   * backface hidden here.  But, it would be removed by the init
-   * function of nsDisplayTransform.
-   */
   const Matrix4x4& newTransformMatrix = GetTransform();
 
+  if (mFrame->StyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
+      newTransformMatrix.IsBackfaceVisible()) {
+    return nullptr;
+  }
+
   uint32_t flags = ShouldPrerender(aBuilder) ?
     FrameLayerBuilder::CONTAINER_NOT_CLIPPED_BY_ANCESTORS : 0;
   flags |= FrameLayerBuilder::CONTAINER_ALLOW_PULL_BACKGROUND_COLOR;
   RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mStoredList.GetChildren(),
                            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() && !mNoExtendContext) {
-    container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_EXTEND_3D_CONTEXT);
+  if (mFrame->Preserves3D() || mFrame->Preserves3DChildren()) {
+    container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
   } else {
-    container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_EXTEND_3D_CONTEXT);
+    container->SetContentFlags(container->GetContentFlags() & ~Layer::CONTENT_PRESERVE_3D);
   }
 
   nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(container, aBuilder,
                                                            this, mFrame,
                                                            eCSSProperty_transform);
   if (ShouldPrerender(aBuilder)) {
     container->SetUserData(nsIFrame::LayerIsPrerenderedDataKey(),
                            /*the value is irrelevant*/nullptr);
@@ -5404,17 +5340,17 @@ already_AddRefed<Layer> nsDisplayTransfo
 }
 
 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 (!GetTransform().Is2D() || mFrame->Preserves3D()) {
     return LAYER_ACTIVE_FORCE;
   }
   // Here we check if the *post-transform* bounds of this item are big enough
   // to justify an active layer.
   if (ActiveLayerTracker::IsStyleAnimated(aBuilder, mFrame, eCSSProperty_transform) &&
       !IsItemTooSmallForActiveLayer(this))
     return LAYER_ACTIVE;
   if (nsLayoutUtils::HasAnimationsForCompositor(mFrame, eCSSProperty_transform)) {
@@ -5471,17 +5407,17 @@ void nsDisplayTransform::HitTest(nsDispl
    * 2. Invert the matrix.
    * 3. Use it to transform the rect into the correct space.
    * 4. Pass that rect down through to the list's version of HitTest.
    */
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   Matrix4x4 matrix = GetTransform();
 
-  if (!IsFrameVisible(mFrame, GetAccumulatedPreserved3DTransform())) {
+  if (!IsFrameVisible(mFrame, matrix)) {
     return;
   }
 
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
 
@@ -5548,18 +5484,17 @@ void nsDisplayTransform::HitTest(nsDispl
 
 float
 nsDisplayTransform::GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint)
 {
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   Matrix4x4 matrix = GetTransform();
 
-  NS_ASSERTION(IsFrameVisible(mFrame, GetAccumulatedPreserved3DTransform()),
-               "We can't have hit a frame that isn't visible!");
+  NS_ASSERTION(IsFrameVisible(mFrame, matrix), "We can't have hit a frame that isn't visible!");
 
   Matrix4x4 inverse = matrix;
   inverse.Invert();
   Point4D point = inverse.ProjectPoint(Point(NSAppUnitsToFloatPixels(aPoint.x, factor),
                                              NSAppUnitsToFloatPixels(aPoint.y, factor)));
   NS_ASSERTION(point.HasPositiveWCoord(), "Why are we trying to get the depth for a point we didn't hit?");
 
   Point point2d = point.As2DPoint();
@@ -5568,69 +5503,25 @@ nsDisplayTransform::GetHitDepthAtPoint(n
   return transformed.z;
 }
 
 /* The bounding rectangle for the object is the overflow rectangle translated
  * by the reference point.
  */
 nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder, bool* aSnap)
 {
-  /* For some cases, the transform would make an empty bounds, but it
-   * may be turned back again to get a non-empty bounds.  We should
-   * not depend on transforming bounds level by level.
-   *
-   * Here, it applies accumulated transforms on the leaf frames of the
-   * 3d rendering context, and track and accmulate bounds at
-   * nsDisplayListBuilder.
-   */
-  nsDisplayListBuilder::AutoAccumulateTransform accTransform(aBuilder);
-  Maybe<nsDisplayListBuilder::AutoAccumulateRect> accRect;
-  bool startPreserves3D =
-    mFrame->Extend3DContext() && !mFrame->Combines3DTransformWithAncestors();
-
-  if (!mFrame->Combines3DTransformWithAncestors()) {
-    accTransform.StartRoot();
-  }
-
-  accTransform.Accumulate(GetTransform());
-  if (startPreserves3D) {
-    accRect.emplace(aBuilder);
-  }
-
-  /* For Preserves3D, it is bounds of only children as leaf frames.
-   * For non-leaf frames, their bounds are accumulated and kept at
-   * nsDisplayListBuilder.
-   */
   nsRect untransformedBounds = MaybePrerender() ?
     mFrame->GetVisualOverflowRectRelativeToSelf() :
     mStoredList.GetBounds(aBuilder, aSnap);
   *aSnap = false;
   // GetTransform always operates in dev pixels.
   float factor = mFrame->PresContext()->AppUnitsPerDevPixel();
-  nsRect rect =
-    nsLayoutUtils::MatrixTransformRect(untransformedBounds,
-                                       accTransform.GetCurrentTransform(),
-                                       factor);
-
-  if (mFrame->Combines3DTransformWithAncestors()) {
-    if (!mFrame->Extend3DContext() &&
-        !aBuilder->GetAccumulatedRectLevels()) {
-      // For preserve-3d, only leaf frames and frames start
-      // preserve-3d chain have non-empty bounds.
-      return rect;
-    }
-    aBuilder->AccumulateRect(rect);
-    return nsRect();
-  }
-
-  if (startPreserves3D) {
-    rect.UnionRect(rect, aBuilder->GetAccumulatedRect());
-  }
-
-  return rect;
+  return nsLayoutUtils::MatrixTransformRect(untransformedBounds,
+                                            GetTransform(),
+                                            factor);
 }
 
 /* The transform is opaque iff the transform consists solely of scales and
  * translations and if the underlying content is opaque.  Thus if the transform
  * is of the form
  *
  * |a c e|
  * |b d f|
@@ -5746,59 +5637,52 @@ nsDisplayTransform::TryMerge(nsDisplayLi
  * @param aBoundsOverride (optional) Force the frame bounds to be the
  *        specified bounds.
  * @return The smallest rectangle containing the image of the transformed
  *         rectangle.
  */
 nsRect nsDisplayTransform::TransformRect(const nsRect &aUntransformedBounds,
                                          const nsIFrame* aFrame,
                                          const nsPoint &aOrigin,
-                                         const nsRect* aBoundsOverride,
-                                         bool aPreserves3D)
+                                         const nsRect* aBoundsOverride)
 {
   NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
 
   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
   return nsLayoutUtils::MatrixTransformRect
     (aUntransformedBounds,
-     (aPreserves3D ?
-      GetResultingTransformMatrixP3D(aFrame, aOrigin, factor,
-                                     aBoundsOverride) :
-      GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride)),
+     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!");
 
   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
   return nsLayoutUtils::MatrixTransformRectOut
     (aUntransformedBounds,
-     GetResultingTransformMatrixP3D(aFrame, aOrigin, factor, aBoundsOverride),
+     GetResultingTransformMatrix(aFrame, aOrigin, factor, aBoundsOverride),
      factor);
 }
 
 bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
                                          const nsRect &aChildBounds,
                                          const nsIFrame* aFrame,
                                          const nsPoint &aOrigin,
-                                         nsRect *aOutRect,
-                                         bool aPreserves3D)
+                                         nsRect *aOutRect)
 {
   NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
 
   float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
 
-  Matrix4x4 transform = aPreserves3D ?
-    GetResultingTransformMatrixP3D(aFrame, aOrigin, factor, nullptr) :
-    GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
+  Matrix4x4 transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
   if (transform.IsSingular()) {
     return false;
   }
 
   Rect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
               NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
               NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
               NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -111,62 +111,25 @@ typedef mozilla::EnumSet<mozilla::gfx::C
  * nsIFrame::BuildDisplayList.
  * It contains the parameters that don't change from frame to frame and manages
  * the display list memory using a PLArena. It also establishes the reference
  * coordinate system for all display list items. Some of the parameters are
  * available from the prescontext/presshell, but we copy them into the builder
  * for faster/more convenient access.
  */
 class nsDisplayListBuilder {
-  /**
-   * This manages status of a 3d context to collect visible rects of
-   * descendants and passing a dirty rect.
-   *
-   * Since some transforms maybe singular, passing visible rects or
-   * the dirty rect level by level from parent to children may get a
-   * wrong result, being different from the result of appling with
-   * effective transform directly.
-   *
-   * nsFrame::BuildDisplayListForStackingContext() uses
-   * AutoPreserves3DContext to install an instance on the builder.
-   *
-   * \see AutoAccumulateTransform, AutoAccumulateRect,
-   *      AutoPreserves3DContext, Accumulate, GetCurrentTransform,
-   *      StartRoot.
-   */
-  class Preserves3DContext {
-  public:
-    typedef mozilla::gfx::Matrix4x4 Matrix4x4;
-
-    Preserves3DContext() {}
-    Preserves3DContext(const Preserves3DContext &aOther)
-      : mAccumulatedTransform()
-      , mAccumulatedRect()
-      , mAccumulatedRectLevels(0)
-      , mDirtyRect(aOther.mDirtyRect) {}
-
-    // Accmulate transforms of ancestors on the preserves-3d chain.
-    Matrix4x4 mAccumulatedTransform;
-    // Accmulate visible rect of descendants in the preserves-3d context.
-    nsRect mAccumulatedRect;
-    // How far this frame is from the root of the current 3d context.
-    int mAccumulatedRectLevels;
-    nsRect mDirtyRect;
-  };
-
 public:
   typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
   typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
   typedef mozilla::DisplayItemClip DisplayItemClip;
   typedef mozilla::DisplayListClipState DisplayListClipState;
   typedef nsIWidget::ThemeGeometry ThemeGeometry;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
-  typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 
   /**
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aMode encodes what the builder is being used for.
    * @param aBuildCaret whether or not we should include the caret in any
    * display lists that we make.
    */
@@ -770,96 +733,16 @@ public:
       mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID;
       mBuilder->mCurrentScrollbarFlags = 0;
       mBuilder->mCurrentScrollbarWillHaveLayer = false;
     }
   private:
     nsDisplayListBuilder* mBuilder;
   };
 
-  /**
-   * A helper class to track current effective transform for items.
-   *
-   * For frames that is Combines3DTransformWithAncestors(), we need to
-   * apply all transforms of ancestors on the same preserves3D chain
-   * on the bounds of current frame to the coordination of the 3D
-   * context root.  The 3D context root computes it's bounds from
-   * these transformed bounds.
-   */
-  class AutoAccumulateTransform;
-  friend class AutoAccumulateTransform;
-  class AutoAccumulateTransform {
-  public:
-    typedef mozilla::gfx::Matrix4x4 Matrix4x4;
-
-    explicit AutoAccumulateTransform(nsDisplayListBuilder* aBuilder)
-      : mBuilder(aBuilder)
-      , mSavedTransform(aBuilder->mPreserves3DCtx.mAccumulatedTransform) {}
-
-    ~AutoAccumulateTransform() {
-      mBuilder->mPreserves3DCtx.mAccumulatedTransform = mSavedTransform;
-    }
-
-    void Accumulate(const Matrix4x4& aTransform) {
-      mBuilder->mPreserves3DCtx.mAccumulatedTransform =
-        aTransform * mBuilder->mPreserves3DCtx.mAccumulatedTransform;
-    }
-
-    const Matrix4x4& GetCurrentTransform() {
-      return mBuilder->mPreserves3DCtx.mAccumulatedTransform;
-    }
-
-    void StartRoot() {
-      mBuilder->mPreserves3DCtx.mAccumulatedTransform = Matrix4x4();
-    }
-
-  private:
-    nsDisplayListBuilder* mBuilder;
-    Matrix4x4 mSavedTransform;
-  };
-
-  /**
-   * A helper class to collect bounds rects of descendants.
-   *
-   * For a 3D context root, it's bounds is computed from the bounds of
-   * descendants.  If we transform bounds frame by frame applying
-   * transforms, the bounds may turn to empty for any singular
-   * transform on the path, but it is not empty for the accumulated
-   * transform.
-   */
-  class AutoAccumulateRect;
-  friend class AutoAccumulateRect;
-  class AutoAccumulateRect {
-  public:
-    explicit AutoAccumulateRect(nsDisplayListBuilder* aBuilder)
-      : mBuilder(aBuilder)
-      , mSavedRect(aBuilder->mPreserves3DCtx.mAccumulatedRect) {
-      aBuilder->mPreserves3DCtx.mAccumulatedRect = nsRect();
-      aBuilder->mPreserves3DCtx.mAccumulatedRectLevels++;
-    }
-    ~AutoAccumulateRect() {
-      mBuilder->mPreserves3DCtx.mAccumulatedRect = mSavedRect;
-      mBuilder->mPreserves3DCtx.mAccumulatedRectLevels--;
-    }
-
-  private:
-    nsDisplayListBuilder* mBuilder;
-    nsRect mSavedRect;
-  };
-
-  void AccumulateRect(const nsRect& aRect) {
-    mPreserves3DCtx.mAccumulatedRect.UnionRect(mPreserves3DCtx.mAccumulatedRect, aRect);
-  }
-  const nsRect& GetAccumulatedRect() {
-    return mPreserves3DCtx.mAccumulatedRect;
-  }
-  int GetAccumulatedRectLevels() {
-    return mPreserves3DCtx.mAccumulatedRectLevels;
-  }
-
   // Helpers for tables
   nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
   void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
 
   struct OutOfFlowDisplayData {
     OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip,
                          const nsRect &aDirtyRect)
       : mContainingBlockClip(aContainingBlockClip)
@@ -1011,56 +894,16 @@ public:
 
   /**
    * Retrieve the stored dirty rect for the scrolled contents of aScrollableFrame.
    * @param  aScrollableFrame the scroll frame
    * @return                  the dirty rect, relative to aScrollableFrame's *reference frame*
    */
   nsRect GetDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame) const;
 
-  /**
-   * A helper class to install/restore nsDisplayListBuilder::mPreserves3DCtx.
-   *
-   * mPreserves3DCtx is used by class AutoAccumulateTransform &
-   * AutoAccumulateRect to passing data between frames in the 3D
-   * context.  If a frame create a new 3D context, it should restore
-   * the value of mPreserves3DCtx before returning back to the parent.
-   * This class do it for the users.
-   */
-  class AutoPreserves3DContext;
-  friend class AutoPreserves3DContext;
-  class AutoPreserves3DContext {
-  public:
-    explicit AutoPreserves3DContext(nsDisplayListBuilder* aBuilder)
-      : mBuilder(aBuilder)
-      , mSavedCtx(aBuilder->mPreserves3DCtx) {}
-    ~AutoPreserves3DContext() {
-      mBuilder->mPreserves3DCtx = mSavedCtx;
-    }
-
-  private:
-    nsDisplayListBuilder* mBuilder;
-    Preserves3DContext mSavedCtx;
-  };
-
-  const nsRect GetPreserves3DDirtyRect(const nsIFrame *aFrame) const {
-    nsRect dirty = mPreserves3DCtx.mDirtyRect;
-    // Translate the dirty rect to make it positioned relative to the
-    // origin of aFrame.
-    const nsIFrame *rootPreserves3D = aFrame;
-    while (rootPreserves3D && rootPreserves3D->Combines3DTransformWithAncestors()) {
-      dirty.MoveBy(-rootPreserves3D->GetPosition());
-      rootPreserves3D = rootPreserves3D->GetParent();
-    }
-    return dirty;
-  }
-  void SetPreserves3DDirtyRect(const nsRect &aDirtyRect) {
-    mPreserves3DCtx.mDirtyRect = aDirtyRect;
-  }
-
 private:
   void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
                                     const nsRect& aDirtyRect);
 
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
@@ -1154,17 +997,16 @@ private:
   nsDisplayList*                 mPendingScrollInfoItems;
   nsDisplayList*                 mCommittedScrollInfoItems;
   nsTArray<DisplayItemClip*>     mDisplayItemClipsToDestroy;
   Mode                           mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   uint32_t                       mCurrentScrollbarFlags;
   BlendModeSet                   mContainedBlendModes;
-  Preserves3DContext             mPreserves3DCtx;
   bool                           mIsBuildingScrollbar;
   bool                           mCurrentScrollbarWillHaveLayer;
   bool                           mBuildCaret;
   bool                           mIgnoreSuppression;
   bool                           mHadToIgnoreSuppression;
   bool                           mIsAtRootOfPseudoStackingContext;
   bool                           mIncludeAllOutOfFlows;
   bool                           mDescendIntoSubdocuments;
@@ -1731,20 +1573,16 @@ 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
@@ -3602,40 +3440,16 @@ private:
  * INVARIANT: The wrapped frame is transformed or we supplied a transform getter
  * function.
  * INVARIANT: The wrapped frame is non-null.
  */ 
 class nsDisplayTransform: public nsDisplayItem
 {
   typedef mozilla::gfx::Matrix4x4 Matrix4x4;
   typedef mozilla::gfx::Point3D Point3D;
-
-  /*
-   * Avoid doing UpdateBounds() during construction.
-   */
-  class StoreList : public nsDisplayWrapList {
-  public:
-    StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-              nsDisplayList* aList) :
-      nsDisplayWrapList(aBuilder, aFrame, aList) {}
-    StoreList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
-              nsDisplayItem* aItem) :
-      nsDisplayWrapList(aBuilder, aFrame, aItem) {}
-    virtual ~StoreList() {}
-
-    virtual void UpdateBounds(nsDisplayListBuilder* aBuilder) override {}
-    virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
-                             bool* aSnap) override {
-      // The bounds should not be computed until now, because we don't
-      // get accmulated transform before.
-      nsDisplayWrapList::UpdateBounds(aBuilder);
-      return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
-    }
-  };
-
 public:
   /**
    * Returns a matrix (in pixels) for the current frame. The matrix should be relative to
    * the current frame's coordinate space.
    *
    * @param aFrame The frame to compute the transform for.
    * @param aAppUnitsPerPixel The number of app units per graphics unit.
    */
@@ -3648,19 +3462,16 @@ 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
 
@@ -3717,21 +3528,16 @@ public:
     return mChildrenVisibleRect;
   }
 
   enum {
     INDEX_MAX = UINT32_MAX >> nsDisplayItem::TYPE_BITS
   };
 
   const Matrix4x4& GetTransform();
-  /**
-   * Return the transform that is aggregation of all transform on the
-   * preserves3d chain.
-   */
-  const Matrix4x4& GetAccumulatedPreserved3DTransform();
 
   float GetHitDepthAtPoint(nsDisplayListBuilder* aBuilder, const nsPoint& aPoint);
 
   /**
    * TransformRect takes in as parameters a rectangle (in aFrame's coordinate
    * space) and returns the smallest rectangle (in aFrame's coordinate space)
    * containing the transformed image of that rectangle.  That is, it takes
    * the four corners of the rectangle, transforms them according to the
@@ -3746,33 +3552,31 @@ public:
    *        coordinate space.
    * @param aBoundsOverride (optional) Rather than using the frame's computed
    *        bounding rect as frame bounds, use this rectangle instead.  Pass
    *        nullptr (or nothing at all) to use the default.
    */
   static nsRect TransformRect(const nsRect &aUntransformedBounds, 
                               const nsIFrame* aFrame,
                               const nsPoint &aOrigin,
-                              const nsRect* aBoundsOverride = nullptr,
-                              bool aPreserves3D = true);
+                              const nsRect* aBoundsOverride = nullptr);
 
   static nsRect TransformRectOut(const nsRect &aUntransformedBounds, 
                                  const nsIFrame* aFrame,
                                  const nsPoint &aOrigin,
                                  const nsRect* aBoundsOverride = nullptr);
 
   /* UntransformRect is like TransformRect, except that it inverts the
    * transform.
    */
   static bool UntransformRect(const nsRect &aTransformedBounds,
                               const nsRect &aChildBounds,
                               const nsIFrame* aFrame,
                               const nsPoint &aOrigin,
-                              nsRect *aOutRect,
-                              bool aPreserves3D = true);
+                              nsRect *aOutRect);
 
   bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
                               nsRect* aOutRect);
 
   static Point3D GetDeltaToTransformOrigin(const nsIFrame* aFrame,
                                            float aAppUnitsPerPixel,
                                            const nsRect* aBoundsOverride);
 
@@ -3832,22 +3636,16 @@ public:
                                                const nsRect* aBoundsOverride = nullptr,
                                                nsIFrame** aOutAncestor = nullptr,
                                                bool aOffsetByOrigin = false);
   static Matrix4x4 GetResultingTransformMatrix(const FrameTransformProperties& aProperties,
                                                const nsPoint& aOrigin,
                                                float aAppUnitsPerPixel,
                                                const nsRect* aBoundsOverride = nullptr,
                                                nsIFrame** aOutAncestor = nullptr);
-  static Matrix4x4 GetResultingTransformMatrixP3D(const nsIFrame* aFrame,
-                                                  const nsPoint& aOrigin,
-                                                  float aAppUnitsPerPixel,
-                                                  const nsRect* aBoundsOverride = nullptr,
-                                                  nsIFrame** aOutAncestor = nullptr,
-                                                  bool aOffsetByOrigin = false);
   /**
    * Return true when we should try to prerender the entire contents of the
    * transformed frame even when it's not completely visible (yet).
    */
   static bool ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
                                                 nsIFrame* aFrame,
                                                 bool aLogAnimations = false);
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
@@ -3860,53 +3658,35 @@ 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;
 
-  // Force the layer created for this item not to extend 3D context.
-  // See nsIFrame::BuildDisplayListForStackingContext()
-  void SetNoExtendContext() { mNoExtendContext = true; }
-
 private:
   void SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder);
   void Init(nsDisplayListBuilder* aBuilder);
 
   static Matrix4x4 GetResultingTransformMatrixInternal(const FrameTransformProperties& aProperties,
                                                        const nsPoint& aOrigin,
                                                        float aAppUnitsPerPixel,
                                                        const nsRect* aBoundsOverride,
                                                        nsIFrame** aOutAncestor,
-                                                       bool aOffsetByOrigin,
-                                                       bool aDoPreserves3D);
-
-  StoreList mStoredList;
+                                                       bool aOffsetByOrigin);
+
+  nsDisplayWrapList mStoredList;
   Matrix4x4 mTransform;
-  // Accumulated transform of ancestors on the preserves-3d chain.
-  Matrix4x4 mTransformPreserves3D;
   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;
-  // Be forced not to extend 3D context.  Since we don't create a
-  // transform item, a container layer, for every frames in a
-  // preserves3d context, the transform items of a child preserves3d
-  // context may extend the parent context not intented if the root of
-  // the child preserves3d context doesn't create a transform item.
-  // With this flags, we force the item not extending 3D context.
-  bool mNoExtendContext;
-  // mTransform have been presetted by the constructor.
-  bool mHasPresetTransform;
-  // True if mTransformPreserves3D have been initialized.
-  bool mTransformPreserves3DInited;
 };
 
 /**
  * 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/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2407,17 +2407,17 @@ nsLayoutUtils::GetTransformToAncestor(ns
 {
   nsIFrame* parent;
   Matrix4x4 ctm;
   if (aFrame == aAncestor) {
     return ctm;
   }
   ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
   while (parent && parent != aAncestor) {
-    if (!parent->Extend3DContext()) {
+    if (!parent->Preserves3DChildren()) {
       ctm.ProjectTo2D();
     }
     ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
   }
   return ctm;
 }
 
 gfxSize
@@ -2444,17 +2444,17 @@ GetTransformToAncestorExcludingAnimated(
   if (ActiveLayerTracker::IsScaleSubjectToAnimation(aFrame)) {
     return ctm;
   }
   ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
   while (parent && parent != aAncestor) {
     if (ActiveLayerTracker::IsScaleSubjectToAnimation(parent)) {
       return Matrix4x4();
     }
-    if (!parent->Extend3DContext()) {
+    if (!parent->Preserves3DChildren()) {
       ctm.ProjectTo2D();
     }
     ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
   }
   return ctm;
 }
 
 gfxSize
@@ -2669,17 +2669,17 @@ nsLayoutUtils::ClampRectToScrollFrames(n
 }
 
 bool
 nsLayoutUtils::GetLayerTransformForFrame(nsIFrame* aFrame,
                                          Matrix4x4* aTransform)
 {
   // FIXME/bug 796690: we can sometimes compute a transform in these
   // cases, it just increases complexity considerably.  Punt for now.
-  if (aFrame->Extend3DContext() || aFrame->HasTransformGetter()) {
+  if (aFrame->Preserves3DChildren() || aFrame->HasTransformGetter()) {
     return false;
   }
 
   nsIFrame* root = nsLayoutUtils::GetDisplayRootFrame(aFrame);
   if (root->HasAnyStateBits(NS_FRAME_UPDATE_LAYER_TREE)) {
     // Content may have been invalidated, so we can't reliably compute
     // the "layer transform" in general.
     return false;
@@ -6635,27 +6635,37 @@ nsLayoutUtils::GetDisplayRootFrame(nsIFr
   }
 }
 
 /* static */ nsIFrame*
 nsLayoutUtils::GetReferenceFrame(nsIFrame* aFrame)
 {
   nsIFrame *f = aFrame;
   for (;;) {
-    if (f->IsTransformed() || f->IsPreserve3DLeaf() || IsPopup(f)) {
+    if (f->IsTransformed() || IsPopup(f)) {
       return f;
     }
     nsIFrame* parent = GetCrossDocParentFrame(f);
     if (!parent) {
       return f;
     }
     f = parent;
   }
 }
 
+/* static */ nsIFrame*
+nsLayoutUtils::GetTransformRootFrame(nsIFrame* aFrame)
+{
+  nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
+  while (parent && parent->Preserves3DChildren()) {
+    parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
+  }
+  return parent;
+}
+
 /* static */ uint32_t
 nsLayoutUtils::GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
                                        const nsStyleFont* aStyleFont,
                                        const nsStyleText* aStyleText,
                                        nscoord aLetterSpacing)
 {
   uint32_t result = 0;
   if (aLetterSpacing != 0) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1956,28 +1956,40 @@ public:
   /**
    * Find the nearest "display root". This is the nearest enclosing
    * popup frame or the root prescontext's root frame.
    */
   static nsIFrame* GetDisplayRootFrame(nsIFrame* aFrame);
 
   /**
    * Get the reference frame that would be used when constructing a
-   * display item for this frame.  Rather than using their own frame
-   * as a reference frame.)
+   * display item for this frame.  (Note, however, that
+   * nsDisplayTransform use the reference frame appropriate for their
+   * GetTransformRootFrame(), rather than using their own frame as a
+   * reference frame.)
    *
    * This duplicates some of the logic of GetDisplayRootFrame above and
    * of nsDisplayListBuilder::FindReferenceFrameFor.
    *
    * If you have an nsDisplayListBuilder, you should get the reference
    * frame from it instead of calling this.
    */
   static nsIFrame* GetReferenceFrame(nsIFrame* aFrame);
 
   /**
+   * Get the parent of this frame, except if that parent is part of a
+   * preserve-3d hierarchy, get the parent of the root of the
+   * preserve-3d hierarchy.
+   *
+   * (This is used as the starting point for reference frame computation
+   * for nsDisplayTransform display items.)
+   */
+  static nsIFrame* GetTransformRootFrame(nsIFrame* aFrame);
+
+  /**
    * Get textrun construction flags determined by a given style; in particular
    * some combination of:
    * -- TEXT_DISABLE_OPTIONAL_LIGATURES if letter-spacing is in use
    * -- TEXT_OPTIMIZE_SPEED if the text-rendering CSS property and font size
    * and prefs indicate we should be optimizing for speed over quality
    */
   static uint32_t GetTextRunFlagsForStyle(nsStyleContext* aStyleContext,
                                           const nsStyleFont* aStyleFont,
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5718,17 +5718,17 @@ PresShell::MarkImagesInSubtreeVisible(ns
     if (usingDisplayport) {
       rect = displayPort;
     } else {
       rect = rect.Intersect(scrollFrame->GetScrollPortRect());
     }
     rect = scrollFrame->ExpandRectToNearlyVisible(rect);
   }
 
-  bool preserves3DChildren = aFrame->Extend3DContext();
+  bool preserves3DChildren = aFrame->Preserves3DChildren();
 
   // we assume all images in popups are visible elsewhere, so we skip them here
   const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
                                     nsIFrame::kSelectPopupList);
   for (nsIFrame::ChildListIterator childLists(aFrame);
        !childLists.IsDone(); childLists.Next()) {
     if (skip.Contains(childLists.CurrentID())) {
       continue;
@@ -5736,17 +5736,17 @@ PresShell::MarkImagesInSubtreeVisible(ns
 
     for (nsIFrame* child : childLists.CurrentList()) {
       nsRect r = rect - child->GetPosition();
       if (!r.IntersectRect(r, child->GetVisualOverflowRect())) {
         continue;
       }
       if (child->IsTransformed()) {
         // for children of a preserve3d element we just pass down the same dirty rect
-        if (!preserves3DChildren || !child->Combines3DTransformWithAncestors()) {
+        if (!preserves3DChildren || !child->Preserves3D()) {
           const nsRect overflow = child->GetVisualOverflowRectRelativeToSelf();
           nsRect out;
           if (nsDisplayTransform::UntransformRect(r, overflow, child, nsPoint(0,0), &out)) {
             r = out;
           } else {
             r.SetEmpty();
           }
         }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1116,17 +1116,17 @@ nsIFrame::HasOpacityInternal(float aThre
 bool
 nsIFrame::IsSVGTransformed(gfx::Matrix *aOwnTransforms,
                            gfx::Matrix *aFromParentTransforms) const
 {
   return false;
 }
 
 bool
-nsIFrame::Extend3DContext() const
+nsIFrame::Preserves3DChildren() const
 {
   const nsStyleDisplay* disp = StyleDisplay();
   if (disp->mTransformStyle != NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
       !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
     return false;
   }
 
   // If we're all scroll frame, then all descendants will be clipped, so we can't preserve 3d.
@@ -1136,19 +1136,19 @@ nsIFrame::Extend3DContext() const
 
   nsRect temp;
   return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
          !GetClipPropClipRect(disp, &temp, GetSize()) &&
          !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
 }
 
 bool
-nsIFrame::Combines3DTransformWithAncestors() const
-{
-  if (!GetParent() || !GetParent()->Extend3DContext()) {
+nsIFrame::Preserves3D() const
+{
+  if (!GetParent() || !GetParent()->Preserves3DChildren()) {
     return false;
   }
   return StyleDisplay()->HasTransform(this) || StyleDisplay()->BackfaceIsHidden();
 }
 
 bool
 nsIFrame::HasPerspective() const
 {
@@ -1782,22 +1782,136 @@ 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
+WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder,
+                           nsDisplayList *aList, nsDisplayList *aOutput,
+                           uint32_t& aIndex, nsDisplayList* aTemp)
+{
+  if (aIndex > nsDisplayTransform::INDEX_MAX) {
+    return NS_OK;
+  }
+
+  nsresult rv = NS_OK;
+  while (nsDisplayItem *item = aList->RemoveBottom()) {
+    nsIFrame *childFrame = item->Frame();
+
+    // We accumulate sequential items that aren't transforms into the 'temp' list
+    // and then flush this list into aOutput by wrapping the whole lot with a single
+    // nsDisplayTransform.
+
+    if (childFrame->GetParent() &&
+        (childFrame->GetParent()->Preserves3DChildren() || childFrame == aFrame)) {
+      switch (item->GetType()) {
+        case nsDisplayItem::TYPE_TRANSFORM: {
+          if (!aTemp->IsEmpty()) {
+            // Flush current aTemp contents
+            aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder,
+                aFrame, aTemp, aTemp->GetVisibleRect(), aIndex++));
+          }
+          // Override item's clipping with our current clip state (if any). Since we're
+          // bubbling up a preserve-3d transformed child to a preserve-3d parent,
+          // we can be sure the child doesn't have clip state of its own.
+          NS_ASSERTION(!item->GetClip().HasClip(), "Unexpected clip on item");
+          const DisplayItemClip* clip = aBuilder->ClipState().GetCurrentCombinedClip(aBuilder);
+          if (clip) {
+            item->SetClip(aBuilder, *clip);
+          }
+          aOutput->AppendToTop(item);
+          break;
+        }
+        case nsDisplayItem::TYPE_WRAP_LIST: {
+          nsDisplayWrapList *list = static_cast<nsDisplayWrapList*>(item);
+          rv = WrapPreserve3DListInternal(aFrame, aBuilder,
+              list->GetChildren(), aOutput, aIndex, aTemp);
+          list->~nsDisplayWrapList();
+          break;
+        }
+        case nsDisplayItem::TYPE_OPACITY: {
+          if (!aTemp->IsEmpty()) {
+            // Flush current aTemp contents
+            aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder,
+                aFrame, aTemp, aTemp->GetVisibleRect(), aIndex++));
+          }
+          nsDisplayOpacity *opacity = static_cast<nsDisplayOpacity*>(item);
+          nsDisplayList output;
+          // Call GetChildren, not GetSameCoordinateSystemChildren, because
+          // the preserve-3d children of 'opacity' are temporarily not in the
+          // same coordinate system as the opacity --- until this wrapping is done.
+          rv = WrapPreserve3DListInternal(aFrame, aBuilder,
+              opacity->GetChildren(), &output, aIndex, aTemp);
+          if (!aTemp->IsEmpty()) {
+            output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder,
+                aFrame, aTemp, aTemp->GetVisibleRect(), aIndex++));
+          }
+
+          opacity->SetVisibleRect(output.GetVisibleRect());
+          opacity->SetReferenceFrame(output.GetBottom()->ReferenceFrame());
+          opacity->GetChildren()->AppendToTop(&output);
+          opacity->UpdateBounds(aBuilder);
+          aOutput->AppendToTop(item);
+          break;
+        }
+        default: {
+          if (childFrame->StyleDisplay()->BackfaceIsHidden()) {
+            if (!aTemp->IsEmpty()) {
+              aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder,
+                  aFrame, aTemp, aTemp->GetVisibleRect(), aIndex++));
+            }
+
+            aOutput->AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder,
+                childFrame, item, item->GetVisibleRect(), aIndex++));
+          } else {
+            aTemp->AppendToTop(item);
+          }
+          break;
+        }
+      } 
+    } else {
+      aTemp->AppendToTop(item);
+    }
+ 
+    if (NS_FAILED(rv) || !item || aIndex > nsDisplayTransform::INDEX_MAX)
+      return rv;
+  }
+    
+  return NS_OK;
+}
+
 static bool
 IsScrollFrameActive(nsDisplayListBuilder* aBuilder, nsIScrollableFrame* aScrollableFrame)
 {
   return aScrollableFrame && aScrollableFrame->IsScrollingActive(aBuilder);
 }
 
+static nsresult
+WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
+                   nsDisplayList *aList)
+{
+  uint32_t index = 0;
+  nsDisplayList temp;
+  nsDisplayList output;
+  nsresult rv = WrapPreserve3DListInternal(aFrame, aBuilder, aList, &output,
+      index, &temp);
+
+  if (!temp.IsEmpty()) {
+    output.AppendToTop(new (aBuilder) nsDisplayTransform(aBuilder, aFrame,
+        &temp, temp.GetVisibleRect(), index++));
+  }
+
+  aList->AppendToTop(&output);
+  return rv;
+}
+
 class AutoSaveRestoreBlendMode
 {
   nsDisplayListBuilder& mBuilder;
   EnumSet<gfx::CompositionOp> mSavedBlendModes;
 public:
   explicit AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder)
     : mBuilder(aBuilder)
     , mSavedBlendModes(aBuilder.ContainedBlendModes())
@@ -1933,55 +2047,40 @@ nsIFrame::BuildDisplayListForStackingCon
       return;
     }
   }
 
   if (disp->mWillChangeBitField != 0) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
-  Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
-  if (Extend3DContext() && !Combines3DTransformWithAncestors()) {
-    // Start a new preserves3d context to keep informations on
-    // nsDisplayListBuilder.
-    autoPreserves3DContext.emplace(aBuilder);
-    // Save dirty rect on the builder to avoid being distorted for
-    // 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;
 
   bool inTransform = aBuilder->IsInTransform();
   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())) {
+        nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) {
       dirtyRect = overflow;
     } else {
-      if (overflow.IsEmpty()) {
+      if (overflow.IsEmpty() && !Preserves3DChildren()) {
         return;
       }
 
       nsRect untransformedDirtyRect;
       if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
-            nsPoint(0,0), &untransformedDirtyRect, false)) {
+            nsPoint(0,0), &untransformedDirtyRect)) {
         dirtyRect = untransformedDirtyRect;
       } 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;
@@ -2033,21 +2132,20 @@ nsIFrame::BuildDisplayListForStackingCon
     nsRect clipPropClip;
     if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
                               nestedClipState)) {
       dirtyRect.IntersectRect(dirtyRect, clipPropClip);
     }
 
     MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
 
-    // Extend3DContext() also guarantees that applyAbsPosClipping and usingSVGEffects are false
+    // 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);
-      aBuilder->MarkPreserve3DFramesForDisplayList(this, dirty);
+    if (Preserves3DChildren()) {
+      aBuilder->MarkPreserve3DFramesForDisplayList(this, aDirtyRect);
     }
 
     if (aBuilder->IsBuildingLayerEventRegions()) {
       nsDisplayLayerEventRegions* eventRegions =
         new (aBuilder) nsDisplayLayerEventRegions(aBuilder, this);
       eventRegions->AddFrame(aBuilder, this);
       aBuilder->SetLayerEventRegions(eventRegions);
       set.BorderBackground()->AppendNewToTop(eventRegions);
@@ -2163,69 +2261,26 @@ 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(), &toOuterReferenceFrame);
+      aBuilder->FindReferenceFrameFor(nsLayoutUtils::GetTransformRootFrame(this));
     buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
       GetOffsetToCrossDoc(outerReferenceFrame));
 
-    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 (Preserves3DChildren()) {
+      WrapPreserve3DList(this, aBuilder, &resultList);
+    } else {
+      resultList.AppendNewToTop(
+        new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect));
     }
   }
 
   /* 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));
@@ -2358,17 +2413,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     } else {
       // The out-of-flow frame did not intersect the dirty area. We may still
       // need to traverse into it, since it may contain placeholders we need
       // to enter to reach other out-of-flow frames that are visible.
       dirty.SetEmpty();
     }
     pseudoStackingContext = true;
   }
-  if (child->Combines3DTransformWithAncestors()) {
+  if (child->Preserves3D()) {
     nsRect* savedDirty = static_cast<nsRect*>
       (child->Properties().Get(nsDisplayListBuilder::Preserve3DDirtyRectProperty()));
     if (savedDirty) {
       dirty = *savedDirty;
     } else {
       dirty.SetEmpty();
     }
   }
@@ -5805,20 +5860,20 @@ nsIFrame::ListGeneric(nsACString& aTo, c
     aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
   }
   if (IsTransformed()) {
     aTo += nsPrintfCString(" transformed");
   }
   if (ChildrenHavePerspective()) {
     aTo += nsPrintfCString(" perspective");
   }
-  if (Extend3DContext()) {
+  if (Preserves3DChildren()) {
     aTo += nsPrintfCString(" preserves-3d-children");
   }
-  if (Combines3DTransformWithAncestors()) {
+  if (Preserves3D()) {
     aTo += nsPrintfCString(" preserves-3d");
   }
   if (mContent) {
     aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
   }
   aTo += nsPrintfCString(" [sc=%p", static_cast<void*>(mStyleContext));
   if (mStyleContext) {
     nsIAtom* pseudoTag = mStyleContext->GetPseudo();
@@ -7462,39 +7517,39 @@ UnionBorderBoxes(nsIFrame* aFrame, bool 
     if (skip.Contains(childLists.CurrentID())) {
       continue;
     }
 
     nsFrameList children = childLists.CurrentList();
     for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
       nsIFrame* child = e.get();
       // Note that passing |true| for aApplyTransform when
-      // child->Combines3DTransformWithAncestors() is incorrect if our
-      // aApplyTransform is false... but the opposite would be as
-      // well.  This is because elements within a preserve-3d scene
-      // are always transformed up to the top of the scene.  This
-      // means we don't have a mechanism for getting a transform up to
-      // an intermediate point within the scene.  We choose to
-      // over-transform rather than under-transform because this is
-      // consistent with other overflow areas.
+      // child->Preserves3D() is incorrect if our aApplyTransform is
+      // false... but the opposite would be as well.  This is because
+      // elements within a preserve-3d scene are always transformed up
+      // to the top of the scene.  This means we don't have a
+      // mechanism for getting a transform up to an intermediate point
+      // within the scene.  We choose to over-transform rather than
+      // under-transform because this is consistent with other
+      // overflow areas.
       nsRect childRect = UnionBorderBoxes(child, true) +
                          child->GetPosition();
 
       if (hasClipPropClip) {
         // Intersect with the clip before transforming.
         childRect.IntersectRect(childRect, clipPropClipRect);
       }
 
       // Note that we transform each child separately according to
       // aFrame's transform, and then union, which gives a different
       // (smaller) result from unioning and then transforming the
       // union.  This doesn't match the way we handle overflow areas
       // with 2-D transforms, though it does match the way we handle
       // overflow areas in preserve-3d 3-D scenes.
-      if (doTransform && !child->Combines3DTransformWithAncestors()) {
+      if (doTransform && !child->Preserves3D()) {
         childRect = nsDisplayTransform::TransformRect(childRect, aFrame,
                                                       nsPoint(0, 0), &bounds);
       }
       u.UnionRectEdges(u, childRect);
     }
   }
 
   return u;
@@ -7553,17 +7608,17 @@ ComputeAndIncludeOutlineArea(nsIFrame* a
       // UnionBorderBoxes for some of the subtlety here.
       for (nsIFrame *f = frameForArea, *parent = f->GetParent();
            /* see middle of loop */;
            f = parent, parent = f->GetParent()) {
         r += f->GetPosition();
         if (parent == aFrame) {
           break;
         }
-        if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
+        if (parent->IsTransformed() && !f->Preserves3D()) {
           r = nsDisplayTransform::TransformRect(r, parent, nsPoint(0, 0));
         }
       }
 
       innerRect.UnionRect(innerRect, r);
     }
   }
 
@@ -7601,17 +7656,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
                                  nsSize aNewSize, nsSize* aOldSize)
 {
   NS_ASSERTION(FrameMaintainsOverflow(),
                "Don't call - overflow rects not maintained on these SVG frames");
 
   nsRect bounds(nsPoint(0, 0), aNewSize);
   // Store the passed in overflow area if we are a preserve-3d frame or we have
   // a transform, and it's not just the frame bounds.
-  if (Combines3DTransformWithAncestors() || IsTransformed()) {
+  if (Preserves3D() || IsTransformed()) {
     if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
       nsOverflowAreas* initial =
         static_cast<nsOverflowAreas*>(Properties().Get(nsIFrame::InitialOverflowProperty()));
       if (!initial) {
         Properties().Set(nsIFrame::InitialOverflowProperty(),
                          new nsOverflowAreas(aOverflowAreas));
       } else if (initial != &aOverflowAreas) {
@@ -7714,17 +7769,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
      * 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 (Extend3DContext()) {
+    if (Preserves3DChildren()) {
       ComputePreserve3DChildrenOverflow(aOverflowAreas, newBounds);
     } else if (sizeChanged && ChildrenHavePerspective()) {
       RecomputePerspectiveChildrenOverflow(this, &newBounds);
     }
   } else {
     Properties().Delete(nsIFrame::PreTransformOverflowAreasProperty());
     if (ChildrenHavePerspective() && sizeChanged) {
       nsRect newBounds(nsPoint(0, 0), aNewSize);
@@ -7804,19 +7859,19 @@ RecomputePreserve3DChildrenOverflow(nsIF
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
       if (!child->FrameMaintainsOverflow()) {
         continue; // frame does not maintain overflow rects
       }
-      if (child->Extend3DContext()) {
+      if (child->Preserves3DChildren()) {
         RecomputePreserve3DChildrenOverflow(child, nullptr);
-      } else if (child->Combines3DTransformWithAncestors()) {
+      } else if (child->Preserves3D()) {
         nsOverflowAreas* overflow = 
           static_cast<nsOverflowAreas*>(child->Properties().Get(nsIFrame::InitialOverflowProperty()));
         nsRect bounds(nsPoint(0, 0), child->GetSize());
         if (overflow) {
           nsOverflowAreas overflowCopy = *overflow;
           child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
         } else {
           nsOverflowAreas boundsOverflow;
@@ -7852,33 +7907,33 @@ nsIFrame::ComputePreserve3DChildrenOverf
 {
   // 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.
 
   // If we're the top frame in a preserve 3d chain then we need to recalculate the overflow
   // areas of all our children since they will have used our size/offset which was invalid at
   // the time.
-  if (!Combines3DTransformWithAncestors()) {
+  if (!Preserves3D()) {
     RecomputePreserve3DChildrenOverflow(this, &aBounds);
   }
 
   nsRect childVisual;
   nsRect childScrollable;
   nsIFrame::ChildListIterator lists(this);
   for (; !lists.IsDone(); lists.Next()) {
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsIFrame* child = childFrames.get();
       nsPoint offset = child->GetPosition();
       nsRect visual = child->GetVisualOverflowRect();
       nsRect scrollable = child->GetScrollableOverflowRect();
       visual.MoveBy(offset);
       scrollable.MoveBy(offset);
-      if (child->Combines3DTransformWithAncestors()) {
+      if (child->Preserves3D()) {
         childVisual = childVisual.Union(visual);
         childScrollable = childScrollable.Union(scrollable);
       } else {
         childVisual = 
           childVisual.Union(nsDisplayTransform::TransformRect(visual, 
                             this, nsPoint(0,0), &aBounds));
         childScrollable = 
           childScrollable.Union(nsDisplayTransform::TransformRect(scrollable,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1242,32 +1242,28 @@ public:
    * aOwnTransforms will be set to these transforms. If aFromParentTransforms
    * is non-null and the frame has an SVG parent with children-only transforms,
    * then aFromParentTransforms will be set to these transforms.
    */
   virtual bool IsSVGTransformed(Matrix *aOwnTransforms = nullptr,
                                 Matrix *aFromParentTransforms = nullptr) const;
 
   /**
-   * Returns whether this frame will attempt to extend the 3d transforms of its
+   * Returns whether this frame will attempt to preserve the 3d transforms of its
    * children. This requires transform-style: preserve-3d, as well as no clipping
    * or svg effects.
    */
-  bool Extend3DContext() const;
+  bool Preserves3DChildren() const;
 
   /**
-   * Returns whether this frame has a parent that Extend3DContext() and has
+   * Returns whether this frame has a parent that Preserves3DChildren() and has
    * its own transform (or hidden backface) to be combined with the parent's
    * transform.
    */
-  bool Combines3DTransformWithAncestors() const;
-
-  bool IsPreserve3DLeaf() const {
-    return Combines3DTransformWithAncestors() && !Extend3DContext();
-  }
+  bool Preserves3D() const;
 
   bool HasPerspective() const;
 
   bool ChildrenHavePerspective() const;
 
   // Calculate the overflow size of all child frames, taking preserve-3d into account
   void ComputePreserve3DChildrenOverflow(nsOverflowAreas& aOverflowAreas, const nsRect& aBounds);
 
@@ -3038,20 +3034,16 @@ NS_PTR_TO_INT32(frame->Properties().Get(
 
   /**
    * Returns the content node within the anonymous content that this frame
    * generated and which corresponds to the specified pseudo-element type,
    * or nullptr if there is no such anonymous content.
    */
   virtual mozilla::dom::Element* GetPseudoElement(nsCSSPseudoElements::Type aType);
 
-  bool BackfaceIsHidden() {
-    return StyleDisplay()->BackfaceIsHidden();
-  }
-
 protected:
   // Members
   nsRect           mRect;
   nsIContent*      mContent;
   nsStyleContext*  mStyleContext;
 private:
   nsContainerFrame* mParent;
   nsIFrame*        mNextSibling;  // doubly-linked list of frames
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1654,17 +1654,17 @@ fuzzy-if(Android&&AndroidVersion>=15,8,3
 == 630835-1.html about:blank
 == 631352-1.html 631352-1-ref.html
 skip-if(!haveTestPlugin) skip-if(B2G||Mulet) fails-if(Android) fuzzy-if(winWidget&&!layersGPUAccelerated,102,535)  == 632423-1.html 632423-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Android||B2G||Mulet) random-if(winWidget) == 632781-verybig.html 632781-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 632781-normalsize.html 632781-ref.html
 fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,559) == 633344-1.html 633344-1-ref.html # bug 1103623
 == 634232-1.html 634232-1-ref.html
 fails-if(Android&&AndroidVersion<17&&AndroidVersion!=10) == 635302-1.html 635302-1-ref.html
-fuzzy(1,68) fuzzy-if(gtkWidget,1,70) skip-if(B2G||Mulet) fails-if(Android) == 635373-1.html 635373-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+fuzzy(1,68) skip-if(B2G||Mulet) fails-if(Android) == 635373-1.html 635373-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,118) == 635373-2.html 635373-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,116) == 635373-3.html 635373-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP(..) == 635639-1.html 635639-1-ref.html
 HTTP(..) == 635639-2.html 635639-2-ref.html
 random == 637597-1.html 637597-1-ref.html # bug 637597 was never really fixed!
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-1.html 637852-1-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-2.html 637852-2-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-3.html 637852-3-ref.html
@@ -1880,53 +1880,53 @@ skip-if(!B2G) == 1133905-3.html 1133905-
 skip-if(!B2G) == 1133905-4.html 1133905-ref.html
 skip-if(!B2G) == 1133905-5.html 1133905-ref.html
 skip-if(!B2G) == 1133905-6.html 1133905-ref.html
 skip-if(!B2G) == 1133905-1-v.html 1133905-ref-v.html
 skip-if(!B2G) == 1133905-2-v.html 1133905-ref-v.html
 skip-if(!B2G) == 1133905-3-v.html 1133905-ref-v.html
 skip-if(!B2G) == 1133905-4-v.html 1133905-ref-v.html
 skip-if(!B2G) fuzzy-if(B2G,61,336) == 1133905-5-v.html 1133905-ref-v.html
-skip-if(!B2G) fuzzy-if(B2G,77,287) == 1133905-6-v.html 1133905-ref-v.html
+skip-if(!B2G) fuzzy-if(B2G,61,480) == 1133905-6-v.html 1133905-ref-v.html
 skip-if(!B2G) == 1133905-1-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-2-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-3-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-4-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-5-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-6-h.html 1133905-ref-h.html
 skip-if(!B2G) == 1133905-1-vh.html 1133905-ref-vh.html
 skip-if(!B2G) == 1133905-2-vh.html 1133905-ref-vh.html
 skip-if(!B2G) == 1133905-3-vh.html 1133905-ref-vh.html
 skip-if(!B2G) == 1133905-4-vh.html 1133905-ref-vh.html
 skip-if(!B2G) fuzzy-if(B2G,102,720) == 1133905-5-vh.html 1133905-ref-vh.html
-skip-if(!B2G) fuzzy-if(B2G,102,945) == 1133905-6-vh.html 1133905-ref-vh.html
+skip-if(!B2G) fuzzy-if(B2G,101,1138) == 1133905-6-vh.html 1133905-ref-vh.html
 skip-if(!B2G) == 1133905-1-rtl.html 1133905-ref-rtl.html
 skip-if(!B2G) == 1133905-2-rtl.html 1133905-ref-rtl.html
 skip-if(!B2G) == 1133905-3-rtl.html 1133905-ref-rtl.html
 skip-if(!B2G) == 1133905-4-rtl.html 1133905-ref-rtl.html
 skip-if(!B2G) == 1133905-5-rtl.html 1133905-ref-rtl.html
 skip-if(!B2G) == 1133905-6-rtl.html 1133905-ref-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,20,177) == 1133905-1-v-rtl.html 1133905-ref-v-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,20,174) == 1133905-2-v-rtl.html 1133905-ref-v-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,23,175) == 1133905-1-v-rtl.html 1133905-ref-v-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,18,175) == 1133905-2-v-rtl.html 1133905-ref-v-rtl.html
 skip-if(!B2G) fuzzy-if(B2G,64,181) == 1133905-3-v-rtl.html 1133905-ref-v-rtl.html
 skip-if(!B2G) == 1133905-4-v-rtl.html 1133905-ref-v-rtl.html
 skip-if(!B2G) fuzzy-if(B2G,33,180) == 1133905-5-v-rtl.html 1133905-ref-v-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,77,219) == 1133905-6-v-rtl.html 1133905-ref-v-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,62,222) == 1133905-6-v-rtl.html 1133905-ref-v-rtl.html
 skip-if(!B2G) == 1133905-1-h-rtl.html 1133905-ref-h-rtl.html
 skip-if(!B2G) == 1133905-2-h-rtl.html 1133905-ref-h-rtl.html
 skip-if(!B2G) == 1133905-3-h-rtl.html 1133905-ref-h-rtl.html
 skip-if(!B2G) == 1133905-4-h-rtl.html 1133905-ref-h-rtl.html
 skip-if(!B2G) == 1133905-5-h-rtl.html 1133905-ref-h-rtl.html
 skip-if(!B2G) == 1133905-6-h-rtl.html 1133905-ref-h-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,20,177) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,62,176) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,23,175) == 1133905-1-vh-rtl.html 1133905-ref-vh-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,62,175) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!B2G) fuzzy-if(B2G,23,176) == 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!B2G) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!B2G) fuzzy-if(B2G,102,577) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html
-skip-if(!B2G) fuzzy-if(B2G,102,877) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
+skip-if(!B2G) fuzzy-if(B2G,101,887) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul
 == 1151145-1.html 1151145-1-ref.html
 == 1151306-1.html 1151306-1-ref.html
 == 1153845-1.html 1153845-1-ref.html
 == 1155828-1.html 1155828-1-ref.html
 == 1156129-1.html 1156129-1-ref.html
 == 1169331-1.html 1169331-1-ref.html
 fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html
--- a/layout/reftests/outline/reftest.list
+++ b/layout/reftests/outline/reftest.list
@@ -1,12 +1,12 @@
 == 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(d2d,16,96) == 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
 == outline-overflow-inlineblock-float.html outline-overflow-inlineblock-ref.html
 pref(layout.css.outline-style-auto.enabled,true) skip-if((!gtkWidget&&!winWidget&&!cocoaWidget)||Mulet) == outline-auto-001.html outline-auto-001-ref.html # only works on platforms that supports NS_THEME_FOCUS_OUTLINE # bug 1141511:  Disable some gtkWidget-dependant reftests on Mulet
 pref(layout.css.outline-style-auto.enabled,false) == outline-auto-001.html outline-auto-001-solid-ref.html
 == outline-initial-1a.html outline-initial-1-ref.html
 == outline-initial-1b.html outline-initial-1-ref.html
deleted file mode 100644
--- a/layout/reftests/transform-3d/preserves3d-nested-ref.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!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>
deleted file mode 100644
--- a/layout/reftests/transform-3d/preserves3d-nested.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!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>
--- a/layout/reftests/transform-3d/reftest.list
+++ b/layout/reftests/transform-3d/reftest.list
@@ -6,26 +6,26 @@
 # Check that the perspectve() transform function results in some visual changes
 != rotatex-perspective-1a.html rotatex-1-ref.html
 # Check that -moz-perspective results in visual changes to child transformed elements
 != rotatex-perspective-1b.html rotatex-1-ref.html
 # -moz-perspective should only apply to child elements
 == rotatex-perspective-1c.html rotatex-1-ref.html
 == rotatex-perspective-3a.html rotatex-perspective-3-ref.html
 == scalez-1a.html scalez-1-ref.html
-fuzzy-if(gtkWidget||winWidget,8,376) fuzzy-if(Android,8,441) fuzzy-if(cocoaWidget,17,4) == preserve3d-1a.html preserve3d-1-ref.html
+fuzzy-if(cocoaWidget,17,4) == preserve3d-1a.html preserve3d-1-ref.html
 == preserve3d-1b.html about:blank
 == preserve3d-clipped.html about:blank
 == preserve3d-2a.html preserve3d-2-ref.html
 == 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
+fuzzy-if(Android&&AndroidVersion>=15,4,300) == preserve3d-5a.html preserve3d-5-ref.html
 == scale3d-z.html scalez-1-ref.html
 fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX>=1008,224,924) == scale3d-all.html scale3d-1-ref.html # subpixel AA
 fuzzy-if(winWidget,102,580) fuzzy-if(d2d,143,681) fuzzy-if(OSX>=1008,224,924) == scale3d-all-separate.html scale3d-1-ref.html # subpixel AA
 == scale3d-xz.html scale3d-1-ref.html
 == translatez-1a.html translatez-1-ref.html
 != translatez-1b.html translatez-1-ref.html
 == translate3d-1a.html translate3d-1-ref.html
 == matrix3d-1a.html matrix3d-1-ref.html
@@ -59,12 +59,11 @@ fuzzy-if(winWidget&&!layersGPUAccelerate
 fails-if(!layersGPUAccelerated) == 1035611-1.html 1035611-1-ref.html # Bug 1072898 for !layersGPUAccelerated failures
 != 1157984-1.html about:blank # Bug 1157984
 fuzzy(3,99) == animate-cube-radians.html animate-cube-radians-ref.html # subpixel AA
 fuzzy(3,99) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated,16,6) fuzzy-if(Mulet,16,9) == animate-cube-radians-zoom.html animate-cube-radians-zoom-ref.html
 != animate-cube-radians-ref.html animate-cube-radians-zoom-ref.html
 fuzzy(3,99) == animate-cube-degrees.html animate-cube-degrees-ref.html # subpixel AA
 == animate-cube-degrees-zoom.html animate-cube-degrees-zoom-ref.html
 != animate-cube-degrees-ref.html animate-cube-degrees-zoom-ref.html
-fuzzy-if(B2G,15,100) fuzzy-if(Android,100,100) fuzzy-if(winWidget,90,200) fuzzy-if(B2G,88,100) fuzzy-if(cocoaWidget,90,100) fuzzy-if(gtkWidget,80,200) == preserves3d-nested.html preserves3d-nested-ref.html
 fuzzy-if(cocoaWidget,128,9) == animate-preserve3d-parent.html animate-preserve3d-ref.html # intermittently fuzzy on Mac
 fuzzy-if(cocoaWidget,128,9) == animate-preserve3d-child.html animate-preserve3d-ref.html # intermittently fuzzy on Mac
 == animate-backface-hidden.html about:blank
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -463,28 +463,28 @@ AnimationCollection::CanAnimatePropertyO
       message.AppendLiteral("Performance warning: Async animation of geometric property '");
       message.Append(nsCSSProps::GetStringValue(aProperty));
       message.AppendLiteral("' is disabled");
       LogAsyncAnimationFailure(message, aElement);
     }
     return false;
   }
   if (aProperty == eCSSProperty_transform) {
-    if (frame->Combines3DTransformWithAncestors() ||
-        frame->Extend3DContext()) {
+    if (frame->Preserves3D() ||
+        frame->Preserves3DChildren()) {
       if (shouldLog) {
         nsCString message;
         message.AppendLiteral("Gecko bug: Async animation of 'preserve-3d' transforms is not supported.  See bug 779598");
         LogAsyncAnimationFailure(message, aElement);
       }
       return false;
     }
     // Note that testing BackfaceIsHidden() is not a sufficient test for
     // what we need for animating backface-visibility correctly if we
-    // remove the above test for Extend3DContext(); that would require
+    // remove the above test for Preserves3DChildren(); that would require
     // looking at backface-visibility on descendants as well.
     if (frame->StyleDisplay()->BackfaceIsHidden()) {
       if (shouldLog) {
         nsCString message;
         message.AppendLiteral("Gecko bug: Async animation of 'backface-visibility: hidden' transforms is not supported.  See bug 1186204.");
         LogAsyncAnimationFailure(message, aElement);
       }
       return false;