fune/layout/forms/nsButtonFrameRenderer.cpp
Ciure Andrei 00a73f5055 Backed out 17 changesets (bug 1550554, bug 1549593, bug 1551991, bug 1529002) for failing multiple Android tests and Windows 2012 bustages CLOSED TREE
Backed out changeset 3bb3fafa62e2 (bug 1551991)
Backed out changeset e12a979de502 (bug 1551991)
Backed out changeset d81e4aa6bf0c (bug 1551991)
Backed out changeset c354e61f2a34 (bug 1551991)
Backed out changeset 37fd602bebc2 (bug 1551991)
Backed out changeset 6c1f00cc30ca (bug 1551991)
Backed out changeset 8a7a0329bdc3 (bug 1551991)
Backed out changeset 86159475ddd3 (bug 1551991)
Backed out changeset 35f91a9ea82a (bug 1529002)
Backed out changeset 6798155e71dc (bug 1529002)
Backed out changeset b90c2cf5b8c5 (bug 1550554)
Backed out changeset 882ab9868c95 (bug 1550554)
Backed out changeset b28a48e2ed21 (bug 1550554)
Backed out changeset 2c31fe18eefd (bug 1550554)
Backed out changeset 57f2362aa538 (bug 1550554)
Backed out changeset 45f171b26e95 (bug 1550554)
Backed out changeset 2e4b263c9410 (bug 1549593)
2019-05-16 13:17:10 +03:00

531 lines
20 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 "nsPresContextInlines.h"
#include "nsGkAtoms.h"
#include "nsCSSPseudoElements.h"
#include "nsNameSpaceManager.h"
#include "mozilla/ServoStyleSet.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/RenderRootStateManager.h"
#define ACTIVE "active"
#define HOVER "hover"
#define FOCUS "focus"
using namespace mozilla;
using namespace mozilla::image;
using namespace mozilla::layers;
nsButtonFrameRenderer::nsButtonFrameRenderer() : mFrame(nullptr) {
MOZ_COUNT_CTOR(nsButtonFrameRenderer);
}
nsButtonFrameRenderer::~nsButtonFrameRenderer() {
MOZ_COUNT_DTOR(nsButtonFrameRenderer);
}
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 nsPaintedDisplayItem {
public:
nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame)
: nsPaintedDisplayItem(aBuilder, aFrame) {
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::RenderRootStateManager* aManager,
nsDisplayListBuilder* aDisplayListBuilder) 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, GetPaintRect());
}
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;
}
bool nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* 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 = wr::ToRoundedLayoutRect(deviceBox);
LayoutDeviceRect clipRect =
LayoutDeviceRect::FromAppUnits(GetPaintRect(), appUnitsPerDevPixel);
wr::LayoutRect deviceClipRect = wr::ToRoundedLayoutRect(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 final : public nsPaintedDisplayItem {
public:
nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsButtonFrameRenderer* aRenderer)
: nsPaintedDisplayItem(aBuilder, aFrame), 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 bool CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* 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);
}
bool nsDisplayButtonBorder::CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* 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->Style(),
&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, GetPaintRect(), 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 final : public nsPaintedDisplayItem {
public:
nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsButtonFrameRenderer* aRenderer)
: nsPaintedDisplayItem(aBuilder, aFrame), 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 bool CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* 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, GetPaintRect(), r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
}
bool nsDisplayButtonForeground::CreateWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder,
mozilla::wr::IpcResourceUpdateQueue& aResources,
const StackingContextHelper& aSc,
mozilla::layers::RenderRootStateManager* 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, GetPaintRect(), 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->AppendNewToTop<nsDisplayButtonBoxShadowOuter>(aBuilder,
GetFrame());
}
nsRect buttonRect = mFrame->GetRectRelativeToSelf();
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, mFrame,
buttonRect, aBackground);
aBackground->AppendNewToTop<nsDisplayButtonBorder>(aBuilder, GetFrame(),
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->AppendNewToTop<nsDisplayButtonForeground>(aBuilder, GetFrame(),
this);
}
return NS_OK;
}
void nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect,
nsRect& aResult) {
aResult = aRect;
aResult.Deflate(mFrame->GetUsedBorderAndPadding());
if (mInnerFocusStyle) {
nsMargin innerFocusPadding(0, 0, 0, 0);
mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding);
nsMargin framePadding = mFrame->GetUsedPadding();
innerFocusPadding.top = std::min(innerFocusPadding.top, framePadding.top);
innerFocusPadding.right =
std::min(innerFocusPadding.right, framePadding.right);
innerFocusPadding.bottom =
std::min(innerFocusPadding.bottom, framePadding.bottom);
innerFocusPadding.left =
std::min(innerFocusPadding.left, framePadding.left);
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::SyncDecodeImages
: 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;
ComputedStyle* context = mFrame->Style();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SyncDecodeImages
: 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
ComputedStyle* context = mFrame->Style();
ServoStyleSet* styleSet = aPresContext->StyleSet();
// get styles assigned to -moz-focus-inner (ie dotted border on Windows)
mInnerFocusStyle = styleSet->ProbePseudoElementStyle(
*mFrame->GetContent()->AsElement(), PseudoStyleType::mozFocusInner,
context);
}
ComputedStyle* nsButtonFrameRenderer::GetComputedStyle(int32_t aIndex) const {
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
return mInnerFocusStyle;
default:
return nullptr;
}
}
void nsButtonFrameRenderer::SetComputedStyle(int32_t aIndex,
ComputedStyle* aComputedStyle) {
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
mInnerFocusStyle = aComputedStyle;
break;
}
}