diff options
Diffstat (limited to 'layout/forms/nsButtonFrameRenderer.cpp')
-rw-r--r-- | layout/forms/nsButtonFrameRenderer.cpp | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/layout/forms/nsButtonFrameRenderer.cpp b/layout/forms/nsButtonFrameRenderer.cpp new file mode 100644 index 000000000..096031385 --- /dev/null +++ b/layout/forms/nsButtonFrameRenderer.cpp @@ -0,0 +1,516 @@ +/* -*- 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 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 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* +nsDisplayButtonBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder) +{ + return new nsDisplayItemGenericImageGeometry(this, aBuilder); +} + +void +nsDisplayButtonBorder::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 nsDisplayButtonBorder::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->PaintBorder(aBuilder, pc, *aCtx, mVisibleRect, r); + + nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result); +} + +nsRect +nsDisplayButtonBorder::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)); + } + + nsRect buttonRect; + GetButtonRect(mFrame->GetRectRelativeToSelf(), buttonRect); + + nsDisplayBackgroundImage::AppendBackgroundItemsToTop( + aBuilder, mFrame, buttonRect, aBackground); + + aBackground->AppendNewToTop(new (aBuilder) + 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 ((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::PaintBorder( + 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(); + + PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages() + ? PaintBorderFlags::SYNC_DECODE_IMAGES + : PaintBorderFlags(); + + nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, + mFrame, buttonRect); + + DrawResult 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) { + mOuterFocusStyle->StylePadding()->GetPadding(result); + 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(); + margin->GetMargin(innerFocusMargin); + } + + return innerFocusMargin; +} + +nsMargin +nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding() +{ + nsMargin result(0,0,0,0); + + if (mInnerFocusStyle) { + mInnerFocusStyle->StylePadding()->GetPadding(result); + 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 +} |