Backed out 6 changesets (bug 1097464) for causing OS X 10.10 Reftest Errors
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 17 Sep 2015 16:24:43 +0200
changeset 295678 f436921d97f7a8986b2455ba9cdf2d0e4001f15b
parent 295677 94a7ca13ffa023d7fa5ef4728e6ef0d92ee2ad2e
child 295679 820865a790e47833d3b4e64bff4926de2d480584
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1097464
milestone43.0a1
backs outfbf63ce1ba882223a2eda0644bbecc23e8670d6b
fdaf4cfa6707273e5de6ac5cbfd8c27205c38a73
727ebd9f744a4c84e8acb408c05e77e2c79068ec
03f03a776042ce9309a83748265635bf691be4e5
5ec6364b87d715a3cd0158c6d2f8b59934ca29df
47d056b3af7fa5fca6174f3e050c7f4a69102ef2
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 6 changesets (bug 1097464) for causing OS X 10.10 Reftest Errors Backed out changeset fbf63ce1ba88 (bug 1097464) Backed out changeset fdaf4cfa6707 (bug 1097464) Backed out changeset 727ebd9f744a (bug 1097464) Backed out changeset 03f03a776042 (bug 1097464) Backed out changeset 5ec6364b87d7 (bug 1097464) Backed out changeset 47d056b3af7f (bug 1097464)
gfx/layers/LayerSorter.cpp
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/apz/reftests/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
@@ -595,96 +595,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)
 {
@@ -739,55 +680,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;
@@ -1236,32 +1162,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())
@@ -1269,41 +1179,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
@@ -1319,43 +1213,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()) {
@@ -1386,29 +1276,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()) {
@@ -2011,33 +1888,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;
-}
-
 void
 PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   Layer::PrintInfo(aStream, aPrefix);
   if (!mValidRegion.IsEmpty()) {
     AppendToString(aStream, mValidRegion, " [valid=", "]");
   }
 }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -780,17 +780,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,
 
     /**
@@ -800,23 +800,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)
@@ -1465,36 +1459,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.
@@ -1883,26 +1857,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; }
 
@@ -2137,18 +2103,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);
 
@@ -2164,22 +2128,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/reftests/reftest.list
+++ b/gfx/layers/apz/reftests/reftest.list
@@ -5,10 +5,10 @@ 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
--- 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
@@ -806,25 +806,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
@@ -848,51 +840,16 @@ BasicLayerManager::FlushGroup(PaintLayer
     CompositionOp op = GetEffectiveOperator(aPaintContext.mLayer);
     AutoSetOperator setOperator(aPaintContext.mTarget, ThebesOp(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);
 
@@ -922,34 +879,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) {
@@ -974,21 +924,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
@@ -687,17 +687,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
@@ -396,17 +396,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.
@@ -585,22 +584,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
@@ -649,17 +642,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).
   nsRefPtr<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
@@ -688,19 +680,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
@@ -746,17 +735,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
@@ -922,17 +910,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();
 
   /**
@@ -1127,28 +1114,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.
    */
@@ -2383,27 +2366,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
@@ -2434,25 +2411,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;
@@ -2634,34 +2607,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) ||
@@ -2823,28 +2793,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)
 {
@@ -3101,28 +3069,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.
     nsRefPtr<PaintedLayer> paintedLayer = CreatePaintedLayer(data);
@@ -3360,21 +3316,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()) {
@@ -3571,17 +3522,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;
@@ -3988,18 +3938,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.
@@ -4049,24 +3998,16 @@ ContainerState::ProcessDisplayItems(nsDi
       nsRefPtr<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();
       }
@@ -4121,42 +4062,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 ||
@@ -4173,19 +4096,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);
@@ -4807,20 +4728,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;
@@ -5035,17 +4954,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
@@ -5126,20 +5045,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
@@ -99,16 +99,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,
@@ -569,17 +575,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();
@@ -910,40 +919,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);
       }
     }
   }
 }
@@ -1826,28 +1825,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)));
@@ -3559,31 +3555,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)
@@ -3596,20 +3605,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();
 
@@ -4638,37 +4653,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());
@@ -4691,79 +4703,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
@@ -4828,19 +4812,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
@@ -4923,61 +4905,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:
@@ -5082,36 +5046,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)
@@ -5241,94 +5204,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;
   nsRefPtr<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);
@@ -5341,17 +5277,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)) {
@@ -5408,17 +5344,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)
    */
 
@@ -5485,18 +5421,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();
@@ -5505,69 +5440,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|
@@ -5683,59 +5574,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.
    */
@@ -758,96 +721,16 @@ public:
       // No need to restore old values because scrollbars cannot be nested.
       mBuilder->mCurrentScrollbarTarget = FrameMetrics::NULL_SCROLL_ID;
       mBuilder->mCurrentScrollbarFlags = 0;
     }
   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)
@@ -992,56 +875,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;
@@ -1135,17 +978,16 @@ private:
   nsDisplayList*                 mPendingScrollInfoItems;
   nsDisplayList*                 mCommittedScrollInfoItems;
   nsTArray<DisplayItemClip*>     mDisplayItemClipsToDestroy;
   Mode                           mMode;
   ViewID                         mCurrentScrollParentId;
   ViewID                         mCurrentScrollbarTarget;
   uint32_t                       mCurrentScrollbarFlags;
   BlendModeSet                   mContainedBlendModes;
-  Preserves3DContext             mPreserves3DCtx;
   bool                           mBuildCaret;
   bool                           mIgnoreSuppression;
   bool                           mHadToIgnoreSuppression;
   bool                           mIsAtRootOfPseudoStackingContext;
   bool                           mIncludeAllOutOfFlows;
   bool                           mDescendIntoSubdocuments;
   bool                           mSelectedFramesOnly;
   bool                           mAccurateVisibleRegions;
@@ -1710,20 +1552,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
@@ -3581,40 +3419,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.
    */
@@ -3627,19 +3441,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
 
@@ -3696,21 +3507,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
@@ -3725,33 +3531,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);
 
@@ -3811,22 +3615,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;
@@ -3839,53 +3637,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
@@ -2374,17 +2374,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
@@ -2411,17 +2411,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
@@ -2638,17 +2638,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;
@@ -6624,27 +6624,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
@@ -1955,28 +1955,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
@@ -1115,17 +1115,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.
@@ -1135,19 +1135,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
 {
@@ -1781,22 +1781,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())
@@ -1928,55 +2042,40 @@ nsIFrame::BuildDisplayListForStackingCon
       !needEventRegions) {
     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;
@@ -2028,21 +2127,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);
       aBuilder->SetLayerEventRegions(eventRegions);
       set.BorderBackground()->AppendNewToTop(eventRegions);
     }
@@ -2157,69 +2255,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));
@@ -2340,17 +2395,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();
     }
   }
@@ -5797,20 +5852,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();
@@ -7454,39 +7509,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;
@@ -7545,17 +7600,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);
     }
   }
 
@@ -7593,17 +7648,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
                                  nsSize aNewSize, nsSize* aOldSize)
 {
   NS_ASSERTION(FrameMaintainsOverflow(this),
                "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) {
@@ -7706,17 +7761,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);
@@ -7796,19 +7851,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 (!FrameMaintainsOverflow(child)) {
         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;
@@ -7844,33 +7899,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);
 
@@ -3029,20 +3025,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,1,61) == 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,1,60) == 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,11 +1,11 @@
 == 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||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
 == 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
@@ -532,28 +532,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;