Bug 1392200 - Add backface-visibility support for layers-free mode. r=kats draft
authorMorris Tseng <mtseng@mozilla.com>
Wed, 13 Sep 2017 11:25:58 +0800
changeset 667499 9981e320169ca292c02c651077c9650fa21d4e6b
parent 667497 34ac845fec26542cf00b6c896df96f85de5405b5
child 667517 34a4e1d29954ce5fff19750e4d005ba2875b0114
push id80732
push userbmo:mtseng@mozilla.com
push dateWed, 20 Sep 2017 06:24:10 +0000
reviewerskats
bugs1392200
milestone57.0a1
Bug 1392200 - Add backface-visibility support for layers-free mode. r=kats For layers-full mode, we set the backface-visibility to visible because visibility would be handled by FLB and layers. MozReview-Commit-ID: CUbeUabfC7K
gfx/layers/composite/TextureHost.cpp
gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/StackingContextHelper.cpp
gfx/layers/wr/StackingContextHelper.h
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderCanvasLayer.cpp
gfx/layers/wr/WebRenderColorLayer.cpp
gfx/layers/wr/WebRenderContainerLayer.cpp
gfx/layers/wr/WebRenderImageLayer.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderPaintedLayer.cpp
gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
gfx/layers/wr/WebRenderTextLayer.cpp
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
layout/forms/nsButtonFrameRenderer.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsCanvasFrame.cpp
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsTextFrame.cpp
layout/ipc/RenderFrameParent.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsCSSRenderingBorders.h
layout/painting/nsCSSRenderingGradients.cpp
layout/painting/nsCSSRenderingGradients.h
layout/painting/nsDisplayList.cpp
layout/painting/nsImageRenderer.cpp
layout/tables/nsTableFrame.cpp
layout/xul/nsImageBoxFrame.cpp
widget/cocoa/nsChildView.mm
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -625,21 +625,22 @@ void
 BufferTextureHost::PushExternalImage(wr::DisplayListBuilder& aBuilder,
                                      const wr::LayoutRect& aBounds,
                                      const wr::LayoutRect& aClip,
                                      wr::ImageRendering aFilter,
                                      Range<const wr::ImageKey>& aImageKeys)
 {
   if (GetFormat() != gfx::SurfaceFormat::YUV) {
     MOZ_ASSERT(aImageKeys.length() == 1);
-    aBuilder.PushImage(aBounds, aClip, aFilter, aImageKeys[0]);
+    aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
   } else {
     MOZ_ASSERT(aImageKeys.length() == 3);
     aBuilder.PushYCbCrPlanarImage(aBounds,
                                   aClip,
+                                  true,
                                   aImageKeys[0],
                                   aImageKeys[1],
                                   aImageKeys[2],
                                   wr::WrYuvColorSpace::Rec601,
                                   aFilter);
   }
 }
 
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -231,34 +231,36 @@ MacIOSurfaceTextureHostOGL::PushExternal
 {
   switch (GetFormat()) {
     case gfx::SurfaceFormat::R8G8B8X8:
     case gfx::SurfaceFormat::R8G8B8A8:
     case gfx::SurfaceFormat::B8G8R8A8:
     case gfx::SurfaceFormat::B8G8R8X8: {
       MOZ_ASSERT(aImageKeys.length() == 1);
       MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
-      aBuilder.PushImage(aBounds, aClip, aFilter, aImageKeys[0]);
+      aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
       break;
     }
     case gfx::SurfaceFormat::YUV422: {
       MOZ_ASSERT(aImageKeys.length() == 1);
       MOZ_ASSERT(mSurface->GetPlaneCount() == 0);
       aBuilder.PushYCbCrInterleavedImage(aBounds,
                                          aClip,
+                                         true,
                                          aImageKeys[0],
                                          wr::WrYuvColorSpace::Rec601,
                                          aFilter);
       break;
     }
     case gfx::SurfaceFormat::NV12: {
       MOZ_ASSERT(aImageKeys.length() == 2);
       MOZ_ASSERT(mSurface->GetPlaneCount() == 2);
       aBuilder.PushNV12Image(aBounds,
                              aClip,
+                             true,
                              aImageKeys[0],
                              aImageKeys[1],
                              wr::WrYuvColorSpace::Rec601,
                              aFilter);
       break;
     }
     default: {
       MOZ_ASSERT_UNREACHABLE("unexpected to be called");
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -286,17 +286,18 @@ AsyncImagePipelineManager::ApplyAsyncIma
       float opacity = 1.0f;
       builder.PushStackingContext(wr::ToLayoutRect(pipeline->mScBounds),
                                   0,
                                   &opacity,
                                   pipeline->mScTransform.IsIdentity() ? nullptr : &pipeline->mScTransform,
                                   wr::TransformStyle::Flat,
                                   nullptr,
                                   pipeline->mMixBlendMode,
-                                  nsTArray<wr::WrFilterOp>());
+                                  nsTArray<wr::WrFilterOp>(),
+                                  true);
 
       LayerRect rect(0, 0, pipeline->mCurrentTexture->GetSize().width, pipeline->mCurrentTexture->GetSize().height);
       if (pipeline->mScaleToSize.isSome()) {
         rect = LayerRect(0, 0, pipeline->mScaleToSize.value().width, pipeline->mScaleToSize.value().height);
       }
 
       if (useExternalImage) {
         MOZ_ASSERT(pipeline->mCurrentTexture->AsWebRenderTextureHost());
@@ -306,16 +307,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
                                                      wr::ToLayoutRect(rect),
                                                      pipeline->mFilter,
                                                      range_keys);
         HoldExternalImage(pipelineId, epoch, pipeline->mCurrentTexture->AsWebRenderTextureHost());
       } else {
         MOZ_ASSERT(keys.Length() == 1);
         builder.PushImage(wr::ToLayoutRect(rect),
                           wr::ToLayoutRect(rect),
+                          true,
                           pipeline->mFilter,
                           keys[0]);
       }
       builder.PopStackingContext();
     }
 
     wr::BuiltDisplayList dl;
     wr::LayoutSize builderContentSize;
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -30,17 +30,18 @@ StackingContextHelper::StackingContextHe
   mTransform = aTransform.valueOr(layer->GetTransform());
 
   float opacity = 1.0f;
   mBuilder->PushStackingContext(scBounds, 0, &opacity,
                                 mTransform.IsIdentity() ? nullptr : &mTransform,
                                 wr::TransformStyle::Flat,
                                 nullptr,
                                 wr::ToMixBlendMode(layer->GetMixBlendMode()),
-                                aFilters);
+                                aFilters,
+                                true);
   mOrigin = aLayer->Bounds().TopLeft();
 }
 
 StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
                                              wr::DisplayListBuilder& aBuilder,
                                              WebRenderLayer* aLayer,
                                              uint64_t aAnimationsId,
                                              float* aOpacityPtr,
@@ -55,47 +56,50 @@ StackingContextHelper::StackingContextHe
 
   mBuilder->PushStackingContext(scBounds,
                                 aAnimationsId,
                                 aOpacityPtr,
                                 aTransformPtr,
                                 wr::TransformStyle::Flat,
                                 nullptr,
                                 wr::ToMixBlendMode(aLayer->GetLayer()->GetMixBlendMode()),
-                                aFilters);
+                                aFilters,
+                                true);
   mOrigin = aLayer->Bounds().TopLeft();
 }
 
 StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
                                              wr::DisplayListBuilder& aBuilder,
                                              nsDisplayListBuilder* aDisplayListBuilder,
                                              nsDisplayItem* aItem,
                                              nsDisplayList* aDisplayList,
                                              gfx::Matrix4x4Typed<LayerPixel, LayerPixel>* aBoundTransform,
                                              uint64_t aAnimationsId,
                                              float* aOpacityPtr,
                                              gfx::Matrix4x4* aTransformPtr,
                                              gfx::Matrix4x4* aPerspectivePtr,
                                              const nsTArray<wr::WrFilterOp>& aFilters,
-                                             const gfx::CompositionOp& aMixBlendMode)
+                                             const gfx::CompositionOp& aMixBlendMode,
+                                             bool aBackfaceVisible)
   : mBuilder(&aBuilder)
 {
   bool is2d = !aTransformPtr || (aTransformPtr->Is2D() && !aPerspectivePtr);
   if (aTransformPtr) {
     mTransform = *aTransformPtr;
   }
 
   mBuilder->PushStackingContext(wr::LayoutRect(),
                                 aAnimationsId,
                                 aOpacityPtr,
                                 aTransformPtr,
                                 is2d ? wr::TransformStyle::Flat : wr::TransformStyle::Preserve3D,
                                 aPerspectivePtr,
                                 wr::ToMixBlendMode(aMixBlendMode),
-                                aFilters);
+                                aFilters,
+                                aBackfaceVisible);
 }
 
 StackingContextHelper::~StackingContextHelper()
 {
   if (mBuilder) {
     mBuilder->PopStackingContext();
   }
 }
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -53,17 +53,18 @@ public:
                         nsDisplayItem* aItem,
                         nsDisplayList* aDisplayList,
                         gfx::Matrix4x4Typed<LayerPixel, LayerPixel>* aBoundTransform,
                         uint64_t aAnimationsId,
                         float* aOpacityPtr,
                         gfx::Matrix4x4* aTransformPtr,
                         gfx::Matrix4x4* aPerspectivePtr = nullptr,
                         const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
-                        const gfx::CompositionOp& aMixBlendMode = gfx::CompositionOp::OP_OVER);
+                        const gfx::CompositionOp& aMixBlendMode = gfx::CompositionOp::OP_OVER,
+                        bool aBackfaceVisible = true);
   // This version of the constructor should only be used at the root level
   // of the tree, so that we have a StackingContextHelper to pass down into
   // the RenderLayer traversal, but don't actually want it to push a stacking
   // context on the display list builder.
   StackingContextHelper();
 
   // Pops the stacking context, if one was pushed during the constructor.
   ~StackingContextHelper();
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -236,17 +236,17 @@ WriteFontFileData(const uint8_t* aData, 
   memcpy(data->mFontBuffer.mData, aData, aLength);
 
   data->mFontIndex = aIndex;
 }
 
 void
 WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
                                  gfx::ScaledFont* aFont, const gfx::Color& aColor, const StackingContextHelper& aSc,
-                                 const LayerRect& aBounds, const LayerRect& aClip)
+                                 const LayerRect& aBounds, const LayerRect& aClip, bool aBackfaceVisible)
 {
   MOZ_ASSERT(aFont);
   MOZ_ASSERT(!aGlyphs.IsEmpty());
 
   wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
   MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
 
   nsTArray<wr::GlyphInstance> wr_glyph_instances;
@@ -255,16 +255,17 @@ WebRenderBridgeChild::PushGlyphs(wr::Dis
   for (size_t j = 0; j < aGlyphs.Length(); j++) {
     wr_glyph_instances[j].index = aGlyphs[j].mIndex;
     wr_glyph_instances[j].point = aSc.ToRelativeLayoutPoint(
             LayerPoint::FromUnknownPoint(aGlyphs[j].mPosition));
   }
 
   aBuilder.PushText(aSc.ToRelativeLayoutRect(aBounds),
                     aSc.ToRelativeLayoutRect(aClip),
+                    aBackfaceVisible,
                     aColor,
                     key,
                     Range<const wr::GlyphInstance>(wr_glyph_instances.Elements(), wr_glyph_instances.Length()));
 }
 
 wr::FontInstanceKey
 WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont)
 {
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -111,17 +111,18 @@ public:
   wr::WrImageKey GetNextImageKey()
   {
     return wr::WrImageKey{ GetNamespace(), GetNextResourceId() };
   }
 
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray<gfx::Glyph>& aGlyphs,
                   gfx::ScaledFont* aFont, const gfx::Color& aColor,
                   const StackingContextHelper& aSc,
-                  const LayerRect& aBounds, const LayerRect& aClip);
+                  const LayerRect& aBounds, const LayerRect& aClip,
+                  bool aBackfaceVisible);
 
   wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont);
 
   void RemoveExpiredFontKeys();
   void ClearReadLocks();
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -64,17 +64,17 @@ WebRenderCanvasLayer::RenderLayer(wr::Di
                   Stringify(filter).c_str());
   }
 
   wr::WrImageKey key = GenerateImageKey();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(canvasRenderer->GetExternalImageId().value(), key));
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
-  aBuilder.PushImage(r, r, filter, key);
+  aBuilder.PushImage(r, r, true, filter, key);
 }
 
 void
 WebRenderCanvasLayer::ClearCachedResources()
 {
   mCanvasRenderer->ClearCachedResources();
 }
 
--- a/gfx/layers/wr/WebRenderColorLayer.cpp
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -25,13 +25,13 @@ WebRenderColorLayer::RenderLayer(wr::Dis
 {
   ScrollingLayersHelper scroller(this, aBuilder, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect = Bounds();
   DumpLayerInfo("ColorLayer", rect);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
-  aBuilder.PushRect(r, r, wr::ToColorF(mColor));
+  aBuilder.PushRect(r, r, true, wr::ToColorF(mColor));
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -135,13 +135,13 @@ WebRenderRefLayer::RenderLayer(wr::Displ
   // The conversion from ParentLayerPixel to LayerPixel below is a result of
   // changing the reference layer from "this layer" to the "the layer that
   // created aSc".
   LayerRect rect = ViewAs<LayerPixel>(bounds,
       PixelCastJustification::MovingDownToChildren);
   DumpLayerInfo("RefLayer", rect);
 
   wr::LayoutRect r = aSc.ToRelativeLayoutRect(rect);
-  aBuilder.PushIFrame(r, wr::AsPipelineId(mId));
+  aBuilder.PushIFrame(r, true, wr::AsPipelineId(mId));
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -164,17 +164,17 @@ WebRenderImageLayer::RenderLayer(wr::Dis
     // where it will be done when we build the display list for the iframe.
     // That happens in AsyncImagePipelineManager.
 
     LayerRect rect = ViewAs<LayerPixel>(bounds,
         PixelCastJustification::MovingDownToChildren);
     DumpLayerInfo("Image Layer async", rect);
 
     wr::LayoutRect r = aSc.ToRelativeLayoutRect(rect);
-    aBuilder.PushIFrame(r, mPipelineId.ref());
+    aBuilder.PushIFrame(r, true, mPipelineId.ref());
 
     gfx::Matrix4x4 scTransform = GetTransform();
     // Translate is applied as part of PushIFrame()
     scTransform.PostTranslate(-rect.x, -rect.y, 0);
     // Adjust transform as to apply origin
     LayerPoint scOrigin = Bounds().TopLeft();
     scTransform.PreTranslate(-scOrigin.x, -scOrigin.y, 0);
 
@@ -228,17 +228,17 @@ WebRenderImageLayer::RenderLayer(wr::Dis
 
   DumpLayerInfo("Image Layer", rect);
   if (gfxPrefs::LayersDump()) {
     printf_stderr("ImageLayer %p texture-filter=%s \n",
                   GetLayer(),
                   Stringify(filter).c_str());
   }
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
-  aBuilder.PushImage(r, r, filter, mKey.value());
+  aBuilder.PushImage(r, r, true, filter, mKey.value());
 }
 
 Maybe<wr::WrImageMask>
 WebRenderImageLayer::RenderMaskLayer(const StackingContextHelper& aSc,
                                      const gfx::Matrix4x4& aTransform)
 {
   if (!mContainer) {
      return Nothing();
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -251,21 +251,16 @@ WebRenderLayerManager::CreateWebRenderCo
     nsDisplayList* itemSameCoordinateSystemChildren
       = item->GetSameCoordinateSystemChildren();
     if (item->ShouldFlattenAway(aDisplayListBuilder)) {
       aDisplayList->AppendToBottom(itemSameCoordinateSystemChildren);
       item->Destroy(aDisplayListBuilder);
       continue;
     }
 
-    if (item->BackfaceIsHidden() && aSc.IsBackfaceVisible()) {
-      item->Destroy(aDisplayListBuilder);
-      continue;
-    }
-
     savedItems.AppendToTop(item);
 
     bool forceNewLayerData = false;
     size_t layerCountBeforeRecursing = mLayerScrollData.size();
     if (apzEnabled) {
       // For some types of display items we want to force a new
       // WebRenderLayerScrollData object, to ensure we preserve the APZ-relevant
       // data that is in the display item.
@@ -411,17 +406,18 @@ WebRenderLayerManager::CreateImageKey(ns
     imageData->CreateAsyncImageWebRenderCommands(aBuilder,
                                                  aContainer,
                                                  aSc,
                                                  rect,
                                                  scBounds,
                                                  gfx::Matrix4x4(),
                                                  scaleToSize,
                                                  wr::ImageRendering::Auto,
-                                                 wr::MixBlendMode::Normal);
+                                                 wr::MixBlendMode::Normal,
+                                                 !aItem->BackfaceIsHidden());
     return Nothing();
   }
 
   AutoLockImage autoLock(aContainer);
   if (!autoLock.HasImage()) {
     return Nothing();
   }
   mozilla::layers::Image* image = autoLock.GetImage();
@@ -445,17 +441,17 @@ WebRenderLayerManager::PushImage(nsDispl
     return true;
   }
   if (!key) {
     return false;
   }
 
   auto r = aSc.ToRelativeLayoutRect(aRect);
   SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
-  aBuilder.PushImage(r, r, wr::ToImageRendering(sampleFilter), key.value());
+  aBuilder.PushImage(r, r, !aItem->BackfaceIsHidden(), wr::ToImageRendering(sampleFilter), key.value());
 
   return true;
 }
 
 static void
 PaintItemByDrawTarget(nsDisplayItem* aItem,
                       DrawTarget* aDT,
                       const LayerRect& aImageRect,
@@ -671,16 +667,17 @@ WebRenderLayerManager::PushItemAsImage(n
   if (!fallbackData) {
     return false;
   }
 
   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(imageRect + offset);
   SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
   aBuilder.PushImage(dest,
                      dest,
+                     !aItem->BackfaceIsHidden(),
                      wr::ToImageRendering(sampleFilter),
                      fallbackData->GetKey().value());
   return true;
 }
 
 void
 WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
                                       void* aCallbackData,
--- a/gfx/layers/wr/WebRenderPaintedLayer.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayer.cpp
@@ -99,17 +99,17 @@ WebRenderPaintedLayer::CreateWebRenderDi
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
 
   wr::WrImageKey key = GenerateImageKey();
   WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
-  aBuilder.PushImage(r, r, wr::ImageRendering::Auto, key);
+  aBuilder.PushImage(r, r, true, wr::ImageRendering::Auto, key);
 }
 
 void
 WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                    wr::IpcResourceUpdateQueue& aResources,
                                    const StackingContextHelper& aSc)
 {
   if (!SetupExternalImages()) {
--- a/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
@@ -88,13 +88,14 @@ WebRenderPaintedLayerBlob::RenderLayer(w
 
   ScrollingLayersHelper scroller(this, aBuilder, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
 
   aBuilder.PushImage(sc.ToRelativeLayoutRect(LayerRect(mImageBounds)),
                      sc.ToRelativeLayoutRect(rect),
+                     true,
                      wr::ImageRendering::Auto, mImageKey.value());
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -38,14 +38,14 @@ WebRenderTextLayer::RenderLayer(wr::Disp
         // we apply it here. The glyphs that we push to WR should already be
         // taking the transform into account.
         GetTransform().TransformBounds(IntRectToRect(mBounds))
     );
     DumpLayerInfo("TextLayer", rect);
 
     for (GlyphArray& glyphs : mGlyphs) {
         WrBridge()->PushGlyphs(aBuilder, glyphs.glyphs(), mFont,
-                               glyphs.color().value(), aSc, rect, rect);
+                               glyphs.color().value(), aSc, rect, rect, true);
     }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -114,17 +114,18 @@ void
 WebRenderImageData::CreateAsyncImageWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                       ImageContainer* aContainer,
                                                       const StackingContextHelper& aSc,
                                                       const LayerRect& aBounds,
                                                       const LayerRect& aSCBounds,
                                                       const gfx::Matrix4x4& aSCTransform,
                                                       const gfx::MaybeIntSize& aScaleToSize,
                                                       const wr::ImageRendering& aFilter,
-                                                      const wr::MixBlendMode& aMixBlendMode)
+                                                      const wr::MixBlendMode& aMixBlendMode,
+                                                      bool aIsBackfaceVisible)
 {
   MOZ_ASSERT(aContainer->IsAsync());
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
                                                   aContainer->GetAsyncContainerHandle());
   }
@@ -135,17 +136,17 @@ WebRenderImageData::CreateAsyncImageWebR
   //
   // We don't push a stacking context for this async image pipeline here.
   // Instead, we do it inside the iframe that hosts the image. As a result,
   // a bunch of the calculations normally done as part of that stacking
   // context need to be done manually and pushed over to the parent side,
   // where it will be done when we build the display list for the iframe.
   // That happens in AsyncImagePipelineManager.
   wr::LayoutRect r = aSc.ToRelativeLayoutRect(aBounds);
-  aBuilder.PushIFrame(r, mPipelineId.ref());
+  aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref());
 
   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
                                                                    aSCBounds,
                                                                    aSCTransform,
                                                                    aScaleToSize,
                                                                    aFilter,
                                                                    aMixBlendMode));
 }
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -72,17 +72,18 @@ public:
   void CreateAsyncImageWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                          ImageContainer* aContainer,
                                          const StackingContextHelper& aSc,
                                          const LayerRect& aBounds,
                                          const LayerRect& aSCBounds,
                                          const gfx::Matrix4x4& aSCTransform,
                                          const gfx::MaybeIntSize& aScaleToSize,
                                          const wr::ImageRendering& aFilter,
-                                         const wr::MixBlendMode& aMixBlendMode);
+                                         const wr::MixBlendMode& aMixBlendMode,
+                                         bool aIsBackfaceVisible);
 
   void CreateImageClientIfNeeded();
 
 protected:
   void CreateExternalImageIfNeeded();
 
   wr::MaybeExternalImageId mExternalImageId;
   Maybe<wr::ImageKey> mKey;
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -248,16 +248,17 @@ nsDisplayButtonBoxShadowOuter::CreateWeb
     mozilla::gfx::Point shadowOffset;
     shadowOffset.x = (shadow->mXOffset / appUnitsPerDevPixel);
     shadowOffset.y = (shadow->mYOffset / appUnitsPerDevPixel);
 
     float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
 
     aBuilder.PushBoxShadow(deviceBoxRect,
                            deviceClipRect,
+                           !BackfaceIsHidden(),
                            deviceBoxRect,
                            wr::ToLayoutVector2D(shadowOffset),
                            wr::ToColorF(shadowColor),
                            blurRadius,
                            spreadRadius,
                            borderRadius,
                            wr::BoxShadowClipMode::Outset);
   }
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -485,16 +485,17 @@ BulletRenderer::CreateWebRenderCommandsF
   }
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(destRect);
 
   aBuilder.PushImage(dest,
                      dest,
+                     !aItem->BackfaceIsHidden(),
                      wr::ImageRendering::Auto,
                      key.value());
 }
 
 void
 BulletRenderer::CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
                                                wr::DisplayListBuilder& aBuilder,
                                                wr::IpcResourceUpdateQueue& aResources,
@@ -527,17 +528,17 @@ BulletRenderer::CreateWebRenderCommandsF
   LayerRect destRect = ViewAs<LayerPixel>(
       LayoutDeviceRect::FromAppUnits(
           aItem->GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel),
       PixelCastJustification::WebRenderHasUnitResolution);
 
   for (layers::GlyphArray& glyphs : mGlyphs) {
     aManager->WrBridge()->PushGlyphs(aBuilder, glyphs.glyphs(), mFont,
                                      glyphs.color().value(),
-                                     aSc, destRect, destRect);
+                                     aSc, destRect, destRect, !aItem->BackfaceIsHidden());
   }
 }
 
 class nsDisplayBullet final : public nsDisplayItem {
 public:
   nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame)
   {
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -326,16 +326,17 @@ nsDisplayCanvasBackgroundColor::CreateWe
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
   LayoutDeviceRect rect = LayoutDeviceRect::FromAppUnits(
           bgClipRect, appUnitsPerDevPixel);
 
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(rect);
   aBuilder.PushRect(transformedRect,
                     transformedRect,
+                    !BackfaceIsHidden(),
                     wr::ToColorF(ToDeviceColor(mColor)));
   return true;
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
 {
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -171,17 +171,17 @@ public:
         // We don't push a stacking context for this async image pipeline here.
         // Instead, we do it inside the iframe that hosts the image. As a result,
         // a bunch of the calculations normally done as part of that stacking
         // context need to be done manually and pushed over to the parent side,
         // where it will be done when we build the display list for the iframe.
         // That happens in WebRenderCompositableHolder.
 
         wr::LayoutRect r = aSc.ToRelativeLayoutRect(bounds);
-        aBuilder.PushIFrame(r, data->GetPipelineId().ref());
+        aBuilder.PushIFrame(r, !BackfaceIsHidden(), data->GetPipelineId().ref());
 
         gfx::Matrix4x4 scTransform;
         if (data->NeedsYFlip()) {
           scTransform = scTransform.PreTranslate(0, data->GetSize().height, 0).PreScale(1, -1, 1);
         }
 
         gfxRect destGFXRect = mFrame->PresContext()->AppUnitsToGfxUnits(dest);
         scTransform.PreScale(destGFXRect.Width() / canvasSizeInPx.width,
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5199,53 +5199,55 @@ nsDisplayText::CreateWebRenderCommands(m
     layoutClipRect = LayoutDeviceRect::FromAppUnits(
                 GetClip().GetClipRect(), appUnitsPerDevPixel);
   }
 
   LayerRect boundsRect = LayerRect::FromUnknownRect(layoutBoundsRect.ToUnknownRect());
   LayerRect clipRect = LayerRect::FromUnknownRect(layoutClipRect.ToUnknownRect());
   wr::LayoutRect wrClipRect = aSc.ToRelativeLayoutRect(clipRect); // wr::ToLayoutRect(clipRect);
   wr::LayoutRect wrBoundsRect = aSc.ToRelativeLayoutRect(boundsRect); //wr::ToLayoutRect(boundsRect);
+  bool backfaceVisible = !BackfaceIsHidden();
 
   // Drawing order: selections, shadows,
   //                underline, overline, [grouped in one array]
   //                text, emphasisText,  [grouped in one array]
   //                lineThrough
 
   for (auto& part : mTextDrawer->GetParts()) {
     if (part.selection) {
       auto selection = part.selection.value();
-      aBuilder.PushRect(selection.rect, wrClipRect, selection.color);
+      aBuilder.PushRect(selection.rect, wrClipRect, backfaceVisible, selection.color);
     }
   }
 
   for (auto& part : mTextDrawer->GetParts()) {
     // WR takes the shadows in CSS-order (reverse of rendering order),
     // because the drawing of a shadow actually occurs when it's popped.
     for (const wr::TextShadow& shadow : part.shadows) {
-      aBuilder.PushTextShadow(wrBoundsRect, wrClipRect, shadow);
+      aBuilder.PushTextShadow(wrBoundsRect, wrClipRect, backfaceVisible, shadow);
     }
 
     for (const wr::Line& decoration : part.beforeDecorations) {
-      aBuilder.PushLine(wrClipRect, decoration);
+      aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
     }
 
     for (const mozilla::layout::TextRunFragment& text : part.text) {
       // mOpacity is set after we do our analysis, so we need to apply it here.
       // mOpacity is only non-trivial when we have "pure" text, so we don't
       // ever need to apply it to shadows or decorations.
       auto color = text.color;
       color.a *= mOpacity;
 
       aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
-                                       color, aSc, boundsRect, clipRect);
+                                       color, aSc, boundsRect, clipRect,
+                                       backfaceVisible);
     }
 
     for (const wr::Line& decoration : part.afterDecorations) {
-      aBuilder.PushLine(wrClipRect, decoration);
+      aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
     }
 
     for (size_t i = 0; i < part.shadows.Length(); ++i) {
       aBuilder.PopTextShadow();
     }
   }
 
   return true;
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -393,16 +393,17 @@ nsDisplayRemote::CreateWebRenderCommands
 
   mOffset = mozilla::layout::GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
 
   mozilla::LayoutDeviceRect visible = mozilla::LayoutDeviceRect::FromAppUnits(
       GetVisibleRect(), mFrame->PresContext()->AppUnitsPerDevPixel());
   visible += mOffset;
 
   aBuilder.PushIFrame(aSc.ToRelativeLayoutRect(visible),
+      !BackfaceIsHidden(),
       mozilla::wr::AsPipelineId(GetRemoteLayersId()));
 
   return true;
 }
 
 bool
 nsDisplayRemote::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
                                   mozilla::layers::WebRenderLayerScrollData* aLayerData)
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -783,17 +783,18 @@ ConstructBorderRenderer(nsPresContext* a
                              aDrawTarget,
                              dirtyRect,
                              joinedBorderAreaPx,
                              borderStyles,
                              borderWidths,
                              bgRadii,
                              borderColors,
                              compositeColors,
-                             bgColor);
+                             bgColor,
+                             !aForFrame->BackfaceIsHidden());
 }
 
 
 DrawResult
 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
                                            gfxContext& aRenderingContext,
                                            nsIFrame* aForFrame,
                                            const nsRect& aDirtyRect,
@@ -1062,17 +1063,18 @@ nsCSSRendering::CreateBorderRendererForO
                          dt,
                          dirtyRect,
                          oRect,
                          outlineStyles,
                          outlineWidths,
                          outlineRadii,
                          outlineColors,
                          nullptr,
-                         bgColor);
+                         bgColor,
+                         !aForFrame->BackfaceIsHidden());
 
   return Some(br);
 }
 
 void
 nsCSSRendering::PaintOutline(nsPresContext* aPresContext,
                              gfxContext& aRenderingContext,
                              nsIFrame* aForFrame,
@@ -1124,27 +1126,31 @@ nsCSSRendering::PaintFocus(nsPresContext
   nscolor focusColors[4] = { aColor, aColor, aColor, aColor };
 
   // Because this renders a dotted border, the background color
   // should not be used.  Therefore, we provide a value that will
   // be blatantly wrong if it ever does get used.  (If this becomes
   // something that CSS can style, this function will then have access
   // to a style context and can use the same logic that PaintBorder
   // and PaintOutline do.)
+  //
+  // WebRender layers-free mode don't use PaintFocus function. Just assign
+  // the backface-visibility to true for this case.
   nsCSSBorderRenderer br(aPresContext,
                          nullptr,
                          aDrawTarget,
                          focusRect,
                          focusRect,
                          focusStyles,
                          focusWidths,
                          focusRadii,
                          focusColors,
                          nullptr,
-                         NS_RGB(255, 0, 0));
+                         NS_RGB(255, 0, 0),
+                         true);
   br.DrawBorders();
 
   PrintAsStringNewline();
 }
 
 // Thebes Border Rendering Code End
 //----------------------------------------------------------------------
 
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -174,24 +174,26 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
                                          DrawTarget* aDrawTarget,
                                          const Rect& aDirtyRect,
                                          Rect& aOuterRect,
                                          const uint8_t* aBorderStyles,
                                          const Float* aBorderWidths,
                                          RectCornerRadii& aBorderRadii,
                                          const nscolor* aBorderColors,
                                          nsBorderColors* const* aCompositeColors,
-                                         nscolor aBackgroundColor)
+                                         nscolor aBackgroundColor,
+                                         bool aBackfaceIsVisible)
   : mPresContext(aPresContext),
     mDocument(aDocument),
     mDrawTarget(aDrawTarget),
     mDirtyRect(aDirtyRect),
     mOuterRect(aOuterRect),
     mBorderRadii(aBorderRadii),
-    mBackgroundColor(aBackgroundColor)
+    mBackgroundColor(aBackgroundColor),
+    mBackfaceIsVisible(aBackfaceIsVisible)
 {
   PodCopy(mBorderStyles, aBorderStyles, 4);
   PodCopy(mBorderWidths, aBorderWidths, 4);
   PodCopy(mBorderColors, aBorderColors, 4);
   if (aCompositeColors) {
     PodCopy(mCompositeColors, aCompositeColors, 4);
   } else {
     static nsBorderColors * const noColors[4] = { nullptr };
@@ -3623,16 +3625,17 @@ nsCSSBorderRenderer::CreateWebRenderComm
 
   wr::BorderRadius borderRadius = wr::ToBorderRadius(LayerSize(mBorderRadii[0].width, mBorderRadii[0].height),
                                                      LayerSize(mBorderRadii[1].width, mBorderRadii[1].height),
                                                      LayerSize(mBorderRadii[3].width, mBorderRadii[3].height),
                                                      LayerSize(mBorderRadii[2].width, mBorderRadii[2].height));
   Range<const wr::BorderSide> wrsides(side, 4);
   aBuilder.PushBorder(transformedRect,
                       transformedRect,
+                      mBackfaceIsVisible,
                       wr::ToBorderWidths(mBorderWidths[0], mBorderWidths[1], mBorderWidths[2], mBorderWidths[3]),
                       wrsides,
                       borderRadius);
 }
 
 /* static */Maybe<nsCSSBorderImageRenderer>
 nsCSSBorderImageRenderer::CreateBorderImageRenderer(nsPresContext* aPresContext,
                                                     nsIFrame* aForFrame,
--- a/layout/painting/nsCSSRenderingBorders.h
+++ b/layout/painting/nsCSSRenderingBorders.h
@@ -96,17 +96,18 @@ public:
                       DrawTarget* aDrawTarget,
                       const Rect& aDirtyRect,
                       Rect& aOuterRect,
                       const uint8_t* aBorderStyles,
                       const Float* aBorderWidths,
                       RectCornerRadii& aBorderRadii,
                       const nscolor* aBorderColors,
                       nsBorderColors* const* aCompositeColors,
-                      nscolor aBackgroundColor);
+                      nscolor aBackgroundColor,
+                      bool aBackfaceIsVisible);
 
   // draw the entire border
   void DrawBorders();
 
   bool CanCreateWebRenderCommands();
   void CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                mozilla::wr::IpcResourceUpdateQueue& aResources,
                                const mozilla::layers::StackingContextHelper& aSc);
@@ -157,16 +158,17 @@ private:
   nscolor mBackgroundColor;
 
   // calculated values
   bool mAllBordersSameStyle;
   bool mAllBordersSameWidth;
   bool mOneUnitBorder;
   bool mNoBorderRadius;
   bool mAvoidStroke;
+  bool mBackfaceIsVisible;
 
   // For all the sides in the bitmask, would they be rendered
   // in an identical color and style?
   bool AreBorderSideFinalStylesSame(uint8_t aSides);
 
   // For the given style, is the given corner a solid color?
   bool IsSolidCornerStyle(uint8_t aStyle, mozilla::Corner aCorner);
 
--- a/layout/painting/nsCSSRenderingGradients.cpp
+++ b/layout/painting/nsCSSRenderingGradients.cpp
@@ -1026,16 +1026,17 @@ nsCSSGradientRenderer::BuildWebRenderPar
 void
 nsCSSGradientRenderer::BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
                                                   const layers::StackingContextHelper& aSc,
                                                   layers::WebRenderDisplayItemLayer* aLayer,
                                                   const nsRect& aDest,
                                                   const nsRect& aFillArea,
                                                   const nsSize& aRepeatSize,
                                                   const CSSIntRect& aSrc,
+                                                  bool aIsBackfaceVisible,
                                                   float aOpacity)
 {
   if (aDest.IsEmpty() || aFillArea.IsEmpty()) {
     return;
   }
 
   wr::ExtendMode extendMode;
   nsTArray<wr::GradientStop> stops;
@@ -1080,29 +1081,31 @@ nsCSSGradientRenderer::BuildWebRenderDis
 
   if (mGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
     lineEnd.x = (lineEnd.x - srcTransform.x) * srcTransform.width;
     lineEnd.y = (lineEnd.y - srcTransform.y) * srcTransform.height;
 
     aBuilder.PushLinearGradient(
       wrGradientBounds,
       wrClipBounds,
+      aIsBackfaceVisible,
       mozilla::wr::ToLayoutPoint(lineStart),
       mozilla::wr::ToLayoutPoint(lineEnd),
       stops,
       extendMode,
       mozilla::wr::ToLayoutSize(layerFirstTileSize),
       mozilla::wr::ToLayoutSize(tileSpacing));
   } else {
     gradientRadius.width *= srcTransform.width;
     gradientRadius.height *= srcTransform.height;
 
     aBuilder.PushRadialGradient(
       wrGradientBounds,
       wrClipBounds,
+      aIsBackfaceVisible,
       mozilla::wr::ToLayoutPoint(lineStart),
       mozilla::wr::ToLayoutSize(gradientRadius),
       stops,
       extendMode,
       mozilla::wr::ToLayoutSize(layerFirstTileSize),
       mozilla::wr::ToLayoutSize(tileSpacing));
   }
 }
--- a/layout/painting/nsCSSRenderingGradients.h
+++ b/layout/painting/nsCSSRenderingGradients.h
@@ -82,16 +82,17 @@ public:
    */
   void BuildWebRenderDisplayItems(wr::DisplayListBuilder& aBuilder,
                                   const layers::StackingContextHelper& aSc,
                                   layers::WebRenderDisplayItemLayer* aLayer,
                                   const nsRect& aDest,
                                   const nsRect& aFill,
                                   const nsSize& aRepeatSize,
                                   const mozilla::CSSIntRect& aSrc,
+                                  bool aIsBackfaceVisible,
                                   float aOpacity = 1.0);
 
 private:
   nsCSSGradientRenderer() {}
 
   nsPresContext* mPresContext;
   nsStyleGradient* mGradient;
   nsTArray<ColorStop> mStops;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2942,16 +2942,17 @@ nsDisplaySolidColor::CreateWebRenderComm
   }
 
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
 
   aBuilder.PushRect(transformedRect,
                     transformedRect,
+                    !BackfaceIsHidden(),
                     wr::ToColorF(ToDeviceColor(mColor)));
 
   return true;
 }
 
 nsRect
 nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
                                      bool* aSnap) const
@@ -2994,16 +2995,17 @@ nsDisplaySolidColorRegion::CreateWebRend
 {
   for (auto iter = mRegion.RectIter(); !iter.Done(); iter.Next()) {
     nsRect rect = iter.Get();
     LayoutDeviceRect layerRects = LayoutDeviceRect::FromAppUnits(
       rect, mFrame->PresContext()->AppUnitsPerDevPixel());
     wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(layerRects);
     aBuilder.PushRect(transformedRect,
                       transformedRect,
+                      !BackfaceIsHidden(),
                       wr::ToColorF(ToDeviceColor(mColor)));
   }
 
   return true;
 }
 
 static void
 RegisterThemeGeometry(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
@@ -4265,16 +4267,17 @@ nsDisplayBackgroundColor::CreateWebRende
   }
 
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
         mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel());
   wr::LayoutRect transformedRect = aSc.ToRelativeLayoutRect(bounds);
 
   aBuilder.PushRect(transformedRect,
                     transformedRect,
+                    !BackfaceIsHidden(),
                     wr::ToColorF(ToDeviceColor(mColor)));
 
   return true;
 }
 
 void
 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
                                 gfxContext* aCtx)
@@ -4786,21 +4789,23 @@ nsDisplayCaret::CreateWebRenderCommands(
     hookRect + ToReferenceFrame(), appUnitsPerDevPixel);
 
   wr::LayoutRect caret = aSc.ToRelativeLayoutRect(devCaretRect);
   wr::LayoutRect hook = aSc.ToRelativeLayoutRect(devHookRect);
 
   // Note, WR will pixel snap anything that is layout aligned.
   aBuilder.PushRect(caret,
                     caret,
+                    !BackfaceIsHidden(),
                     wr::ToColorF(color));
 
   if (!devHookRect.IsEmpty()) {
     aBuilder.PushRect(hook,
                       hook,
+                      !BackfaceIsHidden(),
                       wr::ToColorF(color));
   }
   return true;
 }
 
 LayerState
 nsDisplayCaret::GetLayerState(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
@@ -5090,16 +5095,17 @@ nsDisplayBorder::CreateBorderImageWebRen
       gfx::IntSize size;
       Maybe<wr::ImageKey> key = aManager->CreateImageKey(this, container, aBuilder, aSc, size);
       if (key.isNothing()) {
         return;
       }
 
       aBuilder.PushBorderImage(dest,
                                clip,
+                               !BackfaceIsHidden(),
                                wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
                                key.value(),
                                wr::ToNinePatchDescriptor(
                                  (float)(mBorderImageRenderer->mImageSize.width) / appUnitsPerDevPixel,
                                  (float)(mBorderImageRenderer->mImageSize.height) / appUnitsPerDevPixel,
                                  wr::ToSideOffsets2D_u32(slice[0], slice[1], slice[2], slice[3])),
                                wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2], outset[3]),
                                wr::ToRepeatMode(mBorderImageRenderer->mRepeatModeHorizontal),
@@ -5123,25 +5129,27 @@ nsDisplayBorder::CreateBorderImageWebRen
       if (gradientData->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
         LayerPoint startPoint = LayerPoint(dest.origin.x, dest.origin.y);
         startPoint = startPoint + ViewAs<LayerPixel>(lineStart, PixelCastJustification::WebRenderHasUnitResolution);
         LayerPoint endPoint = LayerPoint(dest.origin.x, dest.origin.y);
         endPoint = endPoint + ViewAs<LayerPixel>(lineEnd, PixelCastJustification::WebRenderHasUnitResolution);
 
         aBuilder.PushBorderGradient(dest,
                                     clip,
+                                    !BackfaceIsHidden(),
                                     wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
                                     wr::ToLayoutPoint(startPoint),
                                     wr::ToLayoutPoint(endPoint),
                                     stops,
                                     extendMode,
                                     wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2], outset[3]));
       } else {
         aBuilder.PushBorderRadialGradient(dest,
                                           clip,
+                                          !BackfaceIsHidden(),
                                           wr::ToBorderWidths(widths[0], widths[1], widths[2], widths[3]),
                                           wr::ToLayoutPoint(lineStart),
                                           wr::ToLayoutSize(gradientRadius),
                                           stops,
                                           extendMode,
                                           wr::ToSideOffsets2D_f32(outset[0], outset[1], outset[2], outset[3]));
       }
       break;
@@ -5455,16 +5463,17 @@ nsDisplayBoxShadowOuter::CreateWebRender
 
       // TODO: support non-uniform border radius.
       float borderRadius = hasBorderRadius ? borderRadii.TopLeft().width
                                            : 0.0;
       float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
 
       aBuilder.PushBoxShadow(deviceBoxRect,
                              deviceClipRect,
+                             !BackfaceIsHidden(),
                              deviceBoxRect,
                              wr::ToLayoutVector2D(shadowOffset),
                              wr::ToColorF(shadowColor),
                              blurRadius,
                              spreadRadius,
                              borderRadius,
                              wr::BoxShadowClipMode::Outset);
     }
@@ -5627,16 +5636,17 @@ nsDisplayBoxShadowInner::CreateInsetBoxS
       float blurRadius = float(shadowItem->mRadius) / float(appUnitsPerDevPixel);
       // TODO: WR doesn't support non-uniform border radii
       float borderRadius = innerRadii.TopLeft().width;
       // NOTE: Any spread radius > 0 will render nothing. WR Bug.
       float spreadRadius = float(shadowItem->mSpread) / float(appUnitsPerDevPixel);
 
       aBuilder.PushBoxShadow(wr::ToLayoutRect(deviceBoxRect),
                              deviceClipRect,
+                             !aFrame->BackfaceIsHidden(),
                              wr::ToLayoutRect(deviceBoxRect),
                              wr::ToLayoutVector2D(shadowOffset),
                              wr::ToColorF(shadowColor),
                              blurRadius,
                              spreadRadius,
                              borderRadius,
                              wr::BoxShadowClipMode::Inset
                              );
@@ -7948,17 +7958,19 @@ nsDisplayTransform::CreateWebRenderComma
                            aDisplayListBuilder,
                            this,
                            mStoredList.GetChildren(),
                            &boundTransform,
                            animationsId,
                            nullptr,
                            transformForSC,
                            nullptr,
-                           filters);
+                           filters,
+                           gfx::CompositionOp::OP_OVER,
+                           !BackfaceIsHidden());
 
   return mStoredList.CreateWebRenderCommands(aBuilder, aResources, sc, aParentCommands,
                                              aManager, aDisplayListBuilder);
 }
 
 bool
 nsDisplayTransform::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
                                      mozilla::layers::WebRenderLayerScrollData* aLayerData)
@@ -8554,17 +8566,19 @@ nsDisplayPerspective::CreateWebRenderCom
                            aDisplayListBuilder,
                            this,
                            mList.GetChildren(),
                            nullptr,
                            0,
                            nullptr,
                            &transformForSC,
                            &perspectiveMatrix,
-                           filters);
+                           filters,
+                           gfx::CompositionOp::OP_OVER,
+                           !BackfaceIsHidden());
 
   return mList.CreateWebRenderCommands(aBuilder, aResources, sc, aParentCommands,
                                        aManager, aDisplayListBuilder);
 }
 
 int32_t
 nsDisplayPerspective::ZIndex() const
 {
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -610,17 +610,18 @@ nsImageRenderer::BuildWebRenderDisplayIt
   }
 
   switch (mType) {
     case eStyleImageType_Gradient:
     {
       nsCSSGradientRenderer renderer =
         nsCSSGradientRenderer::Create(aPresContext, mGradientData, mSize);
 
-      renderer.BuildWebRenderDisplayItems(aBuilder, aSc, aLayer, aDest, aFill, aRepeatSize, aSrc, aOpacity);
+      renderer.BuildWebRenderDisplayItems(aBuilder, aSc, aLayer, aDest, aFill,
+                                          aRepeatSize, aSrc, !aItem->BackfaceIsHidden(), aOpacity);
       break;
     }
     case eStyleImageType_Image:
     {
       // XXX(aosmond): We will support downscale-on-decode in bug 1368776. Until
       // then, don't pass FLAG_HIGH_QUALITY_SCALING.
       uint32_t containerFlags = imgIContainer::FLAG_NONE;
       if (mFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
@@ -654,17 +655,17 @@ nsImageRenderer::BuildWebRenderDisplayIt
       wr::LayoutRect fill = aSc.ToRelativeLayoutRect(fillRect);
       wr::LayoutRect clip = aSc.ToRelativeLayoutRect(
           LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel));
 
       LayoutDeviceSize gapSize = LayoutDeviceSize::FromAppUnits(
           aRepeatSize - aDest.Size(), appUnitsPerDevPixel);
 
       SamplingFilter samplingFilter = nsLayoutUtils::GetSamplingFilterForFrame(mForFrame);
-      aBuilder.PushImage(fill, clip,
+      aBuilder.PushImage(fill, clip, !aItem->BackfaceIsHidden(),
                          wr::ToLayoutSize(destRect.Size()), wr::ToLayoutSize(gapSize),
                          wr::ToImageRendering(samplingFilter), key.value());
       break;
     }
     default:
       break;
   }
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -6497,16 +6497,17 @@ struct BCBorderParameters
   nscolor mBorderColor;
   nscolor mBGColor;
   nsRect mBorderRect;
   int32_t mAppUnitsPerDevPixel;
   mozilla::Side mStartBevelSide;
   nscoord mStartBevelOffset;
   mozilla::Side mEndBevelSide;
   nscoord mEndBevelOffset;
+  bool mBackfaceIsVisible;
 };
 
 struct BCBlockDirSeg
 {
   BCBlockDirSeg();
 
   void Start(BCPaintBorderIterator& aIter,
              BCBorderOwner          aBorderOwner,
@@ -7330,16 +7331,17 @@ BCBlockDirSeg::BuildBorderParameters(BCP
     aIter.IsDamageAreaIEndMost() ? eLogicalSideIEnd : eLogicalSideIStart;
   int32_t relColIndex = aIter.GetRelativeColIndex();
   nsTableColFrame* col           = mCol; if (!col) ABORT1(Nothing());
   nsTableCellFrame* cell         = mFirstCell; // ???
   nsIFrame* owner = nullptr;
   result.mBorderStyle = NS_STYLE_BORDER_STYLE_SOLID;
   result.mBorderColor = 0xFFFFFFFF;
   result.mBGColor = aIter.mTableBgColor;
+  result.mBackfaceIsVisible = true;
 
   // All the tables frames have the same presContext, so we just use any one
   // that exists here:
   nsPresContext* presContext = aIter.mTable->PresContext();
   result.mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
 
   switch (mOwner) {
     case eTableOwner:
@@ -7386,16 +7388,17 @@ BCBlockDirSeg::BuildBorderParameters(BCP
       cell = mAjaCell;
       MOZ_FALLTHROUGH;
     case eCellOwner:
       owner = cell;
       break;
   }
   if (owner) {
     ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &result.mBorderStyle, &result.mBorderColor);
+    result.mBackfaceIsVisible = !owner->BackfaceIsHidden();
   }
   BCPixelSize smallHalf, largeHalf;
   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
   LogicalRect segRect(aIter.mTableWM,
                  mOffsetI - presContext->DevPixelsToAppUnits(largeHalf),
                  mOffsetB,
                  presContext->DevPixelsToAppUnits(mWidth), mLength);
   nscoord bEndBevelOffset = (mIsBEndBevel) ?
@@ -7487,16 +7490,17 @@ BCBlockDirSeg::CreateWebRenderCommands(B
   // each side to width of rect is fine.
   wr::BorderWidths borderWidths = wr::ToBorderWidths(transformedRect.size.width,
                                                      transformedRect.size.width,
                                                      transformedRect.size.width,
                                                      transformedRect.size.width);
   Range<const wr::BorderSide> wrsides(wrSide, 4);
   aBuilder.PushBorder(transformedRect,
                       transformedRect,
+                      param->mBackfaceIsVisible,
                       borderWidths,
                       wrsides,
                       borderRadii);
 }
 
 /**
  * Advance the start point of a segment
  */
@@ -7599,16 +7603,17 @@ BCInlineDirSeg::BuildBorderParameters(BC
   // get the border style, color and paint the segment
   LogicalSide side =
     aIter.IsDamageAreaBEndMost() ? eLogicalSideBEnd : eLogicalSideBStart;
   nsIFrame* rg   = aIter.mRg;  if (!rg) ABORT1(Nothing());
   nsIFrame* row  = aIter.mRow; if (!row) ABORT1(Nothing());
   nsIFrame* cell = mFirstCell;
   nsIFrame* col;
   nsIFrame* owner = nullptr;
+  result.mBackfaceIsVisible = true;
 
   // All the tables frames have the same presContext, so we just use any one
   // that exists here:
   nsPresContext* presContext = aIter.mTable->PresContext();
   result.mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
 
   result.mBorderStyle = NS_STYLE_BORDER_STYLE_SOLID;
   result.mBorderColor = 0xFFFFFFFF;
@@ -7657,16 +7662,17 @@ BCInlineDirSeg::BuildBorderParameters(BC
       cell = mAjaCell;
       MOZ_FALLTHROUGH;
     case eCellOwner:
       owner = cell;
       break;
   }
   if (owner) {
     ::GetPaintStyleInfo(owner, aIter.mTableWM, side, &result.mBorderStyle, &result.mBorderColor);
+    result.mBackfaceIsVisible = !owner->BackfaceIsHidden();
   }
   BCPixelSize smallHalf, largeHalf;
   DivideBCBorderSize(mWidth, smallHalf, largeHalf);
   LogicalRect segRect(aIter.mTableWM, mOffsetI,
                       mOffsetB - presContext->DevPixelsToAppUnits(largeHalf),
                       mLength,
                       presContext->DevPixelsToAppUnits(mWidth));
 
@@ -7746,16 +7752,17 @@ BCInlineDirSeg::CreateWebRenderCommands(
   // each side to height of rect is fine.
   wr::BorderWidths borderWidths = wr::ToBorderWidths(transformedRect.size.height,
                                                      transformedRect.size.height,
                                                      transformedRect.size.height,
                                                      transformedRect.size.height);
   Range<const wr::BorderSide> wrsides(wrSide, 4);
   aBuilder.PushBorder(transformedRect,
                       transformedRect,
+                      param->mBackfaceIsVisible,
                       borderWidths,
                       wrsides,
                       borderRadii);
 }
 
 /**
  * Advance the start point of a segment
  */
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -453,17 +453,17 @@ nsImageBoxFrame::CreateWebRenderCommands
   }
   const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest,
                                                              appUnitsPerDevPixel);
   wr::LayoutRect fill = aSc.ToRelativeLayoutRect(fillRect);
 
   LayoutDeviceSize gapSize(0, 0);
   SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
-  aBuilder.PushImage(fill, fill,
+  aBuilder.PushImage(fill, fill, !BackfaceIsHidden(),
                      wr::ToLayoutSize(fillRect.Size()), wr::ToLayoutSize(gapSize),
                      wr::ToImageRendering(sampleFilter), key.value());
 
   return DrawResult::SUCCESS;
 }
 
 nsRect
 nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint)
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2117,17 +2117,17 @@ nsChildView::AddWindowOverlayWebRenderCo
 
     if (needUpdate) {
       wr::ImageDescriptor descriptor(size, stride, format);
       aResources.UpdateImageBuffer(*mTitlebarImageKey, descriptor, buffer);
     }
 
     wr::LayoutRect rect = wr::ToLayoutRect(mTitlebarRect);
     aBuilder.PushImage(wr::LayoutRect{ rect.origin, { float(size.width), float(size.height) } },
-                       rect, wr::ImageRendering::Auto, *mTitlebarImageKey);
+                       rect, true, wr::ImageRendering::Auto, *mTitlebarImageKey);
   }
 }
 
 void
 nsChildView::CleanupWebRenderWindowOverlay(layers::WebRenderBridgeChild* aWrBridge,
                                            wr::IpcResourceUpdateQueue& aResources)
 {
   if (mTitlebarImageKey) {