Bug 1417197 - Create nsITheme::CreateWebRenderCommands in order to optimize simple theme fills, and add a Mac implementation. r?jrmuizel
MozReview-Commit-ID: 1G9NHPwd5ST
--- 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();