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 "nsGkAtoms.h"
10 #include "nsCSSPseudoElements.h"
11 #include "nsNameSpaceManager.h"
12 #include "mozilla/StyleSetHandle.h"
13 #include "mozilla/StyleSetHandleInlines.h"
14 #include "mozilla/Unused.h"
15 #include "nsDisplayList.h"
16 #include "nsITheme.h"
17 #include "nsFrame.h"
18 #include "mozilla/EventStates.h"
19 #include "mozilla/dom/Element.h"
20 #include "Layers.h"
21 #include "gfxPrefs.h"
22 #include "gfxUtils.h"
23 #include "mozilla/layers/WebRenderLayerManager.h"
24 
25 #define ACTIVE "active"
26 #define HOVER "hover"
27 #define FOCUS "focus"
28 
29 using namespace mozilla;
30 using namespace mozilla::image;
31 using namespace mozilla::layers;
32 
nsButtonFrameRenderer()33 nsButtonFrameRenderer::nsButtonFrameRenderer() {
34   MOZ_COUNT_CTOR(nsButtonFrameRenderer);
35 }
36 
~nsButtonFrameRenderer()37 nsButtonFrameRenderer::~nsButtonFrameRenderer() {
38   MOZ_COUNT_DTOR(nsButtonFrameRenderer);
39 
40 #ifdef DEBUG
41   if (mInnerFocusStyle) {
42     mInnerFocusStyle->FrameRelease();
43   }
44 #endif
45 }
46 
SetFrame(nsFrame * aFrame,nsPresContext * aPresContext)47 void nsButtonFrameRenderer::SetFrame(nsFrame* aFrame,
48                                      nsPresContext* aPresContext) {
49   mFrame = aFrame;
50   ReResolveStyles(aPresContext);
51 }
52 
GetFrame()53 nsIFrame* nsButtonFrameRenderer::GetFrame() { return mFrame; }
54 
SetDisabled(bool aDisabled,bool aNotify)55 void nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool aNotify) {
56   Element* element = mFrame->GetContent()->AsElement();
57   if (aDisabled)
58     element->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
59                      aNotify);
60   else
61     element->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, aNotify);
62 }
63 
isDisabled()64 bool nsButtonFrameRenderer::isDisabled() {
65   return mFrame->GetContent()->AsElement()->State().HasState(
66       NS_EVENT_STATE_DISABLED);
67 }
68 
69 class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
70  public:
nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder * aBuilder,nsButtonFrameRenderer * aRenderer)71   nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
72                                 nsButtonFrameRenderer* aRenderer)
73       : nsDisplayItem(aBuilder, aRenderer->GetFrame()) {
74     MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
75   }
76 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayButtonBoxShadowOuter()77   virtual ~nsDisplayButtonBoxShadowOuter() {
78     MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
79   }
80 #endif
81 
82   virtual bool CreateWebRenderCommands(
83       mozilla::wr::DisplayListBuilder& aBuilder,
84       mozilla::wr::IpcResourceUpdateQueue& aResources,
85       const StackingContextHelper& aSc,
86       mozilla::layers::WebRenderLayerManager* aManager,
87       nsDisplayListBuilder* aDisplayListBuilder) override;
88 
89   virtual already_AddRefed<Layer> BuildLayer(
90       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
91       const ContainerLayerParameters& aContainerParameters) override;
92 
93   bool CanBuildWebRenderDisplayItems();
94 
95   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
96   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
97                            bool* aSnap) const override;
98   NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
99 };
100 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const101 nsRect nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder,
102                                                 bool* aSnap) const {
103   *aSnap = false;
104   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
105 }
106 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)107 void nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
108                                           gfxContext* aCtx) {
109   nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
110 
111   nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
112                                       frameRect, mVisibleRect);
113 }
114 
CanBuildWebRenderDisplayItems()115 bool nsDisplayButtonBoxShadowOuter::CanBuildWebRenderDisplayItems() {
116   nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
117   if (!shadows) {
118     return false;
119   }
120 
121   bool hasBorderRadius;
122   bool nativeTheme =
123       nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
124 
125   // We don't support native themed things yet like box shadows around
126   // input buttons.
127   if (nativeTheme) {
128     return false;
129   }
130 
131   return true;
132 }
133 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)134 already_AddRefed<Layer> nsDisplayButtonBoxShadowOuter::BuildLayer(
135     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
136     const ContainerLayerParameters& aContainerParameters) {
137   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
138 }
139 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)140 bool nsDisplayButtonBoxShadowOuter::CreateWebRenderCommands(
141     mozilla::wr::DisplayListBuilder& aBuilder,
142     mozilla::wr::IpcResourceUpdateQueue& aResources,
143     const StackingContextHelper& aSc,
144     mozilla::layers::WebRenderLayerManager* aManager,
145     nsDisplayListBuilder* aDisplayListBuilder) {
146   if (!CanBuildWebRenderDisplayItems()) {
147     return false;
148   }
149   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
150   nsRect shadowRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
151   LayoutDeviceRect deviceBox =
152       LayoutDeviceRect::FromAppUnits(shadowRect, appUnitsPerDevPixel);
153   wr::LayoutRect deviceBoxRect = aSc.ToRelativeLayoutRect(deviceBox);
154 
155   LayoutDeviceRect clipRect =
156       LayoutDeviceRect::FromAppUnits(mVisibleRect, appUnitsPerDevPixel);
157   wr::LayoutRect deviceClipRect = aSc.ToRelativeLayoutRect(clipRect);
158 
159   bool hasBorderRadius;
160   Unused << nsCSSRendering::HasBoxShadowNativeTheme(mFrame, hasBorderRadius);
161 
162   LayoutDeviceSize zeroSize;
163   wr::BorderRadius borderRadius =
164       wr::ToBorderRadius(zeroSize, zeroSize, zeroSize, zeroSize);
165   if (hasBorderRadius) {
166     mozilla::gfx::RectCornerRadii borderRadii;
167     hasBorderRadius = nsCSSRendering::GetBorderRadii(shadowRect, shadowRect,
168                                                      mFrame, borderRadii);
169     if (hasBorderRadius) {
170       borderRadius = wr::ToBorderRadius(
171           LayoutDeviceSize::FromUnknownSize(borderRadii.TopLeft()),
172           LayoutDeviceSize::FromUnknownSize(borderRadii.TopRight()),
173           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomLeft()),
174           LayoutDeviceSize::FromUnknownSize(borderRadii.BottomRight()));
175     }
176   }
177 
178   nsCSSShadowArray* shadows = mFrame->StyleEffects()->mBoxShadow;
179   MOZ_ASSERT(shadows);
180 
181   for (uint32_t i = shadows->Length(); i > 0; i--) {
182     nsCSSShadowItem* shadow = shadows->ShadowAt(i - 1);
183     if (shadow->mInset) {
184       continue;
185     }
186     float blurRadius = float(shadow->mRadius) / float(appUnitsPerDevPixel);
187     gfx::Color shadowColor =
188         nsCSSRendering::GetShadowColor(shadow, mFrame, 1.0);
189 
190     LayoutDevicePoint shadowOffset = LayoutDevicePoint::FromAppUnits(
191         nsPoint(shadow->mXOffset, shadow->mYOffset), appUnitsPerDevPixel);
192 
193     float spreadRadius = float(shadow->mSpread) / float(appUnitsPerDevPixel);
194 
195     aBuilder.PushBoxShadow(deviceBoxRect, deviceClipRect, !BackfaceIsHidden(),
196                            deviceBoxRect, wr::ToLayoutVector2D(shadowOffset),
197                            wr::ToColorF(shadowColor), blurRadius, spreadRadius,
198                            borderRadius, wr::BoxShadowClipMode::Outset);
199   }
200   return true;
201 }
202 
203 class nsDisplayButtonBorder : public nsDisplayItem {
204  public:
nsDisplayButtonBorder(nsDisplayListBuilder * aBuilder,nsButtonFrameRenderer * aRenderer)205   nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder,
206                         nsButtonFrameRenderer* aRenderer)
207       : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
208     MOZ_COUNT_CTOR(nsDisplayButtonBorder);
209   }
210 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayButtonBorder()211   virtual ~nsDisplayButtonBorder() { MOZ_COUNT_DTOR(nsDisplayButtonBorder); }
212 #endif
MustPaintOnContentSide() const213   virtual bool MustPaintOnContentSide() const override { return true; }
214 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)215   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
216                        HitTestState* aState,
217                        nsTArray<nsIFrame*>* aOutFrames) override {
218     aOutFrames->AppendElement(mFrame);
219   }
220   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
221   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
222                            bool* aSnap) const override;
223   virtual nsDisplayItemGeometry* AllocateGeometry(
224       nsDisplayListBuilder* aBuilder) override;
225   virtual void ComputeInvalidationRegion(
226       nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
227       nsRegion* aInvalidRegion) const override;
228   virtual already_AddRefed<Layer> BuildLayer(
229       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
230       const ContainerLayerParameters& aContainerParameters) override;
231   virtual bool CreateWebRenderCommands(
232       mozilla::wr::DisplayListBuilder& aBuilder,
233       mozilla::wr::IpcResourceUpdateQueue& aResources,
234       const StackingContextHelper& aSc,
235       mozilla::layers::WebRenderLayerManager* aManager,
236       nsDisplayListBuilder* aDisplayListBuilder) override;
237   NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
238  private:
239   nsButtonFrameRenderer* mBFR;
240 };
241 
AllocateGeometry(nsDisplayListBuilder * aBuilder)242 nsDisplayItemGeometry* nsDisplayButtonBorder::AllocateGeometry(
243     nsDisplayListBuilder* aBuilder) {
244   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
245 }
246 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)247 already_AddRefed<Layer> nsDisplayButtonBorder::BuildLayer(
248     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
249     const ContainerLayerParameters& aContainerParameters) {
250   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
251 }
252 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)253 bool nsDisplayButtonBorder::CreateWebRenderCommands(
254     mozilla::wr::DisplayListBuilder& aBuilder,
255     mozilla::wr::IpcResourceUpdateQueue& aResources,
256     const StackingContextHelper& aSc,
257     mozilla::layers::WebRenderLayerManager* aManager,
258     nsDisplayListBuilder* aDisplayListBuilder) {
259   // This is really a combination of paint box shadow inner +
260   // paint border.
261   nsRect buttonRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
262   bool snap;
263   nsRegion visible = GetBounds(aDisplayListBuilder, &snap);
264   nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands(
265       aBuilder, aSc, visible, mFrame, buttonRect);
266 
267   bool borderIsEmpty = false;
268   Maybe<nsCSSBorderRenderer> br = nsCSSRendering::CreateBorderRenderer(
269       mFrame->PresContext(), nullptr, mFrame, nsRect(),
270       nsRect(ToReferenceFrame(), mFrame->GetSize()), mFrame->StyleContext(),
271       &borderIsEmpty, mFrame->GetSkipSides());
272   if (!br) {
273     return borderIsEmpty;
274   }
275 
276   br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
277 
278   return true;
279 }
280 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const281 void nsDisplayButtonBorder::ComputeInvalidationRegion(
282     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
283     nsRegion* aInvalidRegion) const {
284   auto geometry =
285       static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
286 
287   if (aBuilder->ShouldSyncDecodeImages() &&
288       geometry->ShouldInvalidateToSyncDecodeImages()) {
289     bool snap;
290     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
291   }
292 
293   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
294 }
295 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)296 void nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder,
297                                   gfxContext* aCtx) {
298   NS_ASSERTION(mFrame, "No frame?");
299   nsPresContext* pc = mFrame->PresContext();
300   nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
301 
302   // draw the border and background inside the focus and outline borders
303   ImgDrawResult result =
304       mBFR->PaintBorder(aBuilder, pc, *aCtx, mVisibleRect, r);
305 
306   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
307 }
308 
GetBounds(nsDisplayListBuilder * aBuilder,bool * aSnap) const309 nsRect nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder,
310                                         bool* aSnap) const {
311   *aSnap = false;
312   return aBuilder->IsForEventDelivery()
313              ? nsRect(ToReferenceFrame(), mFrame->GetSize())
314              : mFrame->GetVisualOverflowRectRelativeToSelf() +
315                    ToReferenceFrame();
316 }
317 
318 class nsDisplayButtonForeground : public nsDisplayItem {
319  public:
nsDisplayButtonForeground(nsDisplayListBuilder * aBuilder,nsButtonFrameRenderer * aRenderer)320   nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder,
321                             nsButtonFrameRenderer* aRenderer)
322       : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
323     MOZ_COUNT_CTOR(nsDisplayButtonForeground);
324   }
325 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayButtonForeground()326   virtual ~nsDisplayButtonForeground() {
327     MOZ_COUNT_DTOR(nsDisplayButtonForeground);
328   }
329 #endif
330 
331   nsDisplayItemGeometry* AllocateGeometry(
332       nsDisplayListBuilder* aBuilder) override;
333   void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
334                                  const nsDisplayItemGeometry* aGeometry,
335                                  nsRegion* aInvalidRegion) const override;
336   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
337   virtual already_AddRefed<Layer> BuildLayer(
338       nsDisplayListBuilder* aBuilder, LayerManager* aManager,
339       const ContainerLayerParameters& aContainerParameters) override;
340   virtual bool CreateWebRenderCommands(
341       mozilla::wr::DisplayListBuilder& aBuilder,
342       mozilla::wr::IpcResourceUpdateQueue& aResources,
343       const StackingContextHelper& aSc,
344       mozilla::layers::WebRenderLayerManager* aManager,
345       nsDisplayListBuilder* aDisplayListBuilder) override;
346   NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
347  private:
348   nsButtonFrameRenderer* mBFR;
349 };
350 
AllocateGeometry(nsDisplayListBuilder * aBuilder)351 nsDisplayItemGeometry* nsDisplayButtonForeground::AllocateGeometry(
352     nsDisplayListBuilder* aBuilder) {
353   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
354 }
355 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const356 void nsDisplayButtonForeground::ComputeInvalidationRegion(
357     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
358     nsRegion* aInvalidRegion) const {
359   auto geometry =
360       static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
361 
362   if (aBuilder->ShouldSyncDecodeImages() &&
363       geometry->ShouldInvalidateToSyncDecodeImages()) {
364     bool snap;
365     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
366   }
367 
368   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
369 }
370 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)371 void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
372                                       gfxContext* aCtx) {
373   nsPresContext* presContext = mFrame->PresContext();
374   const nsStyleDisplay* disp = mFrame->StyleDisplay();
375   if (!mFrame->IsThemed(disp) ||
376       !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
377     nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
378 
379     // Draw the -moz-focus-inner border
380     ImgDrawResult result = mBFR->PaintInnerFocusBorder(aBuilder, presContext,
381                                                        *aCtx, mVisibleRect, r);
382 
383     nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
384   }
385 }
386 
BuildLayer(nsDisplayListBuilder * aBuilder,LayerManager * aManager,const ContainerLayerParameters & aContainerParameters)387 already_AddRefed<mozilla::layers::Layer> nsDisplayButtonForeground::BuildLayer(
388     nsDisplayListBuilder* aBuilder, LayerManager* aManager,
389     const ContainerLayerParameters& aContainerParameters) {
390   return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
391 }
392 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::WebRenderLayerManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)393 bool nsDisplayButtonForeground::CreateWebRenderCommands(
394     mozilla::wr::DisplayListBuilder& aBuilder,
395     mozilla::wr::IpcResourceUpdateQueue& aResources,
396     const StackingContextHelper& aSc,
397     mozilla::layers::WebRenderLayerManager* aManager,
398     nsDisplayListBuilder* aDisplayListBuilder) {
399   Maybe<nsCSSBorderRenderer> br;
400   bool borderIsEmpty = false;
401   nsPresContext* presContext = mFrame->PresContext();
402   const nsStyleDisplay* disp = mFrame->StyleDisplay();
403   if (!mFrame->IsThemed(disp) ||
404       !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
405     nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
406     br = mBFR->CreateInnerFocusBorderRenderer(aDisplayListBuilder, presContext,
407                                               nullptr, mVisibleRect, r,
408                                               &borderIsEmpty);
409   }
410 
411   if (!br) {
412     return borderIsEmpty;
413   }
414 
415   br->CreateWebRenderCommands(this, aBuilder, aResources, aSc);
416   return true;
417 }
418 
DisplayButton(nsDisplayListBuilder * aBuilder,nsDisplayList * aBackground,nsDisplayList * aForeground)419 nsresult nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
420                                               nsDisplayList* aBackground,
421                                               nsDisplayList* aForeground) {
422   if (mFrame->StyleEffects()->mBoxShadow) {
423     aBackground->AppendToTop(
424         MakeDisplayItem<nsDisplayButtonBoxShadowOuter>(aBuilder, this));
425   }
426 
427   nsRect buttonRect = mFrame->GetRectRelativeToSelf();
428 
429   nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, mFrame,
430                                                        buttonRect, aBackground);
431 
432   aBackground->AppendToTop(
433       MakeDisplayItem<nsDisplayButtonBorder>(aBuilder, this));
434 
435   // Only display focus rings if we actually have them. Since at most one
436   // button would normally display a focus ring, most buttons won't have them.
437   if (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder()) {
438     aForeground->AppendToTop(
439         MakeDisplayItem<nsDisplayButtonForeground>(aBuilder, this));
440   }
441   return NS_OK;
442 }
443 
GetButtonInnerFocusRect(const nsRect & aRect,nsRect & aResult)444 void nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect,
445                                                     nsRect& aResult) {
446   aResult = aRect;
447   aResult.Deflate(mFrame->GetUsedBorderAndPadding());
448 
449   nsMargin innerFocusPadding(0, 0, 0, 0);
450   if (mInnerFocusStyle) {
451     mInnerFocusStyle->StylePadding()->GetPadding(innerFocusPadding);
452   }
453   aResult.Inflate(innerFocusPadding);
454 }
455 
PaintInnerFocusBorder(nsDisplayListBuilder * aBuilder,nsPresContext * aPresContext,gfxContext & aRenderingContext,const nsRect & aDirtyRect,const nsRect & aRect)456 ImgDrawResult nsButtonFrameRenderer::PaintInnerFocusBorder(
457     nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext,
458     gfxContext& aRenderingContext, const nsRect& aDirtyRect,
459     const nsRect& aRect) {
460   // we draw the -moz-focus-inner border just inside the button's
461   // normal border and padding, to match Windows themes.
462 
463   nsRect rect;
464 
465   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
466                                ? PaintBorderFlags::SYNC_DECODE_IMAGES
467                                : PaintBorderFlags();
468 
469   ImgDrawResult result = ImgDrawResult::SUCCESS;
470 
471   if (mInnerFocusStyle) {
472     GetButtonInnerFocusRect(aRect, rect);
473 
474     result &=
475         nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
476                                     aDirtyRect, rect, mInnerFocusStyle, flags);
477   }
478 
479   return result;
480 }
481 
482 Maybe<nsCSSBorderRenderer>
CreateInnerFocusBorderRenderer(nsDisplayListBuilder * aBuilder,nsPresContext * aPresContext,gfxContext * aRenderingContext,const nsRect & aDirtyRect,const nsRect & aRect,bool * aBorderIsEmpty)483 nsButtonFrameRenderer::CreateInnerFocusBorderRenderer(
484     nsDisplayListBuilder* aBuilder, nsPresContext* aPresContext,
485     gfxContext* aRenderingContext, const nsRect& aDirtyRect,
486     const nsRect& aRect, bool* aBorderIsEmpty) {
487   if (mInnerFocusStyle) {
488     nsRect rect;
489     GetButtonInnerFocusRect(aRect, rect);
490 
491     gfx::DrawTarget* dt =
492         aRenderingContext ? aRenderingContext->GetDrawTarget() : nullptr;
493     return nsCSSRendering::CreateBorderRenderer(
494         aPresContext, dt, mFrame, aDirtyRect, rect, mInnerFocusStyle,
495         aBorderIsEmpty);
496   }
497 
498   return Nothing();
499 }
500 
PaintBorder(nsDisplayListBuilder * aBuilder,nsPresContext * aPresContext,gfxContext & aRenderingContext,const nsRect & aDirtyRect,const nsRect & aRect)501 ImgDrawResult nsButtonFrameRenderer::PaintBorder(nsDisplayListBuilder* aBuilder,
502                                                  nsPresContext* aPresContext,
503                                                  gfxContext& aRenderingContext,
504                                                  const nsRect& aDirtyRect,
505                                                  const nsRect& aRect) {
506   // get the button rect this is inside the focus and outline rects
507   nsRect buttonRect = aRect;
508   nsStyleContext* context = mFrame->StyleContext();
509 
510   PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
511                                      ? PaintBorderFlags::SYNC_DECODE_IMAGES
512                                      : PaintBorderFlags();
513 
514   nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, mFrame,
515                                       buttonRect);
516 
517   ImgDrawResult result =
518       nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
519                                   aDirtyRect, buttonRect, context, borderFlags);
520 
521   return result;
522 }
523 
524 /**
525  * Call this when styles change
526  */
ReResolveStyles(nsPresContext * aPresContext)527 void nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) {
528   // get all the styles
529   nsStyleContext* context = mFrame->StyleContext();
530   StyleSetHandle styleSet = aPresContext->StyleSet();
531 
532 #ifdef DEBUG
533   if (mInnerFocusStyle) {
534     mInnerFocusStyle->FrameRelease();
535   }
536 #endif
537 
538   // get styles assigned to -moz-inner-focus (ie dotted border on Windows)
539   mInnerFocusStyle = styleSet->ProbePseudoElementStyle(
540       mFrame->GetContent()->AsElement(), CSSPseudoElementType::mozFocusInner,
541       context);
542 
543 #ifdef DEBUG
544   if (mInnerFocusStyle) {
545     mInnerFocusStyle->FrameAddRef();
546   }
547 #endif
548 }
549 
GetStyleContext(int32_t aIndex) const550 nsStyleContext* nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const {
551   switch (aIndex) {
552     case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
553       return mInnerFocusStyle;
554     default:
555       return nullptr;
556   }
557 }
558 
SetStyleContext(int32_t aIndex,nsStyleContext * aStyleContext)559 void nsButtonFrameRenderer::SetStyleContext(int32_t aIndex,
560                                             nsStyleContext* aStyleContext) {
561   switch (aIndex) {
562     case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
563 #ifdef DEBUG
564       if (mInnerFocusStyle) {
565         mInnerFocusStyle->FrameRelease();
566       }
567 #endif
568       mInnerFocusStyle = aStyleContext;
569       break;
570   }
571 #ifdef DEBUG
572   aStyleContext->FrameAddRef();
573 #endif
574 }
575