Bug 1392200 - Add backface-visibility support for layers-free mode. r=kats
authorMorris Tseng <mtseng@mozilla.com>
Thu, 21 Sep 2017 14:41:38 +0800
changeset 431709 28bb2483daae9c4c7f422a34a8daaecb5f510408
parent 431708 525ab770ca31eaae484bbbd733d6d8524bbec718
child 431710 9febdbb6447c248bead2b6546b19e274962cace2
push id7785
push userryanvm@gmail.com
push dateThu, 21 Sep 2017 13:39:55 +0000
treeherdermozilla-beta@06d4034a8a03 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1392200
milestone57.0a1
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
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/d3d11/TextureD3D11.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/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1124,23 +1124,24 @@ DXGITextureHostD3D11::PushExternalImage(
                                         Range<const wr::ImageKey>& aImageKeys)
 {
   switch (GetFormat()) {
     case gfx::SurfaceFormat::R8G8B8X8:
     case gfx::SurfaceFormat::R8G8B8A8:
     case gfx::SurfaceFormat::B8G8R8A8:
     case gfx::SurfaceFormat::B8G8R8X8: {
       MOZ_ASSERT(aImageKeys.length() == 1);
-      aBuilder.PushImage(aBounds, aClip, aFilter, aImageKeys[0]);
+      aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
       break;
     }
     case gfx::SurfaceFormat::NV12: {
       MOZ_ASSERT(aImageKeys.length() == 2);
       aBuilder.PushNV12Image(aBounds,
                              aClip,
+                             true,
                              aImageKeys[0],
                              aImageKeys[1],
                              wr::WrYuvColorSpace::Rec601,
                              aFilter);
       break;
     }
     default: {
       MOZ_ASSERT_UNREACHABLE("unexpected to be called");
@@ -1359,17 +1360,17 @@ void
 DXGIYCbCrTextureHostD3D11::PushExternalImage(wr::DisplayListBuilder& aBuilder,
                                              const wr::LayoutRect& aBounds,
                                              const wr::LayoutRect& aClip,
                                              wr::ImageRendering aFilter,
                                              Range<const wr::ImageKey>& aImageKeys)
 {
   // 1 image key
   MOZ_ASSERT(aImageKeys.length() == 1);
-  aBuilder.PushImage(aBounds, aClip, aFilter, aImageKeys[0]);
+  aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0]);
 }
 
 bool
 DXGIYCbCrTextureHostD3D11::AcquireTextureSource(CompositableTextureSourceRef& aTexture)
 {
   if (!EnsureTextureSource()) {
     return false;
   }
--- 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
@@ -245,17 +245,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;
@@ -264,16 +264,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
@@ -68,17 +68,17 @@ WebRenderCanvasLayer::RenderLayer(wr::Di
   // Eww. Re-creating image keys every time is bad. Probably not worth fixing here
   // since layers-full webrender is going away soon-ish. But don't reproduce what
   // you see here.
   wr::WrImageKey key = GenerateImageKey();
   aResources.AddExternalImage(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, aResources, 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
@@ -203,17 +203,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);
 
@@ -268,17 +268,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,
                                      wr::IpcResourceUpdateQueue& aResources)
 {
   if (!mContainer) {
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -252,21 +252,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.
@@ -416,17 +411,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();
@@ -453,17 +449,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,
@@ -679,16 +675,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
@@ -102,17 +102,17 @@ WebRenderPaintedLayer::CreateWebRenderDi
   DumpLayerInfo("PaintedLayer", rect);
 
   wr::WrImageKey key = GenerateImageKey();
   aResources.AddExternalImage(mExternalImageId.value(), key);
   // TODO: reuse image keys!
   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, aResources, 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
@@ -130,17 +130,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());
   }
@@ -151,17 +152,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
@@ -90,17 +90,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
@@ -246,16 +246,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
@@ -478,16 +478,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,
@@ -519,17 +520,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
@@ -325,16 +325,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
@@ -170,17 +170,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
@@ -5197,53 +5197,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
@@ -392,16 +392,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
@@ -2941,16 +2941,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
@@ -2992,16 +2993,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,
@@ -4261,16 +4263,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)
@@ -4780,21 +4783,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,
@@ -5083,16 +5088,17 @@ nsDisplayBorder::CreateBorderImageWebRen
       gfx::IntSize size;
       Maybe<wr::ImageKey> key = aManager->CreateImageKey(this, container, aBuilder, aResources, 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),
@@ -5116,25 +5122,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;
@@ -5447,16 +5455,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);
     }
@@ -5619,16 +5628,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
                              );
@@ -7938,17 +7948,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,
                                              aManager, aDisplayListBuilder);
 }
 
 bool
 nsDisplayTransform::UpdateScrollData(mozilla::layers::WebRenderScrollData* aData,
                                      mozilla::layers::WebRenderLayerScrollData* aLayerData)
@@ -8543,17 +8555,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,
                                        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
@@ -6494,16 +6494,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,
@@ -7321,16 +7322,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:
@@ -7377,16 +7379,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) ?
@@ -7477,16 +7480,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
  */
@@ -7589,16 +7593,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;
@@ -7647,16 +7652,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));
 
@@ -7735,16 +7741,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
@@ -455,17 +455,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) {