Bug 1392470 - Add wr support for nsDisplayButtonBoxShadowOuter; r?mattwoodrow draft
authorKevin Chen <kechen@mozilla.com>
Mon, 28 Aug 2017 11:29:35 +0800
changeset 653882 75488422a635706ca2bb168a809e627413404526
parent 650228 128a79130ecd6f277190d031a623f991c73c5272
child 728445 9b7d3191e7a16ee3dea5fe99901495f7ddbf927f
push id76443
push userbmo:kechen@mozilla.com
push dateMon, 28 Aug 2017 06:22:37 +0000
reviewersmattwoodrow
bugs1392470
milestone57.0a1
Bug 1392470 - Add wr support for nsDisplayButtonBoxShadowOuter; r?mattwoodrow MozReview-Commit-ID: Az1w8HusRlX
layout/forms/nsButtonFrameRenderer.cpp
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -5,16 +5,17 @@
 #include "nsButtonFrameRenderer.h"
 #include "nsCSSRendering.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "nsCSSPseudoElements.h"
 #include "nsNameSpaceManager.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/Unused.h"
 #include "nsDisplayList.h"
 #include "nsITheme.h"
 #include "nsFrame.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 #include "Layers.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
@@ -82,16 +83,35 @@ public:
     MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayButtonBoxShadowOuter() {
     MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
   }
 #endif
 
+  virtual bool CreateWebRenderCommands(
+    mozilla::wr::DisplayListBuilder& aBuilder,
+    const StackingContextHelper& aSc,
+    nsTArray<WebRenderParentCommand>& aParentCommands,
+    mozilla::layers::WebRenderLayerManager* aManager,
+    nsDisplayListBuilder* aDisplayListBuilder) override;
+
+  virtual LayerState GetLayerState(
+    nsDisplayListBuilder* aBuilder,
+    LayerManager* aManager,
+    const ContainerLayerParameters& aParameters) override;
+
+  virtual already_AddRefed<Layer> BuildLayer(
+    nsDisplayListBuilder* aBuilder,
+    LayerManager* aManager,
+    const ContainerLayerParameters& aContainerParameters) override;
+
+  bool CanBuildWebRenderDisplayItems();
+
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) override;
   NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
 };
 
 nsRect
@@ -104,16 +124,147 @@ void
 nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
                                      gfxContext* aCtx) {
   nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
 
   nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
                                       frameRect, mVisibleRect);
 }
 
+bool
+nsDisplayButtonBoxShadowOuter::CanBuildWebRenderDisplayItems()
+{
+  nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+  if (!shadows) {
+    return false;
+  }
+
+  bool hasBorderRadius;
+  bool nativeTheme =
+    nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
+
+  // We don't support native themed things yet like box shadows around
+  // input buttons.
+  if (nativeTheme) {
+    return false;
+  }
+
+  nsPoint offset = ToReferenceFrame();
+  nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
+  nsRect frameRect =
+    nsCSSRendering::GetShadowRect(borderRect, nativeTheme, mFrame);
+
+  if (hasBorderRadius) {
+    nscoord twipsRadii[8];
+    nsSize sz = frameRect.Size();
+    hasBorderRadius = mFrame->GetBorderRadii(sz, sz, Sides(), twipsRadii);
+  }
+
+  if (hasBorderRadius) {
+    nsCSSRendering::RectCornerRadii borderRadii;
+    nsCSSRendering::GetBorderRadii(frameRect, borderRect, mFrame, borderRadii);
+    if (!borderRadii.AreRadiiSame()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+LayerState
+nsDisplayButtonBoxShadowOuter::GetLayerState(
+  nsDisplayListBuilder* aBuilder,
+  LayerManager* aManager,
+  const ContainerLayerParameters& aParameters)
+{
+  if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowOuterBoxShadow) &&
+      CanBuildWebRenderDisplayItems()) {
+    return LAYER_ACTIVE;
+  }
+  return LAYER_NONE;
+}
+
+already_AddRefed<Layer>
+nsDisplayButtonBoxShadowOuter::BuildLayer(
+  nsDisplayListBuilder* aBuilder,
+  LayerManager* aManager,
+  const ContainerLayerParameters& aContainerParameters)
+{
+  return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
+}
+
+bool
+nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands(
+  mozilla::wr::DisplayListBuilder& aBuilder,
+  const StackingContextHelper& aSc,
+  nsTArray<WebRenderParentCommand>& aParentCommands,
+  mozilla::layers::WebRenderLayerManager* aManager,
+  nsDisplayListBuilder* aDisplayListBuilder)
+{
+  if (aManager->IsLayersFreeTransaction()) {
+    ContainerLayerParameters parameter;
+    if (GetLayerState(aDisplayListBuilder, aManager, parameter) !=
+        LAYER_ACTIVE) {
+      return false;
+    }
+  }
+  int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
+  LayoutDeviceRect deviceBox =
+    LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
+  wr::LayoutRect deviceBoxRect = aSc.ToRelativeLayoutRectRounded(deviceBox);
+
+  LayoutDeviceRect clipRect =
+    LayoutDeviceRect::FromAppUnits(mVisibleRect, appUnitsPerDevPixel);
+  wr::LayoutRect deviceClipRect = aSc.ToRelativeLayoutRect(clipRect);
+
+  bool hasBorderRadius;
+  Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
+
+  float borderRadius = 0.0;
+  if (hasBorderRadius) {
+    mozilla::gfx::RectCornerRadii borderRadii;
+    hasBorderRadius = nsCSSRendering::GetBorderRadii(
+      shadowRect, shadowRect, mFrame, borderRadii);
+    // TODO: support non-uniform border radius.
+    MOZ_ASSERT(borderRadii.AreRadiiSame());
+    borderRadius = hasBorderRadius ? borderRadii.TopLeft().width : 0.0;
+  }
+
+  nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
+  MOZ_ASSERT(shadows);
+
+  for (uint32_t i = shadows->Length(); i > 0; i--) {
+    nsCSSShadowItem* shadow = shadows->ShadowAt(i - 1);
+    if (shadow->mInset) {
+      continue;
+    }
+    float blurRadius = float(shadow->mRadius) / float(appUnitsPerDevPixel);
+    gfx::Color shadowColor =
+      nsCSSRendering::GetShadowColor(shadow, mFrame, 1.0);
+
+    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,
+                           deviceBoxRect,
+                           wr::ToLayoutVector2D(shadowOffset),
+                           wr::ToColorF(shadowColor),
+                           blurRadius,
+                           spreadRadius,
+                           borderRadius,
+                           wr::BoxShadowClipMode::Outset);
+  }
+  return true;
+}
+
 class nsDisplayButtonBorder : public nsDisplayItem {
 public:
   nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder,
                                   nsButtonFrameRenderer* aRenderer)
     : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
     MOZ_COUNT_CTOR(nsDisplayButtonBorder);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING