Files
tubestation/layout/forms/nsButtonFrameRenderer.cpp
Cosmin Sabou 4e421f7abe Backed out 3 changesets (bug 1439960) for build bustages on APZInputBridgeChild.cpp and FrameBuilder.cpp. CLOSED TREE
Backed out changeset b8057c06fc4c (bug 1439960)
Backed out changeset c8d6b0fa1447 (bug 1439960)
Backed out changeset e6bd6ebc8597 (bug 1439960)
2018-03-20 23:18:44 +02:00

653 lines
22 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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"
#include "mozilla/layers/WebRenderLayerManager.h"
#define ACTIVE "active"
#define HOVER "hover"
#define FOCUS "focus"
using namespace mozilla;
using namespace mozilla::image;
using namespace mozilla::layers;
nsButtonFrameRenderer::nsButtonFrameRenderer()
{
MOZ_COUNT_CTOR(nsButtonFrameRenderer);
}
nsButtonFrameRenderer::~nsButtonFrameRenderer()
{
MOZ_COUNT_DTOR(nsButtonFrameRenderer);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
#endif
}
void
nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
{
mFrame = aFrame;
ReResolveStyles(aPresContext);
}
nsIFrame*
nsButtonFrameRenderer::GetFrame()
{
return mFrame;
}
void
nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool aNotify)
{
Element* element = mFrame->GetContent()->AsElement();
if (aDisabled)
element->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
aNotify);
else
element->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, aNotify);
}
bool
nsButtonFrameRenderer::isDisabled()
{
return mFrame->GetContent()->AsElement()->
State().HasState(NS_EVENT_STATE_DISABLED);
}
class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
public:
nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()) {
MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonBoxShadowOuter() {
MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
}
#endif
virtual bool CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) 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) const override;
NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
};
nsRect
nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) const
{
*aSnap = false;
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
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;
}
return true;
}
already_AddRefed<Layer>
nsDisplayButtonBoxShadowOuter::BuildLayer(
nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters)
{
return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
}
bool
nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
if (!CanBuildWebRenderDisplayItems()) {
return false;
}
int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
LayoutDeviceRect deviceBox =
LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
wr::LayoutRect deviceBoxRect = aSc.ToRelativeLayoutRect(deviceBox);
LayoutDeviceRect clipRect =
LayoutDeviceRect::FromAppUnits(mVisibleRect, appUnitsPerDevPixel);
wr::LayoutRect deviceClipRect = aSc.ToRelativeLayoutRect(clipRect);
bool hasBorderRadius;
Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
LayoutDeviceSize zeroSize;
wr::BorderRadius borderRadius = wr::ToBorderRadius(zeroSize, zeroSize,
zeroSize, zeroSize);
if (hasBorderRadius) {
mozilla::gfx::RectCornerRadii borderRadii;
hasBorderRadius = nsCSSRendering::GetBorderRadii(
shadowRect, shadowRect, mFrame, borderRadii);
if (hasBorderRadius) {
borderRadius = wr::ToBorderRadius(
LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
}
}
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);
LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
nsPoint(shadow->mXOffset, 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);
}
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
virtual ~nsDisplayButtonBorder() {
MOZ_COUNT_DTOR(nsDisplayButtonBorder);
}
#endif
virtual bool MustPaintOnContentSide() const override { return true; }
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) override {
aOutFrames->AppendElement(mFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) const override;
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) const override;
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) override;
NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
private:
nsButtonFrameRenderer* mBFR;
};
nsDisplayItemGeometry*
nsDisplayButtonBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
already_AddRefed<Layer>
nsDisplayButtonBorder::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters)
{
return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
}
bool
nsDisplayButtonBorder::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
// This is really a combination of paint box shadow inner +
// paint border.
nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
bool snap;
nsRegion visible = GetBounds(aDisplayListBuilder, &snap);
nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(aBuilder,
aSc,
visible,
mFrame,
buttonRect);
bool borderIsEmpty = false;
Maybe<nsCSSBorderRenderer> br =
nsCSSRendering::CreateBorderRenderer(mFrame->PresContext(),
nullptr,
mFrame,
nsRect(),
nsRect(ToReferenceFrame(), mFrame->GetSize()),
mFrame->StyleContext(),
&borderIsEmpty,
mFrame->GetSkipSides());
if (!br) {
return borderIsEmpty;
}
br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
return true;
}
void
nsDisplayButtonBorder::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) const
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void
nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
NS_ASSERTION(mFrame, "No frame?");
nsPresContext* pc = mFrame->PresContext();
nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
// draw the border and background inside the focus and outline borders
ImgDrawResult result =
mBFR->PaintBorder(aBuilder, pc, *aCtx, mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
nsRect
nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) const
{
*aSnap = false;
return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize())
: mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
class nsDisplayButtonForeground : public nsDisplayItem {
public:
nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonForeground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonForeground() {
MOZ_COUNT_DTOR(nsDisplayButtonForeground);
}
#endif
nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) const override;
virtual void Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx) override;
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters) override;
virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) override;
NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
private:
nsButtonFrameRenderer* mBFR;
};
nsDisplayItemGeometry*
nsDisplayButtonForeground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayButtonForeground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) const
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
gfxContext* aCtx)
{
nsPresContext *presContext = mFrame->PresContext();
const nsStyleDisplay *disp = mFrame->StyleDisplay();
if (!mFrame->IsThemed(disp) ||
!presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
// Draw the -moz-focus-inner border
ImgDrawResult result =
mBFR->PaintInnerFocusBorder(aBuilder, presContext, *aCtx, mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
}
already_AddRefed<mozilla::layers::Layer>
nsDisplayButtonForeground::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerLayerParameters& aContainerParameters)
{
return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
}
bool
nsDisplayButtonForeground::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::WebRenderLayerManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder)
{
Maybe<nsCSSBorderRenderer> br;
bool borderIsEmpty = false;
nsPresContext *presContext = mFrame->PresContext();
const nsStyleDisplay *disp = mFrame->StyleDisplay();
if (!mFrame->IsThemed(disp) ||
!presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
br = mBFR->CreateInnerFocusBorderRenderer(aDisplayListBuilder, presContext, nullptr,
mVisibleRect, r, &borderIsEmpty);
}
if (!br) {
return borderIsEmpty;
}
br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
return true;
}
nsresult
nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
nsDisplayList* aBackground,
nsDisplayList* aForeground)
{
if (mFrame->StyleEffects()->mBoxShadow) {
aBackground->AppendToTop(
MakeDisplayItem<nsDisplayButtonBoxShadowOuter>(aBuilder, this));
}
nsRect buttonRect = mFrame->GetRectRelativeToSelf();
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, mFrame, buttonRect, aBackground);
aBackground->AppendToTop(
MakeDisplayItem<nsDisplayButtonBorder>(aBuilder, this));
// Only display focus rings if we actually have them. Since at most one
// button would normally display a focus ring, most buttons won't have them.
if (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder()) {
aForeground->AppendToTop(
MakeDisplayItem<nsDisplayButtonForeground>(aBuilder, this));
}
return NS_OK;
}
void
nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& aResult)
{
aResult = aRect;
aResult.Deflate(mFrame->GetUsedBorderAndPadding());
nsMargin innerFocusPadding(0,0,0,0);
if (mInnerFocusStyle) {
mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding);
}
aResult.Inflate(innerFocusPadding);
}
ImgDrawResult
nsButtonFrameRenderer::PaintInnerFocusBorder(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
gfxContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// we draw the -moz-focus-inner border just inside the button's
// normal border and padding, to match Windows themes.
nsRect rect;
PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
ImgDrawResult result = ImgDrawResult::SUCCESS;
if (mInnerFocusStyle) {
GetButtonInnerFocusRect(aRect, rect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, rect, mInnerFocusStyle, flags);
}
return result;
}
Maybe<nsCSSBorderRenderer>
nsButtonFrameRenderer::CreateInnerFocusBorderRenderer(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
gfxContext* aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect,
bool* aBorderIsEmpty)
{
if (mInnerFocusStyle) {
nsRect rect;
GetButtonInnerFocusRect(aRect, rect);
gfx::DrawTarget* dt = aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr;
return nsCSSRendering::CreateBorderRenderer(aPresContext,
dt,
mFrame,
aDirtyRect,
rect,
mInnerFocusStyle,
aBorderIsEmpty);
}
return Nothing();
}
ImgDrawResult
nsButtonFrameRenderer::PaintBorder(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
gfxContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// get the button rect this is inside the focus and outline rects
nsRect buttonRect = aRect;
nsStyleContext* context = mFrame->StyleContext();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
mFrame, buttonRect);
ImgDrawResult result =
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, buttonRect, context, borderFlags);
return result;
}
/**
* Call this when styles change
*/
void
nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext)
{
// get all the styles
nsStyleContext* context = mFrame->StyleContext();
StyleSetHandle styleSet = aPresContext->StyleSet();
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
#endif
// get styles assigned to -moz-inner-focus (ie dotted border on Windows)
mInnerFocusStyle =
styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
CSSPseudoElementType::mozFocusInner,
context);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameAddRef();
}
#endif
}
nsStyleContext*
nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const
{
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
return mInnerFocusStyle;
default:
return nullptr;
}
}
void
nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext)
{
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
#endif
mInnerFocusStyle = aStyleContext;
break;
}
#ifdef DEBUG
aStyleContext->FrameAddRef();
#endif
}