Bug 1417197 - Create nsITheme::CreateWebRenderCommands in order to optimize simple theme fills, and add a Mac implementation. r?jrmuizel draft
authorMarkus Stange <mstange@themasta.com>
Tue, 14 Nov 2017 15:34:56 -0500
changeset 697852 a75a90dd41aed4ba3191971defc7b604d35f2b72
parent 697851 bbfe180933d36c4e2d1117f029f384ee5a093ceb
child 697853 25cbdd23f7b5591387d9ab5a920ae4ad5bc520fc
child 697854 a26498d6b05ef4b810cbe363daf29209d007ae73
push id89122
push userbmo:mstange@themasta.com
push dateTue, 14 Nov 2017 20:35:38 +0000
reviewersjrmuizel
bugs1417197
milestone59.0a1
Bug 1417197 - Create nsITheme::CreateWebRenderCommands in order to optimize simple theme fills, and add a Mac implementation. r?jrmuizel MozReview-Commit-ID: 1G9NHPwd5ST
gfx/src/nsITheme.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
widget/cocoa/nsNativeThemeCocoa.h
widget/cocoa/nsNativeThemeCocoa.mm
--- a/gfx/src/nsITheme.h
+++ b/gfx/src/nsITheme.h
@@ -18,16 +18,27 @@ struct nsRect;
 class gfxContext;
 class nsAttrValue;
 class nsPresContext;
 class nsDeviceContext;
 class nsIFrame;
 class nsAtom;
 class nsIWidget;
 
+namespace mozilla {
+namespace layers {
+class StackingContextHelper;
+class WebRenderLayerManager;
+}
+namespace wr {
+class DisplayListBuilder;
+class IpcResourceUpdateQueue;
+}
+}
+
 // IID for the nsITheme interface
 // {7329f760-08cb-450f-8225-dae729096dec}
  #define NS_ITHEME_IID     \
 { 0x7329f760, 0x08cb, 0x450f, \
   { 0x82, 0x25, 0xda, 0xe7, 0x29, 0x09, 0x6d, 0xec } }
 // {0ae05515-cf7a-45a8-9e02-6556de7685b1}
 #define NS_THEMERENDERER_CID \
 { 0x0ae05515, 0xcf7a, 0x45a8, \
@@ -57,16 +68,30 @@ public:
    */
   NS_IMETHOD DrawWidgetBackground(gfxContext* aContext,
                                   nsIFrame* aFrame,
                                   uint8_t aWidgetType,
                                   const nsRect& aRect,
                                   const nsRect& aDirtyRect) = 0;
 
   /**
+   * Create WebRender commands for the theme background.
+   * @return true if the theme knows how to create WebRender commands for the
+   *         given widget type, false if DrawWidgetBackground need sto be called
+   *         instead.
+   */
+  virtual bool CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                const mozilla::layers::StackingContextHelper& aSc,
+                                                mozilla::layers::WebRenderLayerManager* aManager,
+                                                nsIFrame* aFrame,
+                                                uint8_t aWidgetType,
+                                                const nsRect& aRect) { return false; }
+
+  /**
    * Get the computed CSS border for the widget, in pixels.
    */
   NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, 
                              nsIFrame* aFrame,
                              uint8_t aWidgetType,
                              nsIntMargin* aResult)=0;
 
   /**
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4304,16 +4304,29 @@ nsDisplayThemedBackground::PaintInternal
   nsITheme *theme = presContext->GetTheme();
   nsRect drawing(mBackgroundRect);
   theme->GetWidgetOverflow(presContext->DeviceContext(), mFrame, mAppearance,
                            &drawing);
   drawing.IntersectRect(drawing, aBounds);
   theme->DrawWidgetBackground(aCtx, mFrame, mAppearance, mBackgroundRect, drawing);
 }
 
+
+bool
+nsDisplayThemedBackground::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                   mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                   const StackingContextHelper& aSc,
+                                                   mozilla::layers::WebRenderLayerManager* aManager,
+                                                   nsDisplayListBuilder* aDisplayListBuilder)
+{
+  nsITheme *theme = mFrame->PresContext()->GetTheme();
+  return theme->CreateWebRenderCommandsForWidget(aBuilder, aResources, aSc, aManager,
+                                                 mFrame, mAppearance, mBackgroundRect);
+}
+
 bool
 nsDisplayThemedBackground::IsWindowActive() const
 {
   EventStates docState = mFrame->GetContent()->OwnerDoc()->GetDocumentState();
   return !docState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
 }
 
 void
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3891,16 +3891,21 @@ public:
     nsDisplayItem::Destroy(aBuilder);
   }
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) const override;
   virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) const override;
+  virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                       mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                       const StackingContextHelper& aSc,
+                                       mozilla::layers::WebRenderLayerManager* aManager,
+                                       nsDisplayListBuilder* aDisplayListBuilder) override;
   virtual bool MustPaintOnContentSide() const override { return true; }
 
   /**
    * GetBounds() returns the background painting area.
    */
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) const override;
   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
--- a/widget/cocoa/nsNativeThemeCocoa.h
+++ b/widget/cocoa/nsNativeThemeCocoa.h
@@ -50,17 +50,24 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // The nsITheme interface.
   NS_IMETHOD DrawWidgetBackground(gfxContext* aContext,
                                   nsIFrame* aFrame,
                                   uint8_t aWidgetType,
                                   const nsRect& aRect,
                                   const nsRect& aDirtyRect) override;
-  NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext, 
+  bool CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
+                                        mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                        const mozilla::layers::StackingContextHelper& aSc,
+                                        mozilla::layers::WebRenderLayerManager* aManager,
+                                        nsIFrame* aFrame,
+                                        uint8_t aWidgetType,
+                                        const nsRect& aRect) override;
+  NS_IMETHOD GetWidgetBorder(nsDeviceContext* aContext,
                              nsIFrame* aFrame,
                              uint8_t aWidgetType,
                              nsIntMargin* aResult) override;
 
   virtual bool GetWidgetPadding(nsDeviceContext* aContext,
                                   nsIFrame* aFrame,
                                   uint8_t aWidgetType,
                                   nsIntMargin* aResult) override;
--- a/widget/cocoa/nsNativeThemeCocoa.mm
+++ b/widget/cocoa/nsNativeThemeCocoa.mm
@@ -25,16 +25,17 @@
 #include "nsNameSpaceManager.h"
 #include "nsPresContext.h"
 #include "nsGkAtoms.h"
 #include "nsCocoaFeatures.h"
 #include "nsCocoaWindow.h"
 #include "nsNativeThemeColors.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/EventStates.h"
+#include "mozilla/Range.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMeterElement.h"
 #include "nsLookAndFeel.h"
 #include "VibrancyManager.h"
 
 #include "gfxContext.h"
 #include "gfxQuartzSurface.h"
 #include "gfxQuartzNativeDrawing.h"
@@ -2221,16 +2222,36 @@ nsNativeThemeCocoa::DrawResizer(CGContex
   drawInfo.size = kHIThemeGrowBoxSizeNormal;
 
   RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
                                   IsFrameRTL(aFrame));
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+static Color
+NSColorToColor(NSColor* aColor)
+{
+  NSColor* deviceColor = [aColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+  return Color([deviceColor redComponent],
+               [deviceColor greenComponent],
+               [deviceColor blueComponent],
+               [deviceColor alphaComponent]);
+}
+
+static Color
+VibrancyFillColor(nsIFrame* aFrame, nsITheme::ThemeGeometryType aThemeGeometryType)
+{
+  ChildView* childView = ChildViewForFrame(aFrame);
+  if (childView) {
+    return NSColorToColor([childView vibrancyFillColorForThemeGeometryType:aThemeGeometryType]);
+  }
+  return Color();
+}
+
 static void
 DrawVibrancyBackground(CGContextRef cgContext, CGRect inBoxRect,
                        nsIFrame* aFrame, nsITheme::ThemeGeometryType aThemeGeometryType,
                        int aCornerRadiusIfOpaque = 0)
 {
   ChildView* childView = ChildViewForFrame(aFrame);
   if (childView) {
     NSRect rect = NSRectFromCGRect(inBoxRect);
@@ -2952,16 +2973,190 @@ nsNativeThemeCocoa::DrawWidgetBackground
 
   nativeDrawing.EndNativeDrawing();
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
+bool
+nsNativeThemeCocoa::CreateWebRenderCommandsForWidget(mozilla::wr::DisplayListBuilder& aBuilder,
+                                                     mozilla::wr::IpcResourceUpdateQueue& aResources,
+                                                     const mozilla::layers::StackingContextHelper& aSc,
+                                                     mozilla::layers::WebRenderLayerManager* aManager,
+                                                     nsIFrame* aFrame,
+                                                     uint8_t aWidgetType,
+                                                     const nsRect& aRect)
+{
+  nsPresContext* presContext = aFrame->PresContext();
+  wr::LayoutRect bounds = aSc.ToRelativeLayoutRect(
+    LayoutDeviceRect::FromAppUnits(aRect, presContext->AppUnitsPerDevPixel()));
+
+  EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+  // This list needs to stay consistent with the list in DrawWidgetBackground.
+  // For every switch case in DrawWidgetBackground, there are three choices:
+  //  - If the case in DrawWidgetBackground draws nothing for the given widget
+  //    type, then don't list it here. We will hit the "default: return true;"
+  //    case.
+  //  - If the case in DrawWidgetBackground draws something simple for the given
+  //    widget type, imitate that drawing using WebRender commands.
+  //  - If the case in DrawWidgetBackground draws something complicated for the
+  //    given widget type, return false here.
+  switch (aWidgetType) {
+    case NS_THEME_DIALOG:
+      if (IsWindowSheet(aFrame) && VibrancyManager::SystemSupportsVibrancy()) {
+        ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType);
+        aBuilder.PushRect(bounds, bounds, true,
+                          wr::ToColorF(VibrancyFillColor(aFrame, type)));
+        return true;
+      }
+      return false;
+
+    case NS_THEME_MENUPOPUP:
+    case NS_THEME_MENUARROW:
+    case NS_THEME_MENUITEM:
+    case NS_THEME_CHECKMENUITEM:
+    case NS_THEME_MENUSEPARATOR:
+    case NS_THEME_BUTTON_ARROW_UP:
+    case NS_THEME_BUTTON_ARROW_DOWN:
+      return false;
+
+    case NS_THEME_TOOLTIP:
+      if (VibrancyManager::SystemSupportsVibrancy()) {
+        ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType);
+        aBuilder.PushRect(bounds, bounds, true,
+                          wr::ToColorF(VibrancyFillColor(aFrame, type)));
+      } else {
+        aBuilder.PushRect(bounds, bounds, true,
+                          wr::ToColorF(Color(0.996, 1.000, 0.792, 0.950)));
+      }
+      return true;
+
+    case NS_THEME_CHECKBOX:
+    case NS_THEME_RADIO:
+    case NS_THEME_BUTTON:
+    case NS_THEME_FOCUS_OUTLINE:
+    case NS_THEME_MAC_HELP_BUTTON:
+    case NS_THEME_MAC_DISCLOSURE_BUTTON_OPEN:
+    case NS_THEME_MAC_DISCLOSURE_BUTTON_CLOSED:
+    case NS_THEME_BUTTON_BEVEL:
+    case NS_THEME_SPINNER:
+    case NS_THEME_SPINNER_UPBUTTON:
+    case NS_THEME_SPINNER_DOWNBUTTON:
+    case NS_THEME_TOOLBARBUTTON:
+    case NS_THEME_SEPARATOR:
+    case NS_THEME_TOOLBAR:
+    case NS_THEME_WINDOW_TITLEBAR:
+    case NS_THEME_STATUSBAR:
+    case NS_THEME_MENULIST:
+    case NS_THEME_MENULIST_TEXTFIELD:
+    case NS_THEME_MENULIST_BUTTON:
+    case NS_THEME_GROUPBOX:
+    case NS_THEME_TEXTFIELD:
+    case NS_THEME_NUMBER_INPUT:
+    case NS_THEME_SEARCHFIELD:
+    case NS_THEME_PROGRESSBAR:
+    case NS_THEME_PROGRESSBAR_VERTICAL:
+    case NS_THEME_METERBAR:
+    case NS_THEME_TREETWISTY:
+    case NS_THEME_TREETWISTYOPEN:
+    case NS_THEME_TREEHEADERCELL:
+    case NS_THEME_TREEITEM:
+    case NS_THEME_TREEVIEW:
+    case NS_THEME_SCALE_HORIZONTAL:
+    case NS_THEME_SCALE_VERTICAL:
+    case NS_THEME_RANGE:
+    case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+    case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+    case NS_THEME_SCROLLBARTRACK_HORIZONTAL:
+    case NS_THEME_SCROLLBARTRACK_VERTICAL:
+      return false;
+
+    case NS_THEME_TEXTFIELD_MULTILINE: {
+      if (eventState.HasState(NS_EVENT_STATE_FOCUS)) {
+        // We can't draw the focus ring using webrender, so fall back to regular
+        // drawing if we're focused.
+        return false;
+      }
+
+      // White background
+      aBuilder.PushRect(bounds, bounds, true,
+                        wr::ToColorF(Color(1.0, 1.0, 1.0, 1.0)));
+
+      // #737373 for the top border, #999999 for the rest.
+      wr::BorderSide side[4] = {
+        wr::ToBorderSide(Color(0.4510, 0.4510, 0.4510, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.6, 0.6, 0.6, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.6, 0.6, 0.6, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.6, 0.6, 0.6, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+      };
+
+      wr::BorderRadius borderRadius = wr::EmptyBorderRadius();
+      float borderWidth = presContext->CSSPixelsToDevPixels(1.0f);
+      wr::BorderWidths borderWidths =
+        wr::ToBorderWidths(borderWidth, borderWidth, borderWidth, borderWidth);
+
+      mozilla::Range<const wr::BorderSide> wrsides(side, 4);
+      aBuilder.PushBorder(bounds, bounds, true, borderWidths, wrsides, borderRadius);
+
+      return true;
+    }
+
+    case NS_THEME_LISTBOX: {
+      // White background
+      aBuilder.PushRect(bounds, bounds, true,
+                        wr::ToColorF(Color(1.0, 1.0, 1.0, 1.0)));
+
+      // #8E8E8E for the top border, #BEBEBE for the rest.
+      wr::BorderSide side[4] = {
+        wr::ToBorderSide(Color(0.557, 0.557, 0.557, 1.00), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.745, 0.745, 0.745, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.745, 0.745, 0.745, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+        wr::ToBorderSide(Color(0.745, 0.745, 0.745, 1.0), NS_STYLE_BORDER_STYLE_SOLID),
+      };
+
+      wr::BorderRadius borderRadius = wr::EmptyBorderRadius();
+      float borderWidth = presContext->CSSPixelsToDevPixels(1.0f);
+      wr::BorderWidths borderWidths =
+        wr::ToBorderWidths(borderWidth, borderWidth, borderWidth, borderWidth);
+
+      mozilla::Range<const wr::BorderSide> wrsides(side, 4);
+      aBuilder.PushBorder(bounds, bounds, true, borderWidths, wrsides, borderRadius);
+      return true;
+    }
+
+    case NS_THEME_MAC_SOURCE_LIST:
+      if (VibrancyManager::SystemSupportsVibrancy()) {
+        ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType);
+        aBuilder.PushRect(bounds, bounds, true,
+                          wr::ToColorF(VibrancyFillColor(aFrame, type)));
+        return true;
+      }
+      return false;
+
+    case NS_THEME_MAC_VIBRANCY_LIGHT:
+    case NS_THEME_MAC_VIBRANCY_DARK: {
+      ThemeGeometryType type = ThemeGeometryTypeForWidget(aFrame, aWidgetType);
+      aBuilder.PushRect(bounds, bounds, true,
+                        wr::ToColorF(VibrancyFillColor(aFrame, type)));
+      return true;
+    }
+
+    case NS_THEME_TAB:
+    case NS_THEME_TABPANELS:
+    case NS_THEME_RESIZER:
+      return false;
+
+    default:
+      return true;
+  }
+}
+
 nsIntMargin
 nsNativeThemeCocoa::DirectionAwareMargin(const nsIntMargin& aMargin,
                                          nsIFrame* aFrame)
 {
   // Assuming aMargin was originally specified for a horizontal LTR context,
   // reinterpret the values as logical, and then map to physical coords
   // according to aFrame's actual writing mode.
   WritingMode wm = aFrame->GetWritingMode();