Bug 1417197 - Create nsITheme::CreateWebRenderCommands in order to optimize simple theme fills, and add a Mac implementation. r=jrmuizel
authorMarkus Stange <mstange@themasta.com>
Tue, 14 Nov 2017 15:34:56 -0500
changeset 443534 d7af7d6aba30621a9c07b103a9ec25e5e6b576a3
parent 443533 69206021220625e9d7470fd16b0f1585ee7781c2
child 443535 4eebab7d065ec743f96343bd32906b4c1dfdd70c
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1417197
milestone59.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 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();