fune/layout/forms/nsButtonFrameRenderer.cpp
Phil Ringnalda f74761a568 Back out 6 changesets (bug 1227327) for Android reftest failures in 942672-1.html, background-position-2b.html and background-position-2c.html
CLOSED TREE

Backed out changeset c9b0ba301426 (bug 1227327)
Backed out changeset c857ad1fa01c (bug 1227327)
Backed out changeset 4ba58cd94310 (bug 1227327)
Backed out changeset 30e394faeb23 (bug 1227327)
Backed out changeset ce7fd04cc749 (bug 1227327)
Backed out changeset e0fe45294034 (bug 1227327)
2016-05-03 21:49:07 -07:00

525 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsDisplayList.h"
#include "nsITheme.h"
#include "nsFrame.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/Element.h"
#define ACTIVE "active"
#define HOVER "hover"
#define FOCUS "focus"
using namespace mozilla;
using namespace mozilla::image;
nsButtonFrameRenderer::nsButtonFrameRenderer()
{
MOZ_COUNT_CTOR(nsButtonFrameRenderer);
}
nsButtonFrameRenderer::~nsButtonFrameRenderer()
{
MOZ_COUNT_DTOR(nsButtonFrameRenderer);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameRelease();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
}
void
nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
{
mFrame = aFrame;
ReResolveStyles(aPresContext);
}
nsIFrame*
nsButtonFrameRenderer::GetFrame()
{
return mFrame;
}
void
nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify)
{
if (aDisabled)
mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
notify);
else
mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify);
}
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()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonBoxShadowOuter() {
MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
}
#endif
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) override;
NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
private:
nsButtonFrameRenderer* mBFR;
};
nsRect
nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
*aSnap = false;
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
void
nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) {
nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
nsRect buttonRect;
mBFR->GetButtonRect(frameRect, buttonRect);
nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
buttonRect, mVisibleRect);
}
class nsDisplayButtonBorderBackground : public nsDisplayItem {
public:
nsDisplayButtonBorderBackground(nsDisplayListBuilder* aBuilder,
nsButtonFrameRenderer* aRenderer)
: nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayButtonBorderBackground() {
MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground);
}
#endif
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
HitTestState* aState,
nsTArray<nsIFrame*> *aOutFrames) override {
aOutFrames->AppendElement(mFrame);
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap) override;
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
private:
nsButtonFrameRenderer* mBFR;
};
nsDisplayItemGeometry*
nsDisplayButtonBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayButtonBorderBackground::ComputeInvalidationRegion(
nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion)
{
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 nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* 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
DrawResult result =
mBFR->PaintBorderAndBackground(aBuilder, pc, *aCtx, mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
nsRect
nsDisplayButtonBorderBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
*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) override;
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) 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)
{
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,
nsRenderingContext* 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 focus and outline borders.
DrawResult result =
mBFR->PaintOutlineAndFocusBorders(aBuilder, presContext, *aCtx,
mVisibleRect, r);
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
}
nsresult
nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
nsDisplayList* aBackground,
nsDisplayList* aForeground)
{
if (mFrame->StyleEffects()->mBoxShadow) {
aBackground->AppendNewToTop(new (aBuilder)
nsDisplayButtonBoxShadowOuter(aBuilder, this));
}
// Almost all buttons draw some kind of background so there's not much
// point in checking whether we should create this item.
aBackground->AppendNewToTop(new (aBuilder)
nsDisplayButtonBorderBackground(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 ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) ||
(mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) {
aForeground->AppendNewToTop(new (aBuilder)
nsDisplayButtonForeground(aBuilder, this));
}
return NS_OK;
}
DrawResult
nsButtonFrameRenderer::PaintOutlineAndFocusBorders(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// once we have all that we'll draw the focus if we have it. We will
// need to draw 2 focuses, the inner and the outer. This is so we
// can do any kind of look and feel. Some buttons have focus on the
// outside like mac and motif. While others like windows have it
// inside (dotted line). Usually only one will be specifed. But I
// guess you could have both if you wanted to.
nsRect rect;
PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
DrawResult result = DrawResult::SUCCESS;
if (mOuterFocusStyle) {
// ---------- paint the outer focus border -------------
GetButtonOuterFocusRect(aRect, rect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, rect, mOuterFocusStyle, flags);
}
if (mInnerFocusStyle) {
// ---------- paint the inner focus border -------------
GetButtonInnerFocusRect(aRect, rect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, rect, mInnerFocusStyle, flags);
}
return result;
}
DrawResult
nsButtonFrameRenderer::PaintBorderAndBackground(
nsDisplayListBuilder* aBuilder,
nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
{
// get the button rect this is inside the focus and outline rects
nsRect buttonRect;
GetButtonRect(aRect, buttonRect);
nsStyleContext* context = mFrame->StyleContext();
uint32_t bgFlags = aBuilder->GetBackgroundPaintFlags();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
DrawResult result =
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame,
aDirtyRect, buttonRect, bgFlags);
nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
mFrame, buttonRect);
result &=
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
aDirtyRect, buttonRect, context, borderFlags);
return result;
}
void
nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
{
focusRect = aRect;
}
void
nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r)
{
r = aRect;
r.Deflate(GetButtonOuterFocusBorderAndPadding());
}
void
nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect)
{
GetButtonRect(aRect, focusRect);
focusRect.Deflate(GetButtonBorderAndPadding());
focusRect.Deflate(GetButtonInnerFocusMargin());
}
nsMargin
nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding()
{
nsMargin result(0,0,0,0);
if (mOuterFocusStyle) {
if (!mOuterFocusStyle->StylePadding()->GetPadding(result)) {
NS_NOTYETIMPLEMENTED("percentage padding");
}
result += mOuterFocusStyle->StyleBorder()->GetComputedBorder();
}
return result;
}
nsMargin
nsButtonFrameRenderer::GetButtonBorderAndPadding()
{
return mFrame->GetUsedBorderAndPadding();
}
/**
* Gets the size of the buttons border this is the union of the normal and disabled borders.
*/
nsMargin
nsButtonFrameRenderer::GetButtonInnerFocusMargin()
{
nsMargin innerFocusMargin(0,0,0,0);
if (mInnerFocusStyle) {
const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin();
if (!margin->GetMargin(innerFocusMargin)) {
NS_NOTYETIMPLEMENTED("percentage margin");
}
}
return innerFocusMargin;
}
nsMargin
nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding()
{
nsMargin result(0,0,0,0);
if (mInnerFocusStyle) {
if (!mInnerFocusStyle->StylePadding()->GetPadding(result)) {
NS_NOTYETIMPLEMENTED("percentage padding");
}
result += mInnerFocusStyle->StyleBorder()->GetComputedBorder();
}
return result;
}
// gets all the focus borders and padding that will be added to the regular border
nsMargin
nsButtonFrameRenderer::GetAddedButtonBorderAndPadding()
{
return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding();
}
/**
* 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();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
// style for the inner such as a dotted line (Windows)
mInnerFocusStyle =
styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
CSSPseudoElementType::mozFocusInner,
context);
// style for outer focus like a ridged border (MAC).
mOuterFocusStyle =
styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
CSSPseudoElementType::mozFocusOuter,
context);
#ifdef DEBUG
if (mInnerFocusStyle) {
mInnerFocusStyle->FrameAddRef();
}
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameAddRef();
}
#endif
}
nsStyleContext*
nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const
{
switch (aIndex) {
case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
return mInnerFocusStyle;
case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
return mOuterFocusStyle;
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;
case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
#ifdef DEBUG
if (mOuterFocusStyle) {
mOuterFocusStyle->FrameRelease();
}
#endif
mOuterFocusStyle = aStyleContext;
break;
}
#ifdef DEBUG
aStyleContext->FrameAddRef();
#endif
}