summaryrefslogtreecommitdiffstats
path: root/layout/forms/nsButtonFrameRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/forms/nsButtonFrameRenderer.cpp')
-rw-r--r--layout/forms/nsButtonFrameRenderer.cpp516
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
+}