no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / layout / forms / nsButtonFrameRenderer.cpp
blob598610cf72ea93eb7987e9e43ba2cc8a2c292216
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsButtonFrameRenderer.h"
7 #include "nsCSSRendering.h"
8 #include "nsPresContext.h"
9 #include "nsPresContextInlines.h"
10 #include "nsGkAtoms.h"
11 #include "nsCSSPseudoElements.h"
12 #include "nsNameSpaceManager.h"
13 #include "mozilla/ServoStyleSet.h"
14 #include "mozilla/Unused.h"
15 #include "nsDisplayList.h"
16 #include "nsITheme.h"
17 #include "nsIFrame.h"
18 #include "mozilla/dom/Element.h"
20 #include "gfxUtils.h"
21 #include "mozilla/layers/RenderRootStateManager.h"
23 using namespace mozilla;
24 using namespace mozilla::image;
25 using namespace mozilla::layers;
27 namespace mozilla {
28 class nsDisplayButtonBoxShadowOuter;
29 class nsDisplayButtonBorder;
30 class nsDisplayButtonForeground;
31 } // namespace mozilla
33 nsButtonFrameRenderer::nsButtonFrameRenderer() : mFrame(nullptr) {
34 MOZ_COUNT_CTOR(nsButtonFrameRenderer);
37 nsButtonFrameRenderer::~nsButtonFrameRenderer() {
38 MOZ_COUNT_DTOR(nsButtonFrameRenderer);
41 void nsButtonFrameRenderer::SetFrame(nsIFrame* aFrame,
42 nsPresContext* aPresContext) {
43 mFrame = aFrame;
44 ReResolveStyles(aPresContext);
47 nsIFrame* nsButtonFrameRenderer::GetFrame() { return mFrame; }
49 void nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool aNotify) {
50 dom::Element* element = mFrame->GetContent()->AsElement();
51 if (aDisabled)
52 element->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, u""_ns, aNotify);
53 else
54 element->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, aNotify);
57 bool nsButtonFrameRenderer::isDisabled() {
58 return mFrame->GetContent()->AsElement()->IsDisabled();
61 nsresult nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
62 nsDisplayList* aBackground,
63 nsDisplayList* aForeground) {
64 if (!mFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
65 aBackground->AppendNewToTop<nsDisplayButtonBoxShadowOuter>(aBuilder,
66 GetFrame());
69 nsRect buttonRect =
70 mFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mFrame);
72 const AppendedBackgroundType result =
73 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
74 aBuilder, mFrame, buttonRect, aBackground);
75 if (result == AppendedBackgroundType::None) {
76 aBuilder->BuildCompositorHitTestInfoIfNeeded(GetFrame(), aBackground);
79 aBackground->AppendNewToTop<nsDisplayButtonBorder>(aBuilder, GetFrame(),
80 this);
82 // Only display focus rings if we actually have them. Since at most one
83 // button would normally display a focus ring, most buttons won't have them.
84 if (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder() &&
85 mFrame->IsThemed() &&
86 mFrame->PresContext()->Theme()->ThemeWantsButtonInnerFocusRing()) {
87 aForeground->AppendNewToTop<nsDisplayButtonForeground>(aBuilder, GetFrame(),
88 this);
90 return NS_OK;
93 void nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect,
94 nsRect& aResult) {
95 aResult = aRect;
96 aResult.Deflate(mFrame->GetUsedBorderAndPadding());
98 if (mInnerFocusStyle) {
99 nsMargin innerFocusPadding(0, 0, 0, 0);
100 mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding);
102 nsMargin framePadding = mFrame->GetUsedPadding();
104 innerFocusPadding.top = std::min(innerFocusPadding.top, framePadding.top);
105 innerFocusPadding.right =
106 std::min(innerFocusPadding.right, framePadding.right);
107 innerFocusPadding.bottom =
108 std::min(innerFocusPadding.bottom, framePadding.bottom);
109 innerFocusPadding.left =
110 std::min(innerFocusPadding.left, framePadding.left);
112 aResult.Inflate(innerFocusPadding);
116 ImgDrawResult nsButtonFrameRenderer::PaintInnerFocusBorder(
117 nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext,
118 gfxContext& aRenderingContext, const nsRect& aDirtyRect,
119 const nsRect& aRect) {
120 // we draw the -moz-focus-inner border just inside the button's
121 // normal border and padding, to match Windows themes.
123 nsRect rect;
125 PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
126 ? PaintBorderFlags::SyncDecodeImages
127 : PaintBorderFlags();
129 ImgDrawResult result = ImgDrawResult::SUCCESS;
131 if (mInnerFocusStyle) {
132 GetButtonInnerFocusRect(aRect, rect);
134 result &=
135 nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
136 aDirtyRect, rect, mInnerFocusStyle, flags);
139 return result;
142 Maybe<nsCSSBorderRenderer>
143 nsButtonFrameRenderer::CreateInnerFocusBorderRenderer(
144 nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext,
145 gfxContext* aRenderingContext, const nsRect& aDirtyRect,
146 const nsRect& aRect, bool* aBorderIsEmpty) {
147 if (mInnerFocusStyle) {
148 nsRect rect;
149 GetButtonInnerFocusRect(aRect, rect);
151 gfx::DrawTarget* dt =
152 aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr;
153 return nsCSSRendering::CreateBorderRenderer(
154 aPresContext, dt, mFrame, aDirtyRect, rect, mInnerFocusStyle,
155 aBorderIsEmpty);
158 return Nothing();
161 ImgDrawResult nsButtonFrameRenderer::PaintBorder(nsDisplayListBuilder* aBuilder,
162 nsPresContext* aPresContext,
163 gfxContext& aRenderingContext,
164 const nsRect& aDirtyRect,
165 const nsRect& aRect) {
166 // get the button rect this is inside the focus and outline rects
167 nsRect buttonRect = aRect;
168 ComputedStyle* context = mFrame->Style();
170 PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
171 ? PaintBorderFlags::SyncDecodeImages
172 : PaintBorderFlags();
174 nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, mFrame,
175 buttonRect);
177 ImgDrawResult result =
178 nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
179 aDirtyRect, buttonRect, context, borderFlags);
181 return result;
185 * Call this when styles change
187 void nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) {
188 // get all the styles
189 ServoStyleSet* styleSet = aPresContext->StyleSet();
191 // get styles assigned to -moz-focus-inner (ie dotted border on Windows)
192 mInnerFocusStyle = styleSet->ProbePseudoElementStyle(
193 *mFrame->GetContent()->AsElement(), PseudoStyleType::mozFocusInner,
194 nullptr, mFrame->Style());
197 ComputedStyle* nsButtonFrameRenderer::GetComputedStyle(int32_t aIndex) const {
198 switch (aIndex) {
199 case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
200 return mInnerFocusStyle;
201 default:
202 return nullptr;
206 void nsButtonFrameRenderer::SetComputedStyle(int32_t aIndex,
207 ComputedStyle* aComputedStyle) {
208 switch (aIndex) {
209 case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
210 mInnerFocusStyle = aComputedStyle;
211 break;
215 namespace mozilla {
217 class nsDisplayButtonBoxShadowOuter : public nsPaintedDisplayItem {
218 public:
219 nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
220 nsIFrame* aFrame)
221 : nsPaintedDisplayItem(aBuilder, aFrame) {
222 MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
224 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonBoxShadowOuter)
226 virtual bool CreateWebRenderCommands(
227 mozilla::wr::DisplayListBuilder& aBuilder,
228 mozilla::wr::IpcResourceUpdateQueue& aResources,
229 const StackingContextHelper& aSc,
230 mozilla::layers::RenderRootStateManager* aManager,
231 nsDisplayListBuilder* aDisplayListBuilder) override;
233 bool CanBuildWebRenderDisplayItems();
235 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
236 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
237 bool* aSnap) const override;
238 NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
241 nsRect nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
242 bool* aSnap) const {
243 *aSnap = false;
244 return mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
247 void nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
248 gfxContext* aCtx) {
249 nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
251 nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
252 frameRect, GetPaintRect(aBuilder, aCtx));
255 bool nsDisplayButtonBoxShadowOuter::CanBuildWebRenderDisplayItems() {
256 // FIXME(emilio): Is this right? That doesn't make much sense.
257 if (mFrame->StyleEffects()->mBoxShadow.IsEmpty()) {
258 return false;
261 bool hasBorderRadius;
262 bool nativeTheme =
263 nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
265 // We don't support native themed things yet like box shadows around
266 // input buttons.
267 return !nativeTheme;
270 bool nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands(
271 mozilla::wr::DisplayListBuilder& aBuilder,
272 mozilla::wr::IpcResourceUpdateQueue& aResources,
273 const StackingContextHelper& aSc,
274 mozilla::layers::RenderRootStateManager* aManager,
275 nsDisplayListBuilder* aDisplayListBuilder) {
276 if (!CanBuildWebRenderDisplayItems()) {
277 return false;
279 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
280 nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
281 LayoutDeviceRect deviceBox =
282 LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
283 wr::LayoutRect deviceBoxRect = wr::ToLayoutRect(deviceBox);
285 bool dummy;
286 LayoutDeviceRect clipRect = LayoutDeviceRect::FromAppUnits(
287 GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel);
288 wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
290 bool hasBorderRadius;
291 Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
293 LayoutDeviceSize zeroSize;
294 wr::BorderRadius borderRadius =
295 wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
296 if (hasBorderRadius) {
297 gfx::RectCornerRadii borderRadii;
298 hasBorderRadius = nsCSSRendering::GetBorderRadii(shadowRect, shadowRect,
299 mFrame, borderRadii);
300 if (hasBorderRadius) {
301 borderRadius = wr::ToBorderRadius(borderRadii);
305 const Span<const StyleBoxShadow> shadows =
306 mFrame->StyleEffects()->mBoxShadow.AsSpan();
307 MOZ_ASSERT(!shadows.IsEmpty());
309 for (const StyleBoxShadow& shadow : Reversed(shadows)) {
310 if (shadow.inset) {
311 continue;
313 float blurRadius =
314 float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
315 gfx::DeviceColor shadowColor =
316 ToDeviceColor(nsCSSRendering::GetShadowColor(shadow.base, mFrame, 1.0));
318 LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
319 nsPoint(shadow.base.horizontal.ToAppUnits(),
320 shadow.base.vertical.ToAppUnits()),
321 appUnitsPerDevPixel);
323 float spreadRadius =
324 float(shadow.spread.ToAppUnits()) / float(appUnitsPerDevPixel);
326 aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
327 deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
328 wr::ToColorF(shadowColor), blurRadius, spreadRadius,
329 borderRadius, wr::BoxShadowClipMode::Outset);
331 return true;
334 class nsDisplayButtonBorder final : public nsPaintedDisplayItem {
335 public:
336 nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
337 nsButtonFrameRenderer* aRenderer)
338 : nsPaintedDisplayItem(aBuilder, aFrame), mBFR(aRenderer) {
339 MOZ_COUNT_CTOR(nsDisplayButtonBorder);
341 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonBorder)
343 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
344 HitTestState* aState,
345 nsTArray<nsIFrame*>* aOutFrames) override {
346 aOutFrames->AppendElement(mFrame);
348 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
349 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
350 bool* aSnap) const override;
351 virtual bool CreateWebRenderCommands(
352 mozilla::wr::DisplayListBuilder& aBuilder,
353 mozilla::wr::IpcResourceUpdateQueue& aResources,
354 const StackingContextHelper& aSc,
355 mozilla::layers::RenderRootStateManager* aManager,
356 nsDisplayListBuilder* aDisplayListBuilder) override;
357 NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
358 private:
359 nsButtonFrameRenderer* mBFR;
362 bool nsDisplayButtonBorder::CreateWebRenderCommands(
363 mozilla::wr::DisplayListBuilder& aBuilder,
364 mozilla::wr::IpcResourceUpdateQueue& aResources,
365 const StackingContextHelper& aSc,
366 mozilla::layers::RenderRootStateManager* aManager,
367 nsDisplayListBuilder* aDisplayListBuilder) {
368 // This is really a combination of paint box shadow inner +
369 // paint border.
370 aBuilder.StartGroup(this);
371 const nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
372 bool snap;
373 nsRect visible = GetBounds(aDisplayListBuilder, &snap);
374 nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
375 aBuilder, aSc, visible, mFrame, buttonRect);
377 bool borderIsEmpty = false;
378 Maybe<nsCSSBorderRenderer> br = nsCSSRendering::CreateBorderRenderer(
379 mFrame->PresContext(), nullptr, mFrame, nsRect(),
380 nsRect(ToReferenceFrame(), mFrame->GetSize()), mFrame->Style(),
381 &borderIsEmpty, mFrame->GetSkipSides());
382 if (!br) {
383 if (borderIsEmpty) {
384 aBuilder.FinishGroup();
385 } else {
386 aBuilder.CancelGroup(true);
389 return borderIsEmpty;
392 br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
393 aBuilder.FinishGroup();
394 return true;
397 void nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder,
398 gfxContext* aCtx) {
399 NS_ASSERTION(mFrame, "No frame?");
400 nsPresContext* pc = mFrame->PresContext();
401 nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
403 // draw the border and background inside the focus and outline borders
404 Unused << mBFR->PaintBorder(aBuilder, pc, *aCtx, GetPaintRect(aBuilder, aCtx),
408 nsRect nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder,
409 bool* aSnap) const {
410 *aSnap = false;
411 return aBuilder->IsForEventDelivery()
412 ? nsRect(ToReferenceFrame(), mFrame->GetSize())
413 : mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
416 class nsDisplayButtonForeground final : public nsPaintedDisplayItem {
417 public:
418 nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
419 nsButtonFrameRenderer* aRenderer)
420 : nsPaintedDisplayItem(aBuilder, aFrame), mBFR(aRenderer) {
421 MOZ_COUNT_CTOR(nsDisplayButtonForeground);
423 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayButtonForeground)
425 void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
426 bool CreateWebRenderCommands(
427 mozilla::wr::DisplayListBuilder& aBuilder,
428 mozilla::wr::IpcResourceUpdateQueue& aResources,
429 const StackingContextHelper& aSc,
430 mozilla::layers::RenderRootStateManager* aManager,
431 nsDisplayListBuilder* aDisplayListBuilder) override;
432 NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
433 private:
434 nsButtonFrameRenderer* mBFR;
437 void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
438 gfxContext* aCtx) {
439 nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
441 // Draw the -moz-focus-inner border
442 Unused << mBFR->PaintInnerFocusBorder(aBuilder, mFrame->PresContext(), *aCtx,
443 GetPaintRect(aBuilder, aCtx), r);
446 bool nsDisplayButtonForeground::CreateWebRenderCommands(
447 mozilla::wr::DisplayListBuilder& aBuilder,
448 mozilla::wr::IpcResourceUpdateQueue& aResources,
449 const StackingContextHelper& aSc,
450 mozilla::layers::RenderRootStateManager* aManager,
451 nsDisplayListBuilder* aDisplayListBuilder) {
452 Maybe<nsCSSBorderRenderer> br;
453 bool borderIsEmpty = false;
454 bool dummy;
455 nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
456 br = mBFR->CreateInnerFocusBorderRenderer(
457 aDisplayListBuilder, mFrame->PresContext(), nullptr,
458 GetBounds(aDisplayListBuilder, &dummy), r, &borderIsEmpty);
460 if (!br) {
461 return borderIsEmpty;
464 aBuilder.StartGroup(this);
465 br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
466 aBuilder.FinishGroup();
468 return true;
471 } // namespace mozilla