nsButtonFrameRenderer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsButtonFrameRenderer.h"
  6. #include "nsCSSRendering.h"
  7. #include "nsPresContext.h"
  8. #include "nsGkAtoms.h"
  9. #include "nsCSSPseudoElements.h"
  10. #include "nsNameSpaceManager.h"
  11. #include "mozilla/StyleSetHandle.h"
  12. #include "mozilla/StyleSetHandleInlines.h"
  13. #include "nsDisplayList.h"
  14. #include "nsITheme.h"
  15. #include "nsFrame.h"
  16. #include "mozilla/EventStates.h"
  17. #include "mozilla/dom/Element.h"
  18. #define ACTIVE "active"
  19. #define HOVER "hover"
  20. #define FOCUS "focus"
  21. using namespace mozilla;
  22. using namespace mozilla::image;
  23. nsButtonFrameRenderer::nsButtonFrameRenderer()
  24. {
  25. MOZ_COUNT_CTOR(nsButtonFrameRenderer);
  26. }
  27. nsButtonFrameRenderer::~nsButtonFrameRenderer()
  28. {
  29. MOZ_COUNT_DTOR(nsButtonFrameRenderer);
  30. #ifdef DEBUG
  31. if (mInnerFocusStyle) {
  32. mInnerFocusStyle->FrameRelease();
  33. }
  34. if (mOuterFocusStyle) {
  35. mOuterFocusStyle->FrameRelease();
  36. }
  37. #endif
  38. }
  39. void
  40. nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext)
  41. {
  42. mFrame = aFrame;
  43. ReResolveStyles(aPresContext);
  44. }
  45. nsIFrame*
  46. nsButtonFrameRenderer::GetFrame()
  47. {
  48. return mFrame;
  49. }
  50. void
  51. nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify)
  52. {
  53. if (aDisabled)
  54. mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
  55. notify);
  56. else
  57. mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify);
  58. }
  59. bool
  60. nsButtonFrameRenderer::isDisabled()
  61. {
  62. return mFrame->GetContent()->AsElement()->
  63. State().HasState(NS_EVENT_STATE_DISABLED);
  64. }
  65. class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
  66. public:
  67. nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder,
  68. nsButtonFrameRenderer* aRenderer)
  69. : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
  70. MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
  71. }
  72. #ifdef NS_BUILD_REFCNT_LOGGING
  73. virtual ~nsDisplayButtonBoxShadowOuter() {
  74. MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
  75. }
  76. #endif
  77. virtual void Paint(nsDisplayListBuilder* aBuilder,
  78. nsRenderingContext* aCtx) override;
  79. virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
  80. bool* aSnap) override;
  81. NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER)
  82. private:
  83. nsButtonFrameRenderer* mBFR;
  84. };
  85. nsRect
  86. nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
  87. *aSnap = false;
  88. return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  89. }
  90. void
  91. nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
  92. nsRenderingContext* aCtx) {
  93. nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize());
  94. nsRect buttonRect;
  95. mBFR->GetButtonRect(frameRect, buttonRect);
  96. nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
  97. buttonRect, mVisibleRect);
  98. }
  99. class nsDisplayButtonBorder : public nsDisplayItem {
  100. public:
  101. nsDisplayButtonBorder(nsDisplayListBuilder* aBuilder,
  102. nsButtonFrameRenderer* aRenderer)
  103. : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
  104. MOZ_COUNT_CTOR(nsDisplayButtonBorder);
  105. }
  106. #ifdef NS_BUILD_REFCNT_LOGGING
  107. virtual ~nsDisplayButtonBorder() {
  108. MOZ_COUNT_DTOR(nsDisplayButtonBorder);
  109. }
  110. #endif
  111. virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  112. HitTestState* aState,
  113. nsTArray<nsIFrame*> *aOutFrames) override {
  114. aOutFrames->AppendElement(mFrame);
  115. }
  116. virtual void Paint(nsDisplayListBuilder* aBuilder,
  117. nsRenderingContext* aCtx) override;
  118. virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
  119. bool* aSnap) override;
  120. virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
  121. virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  122. const nsDisplayItemGeometry* aGeometry,
  123. nsRegion *aInvalidRegion) override;
  124. NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
  125. private:
  126. nsButtonFrameRenderer* mBFR;
  127. };
  128. nsDisplayItemGeometry*
  129. nsDisplayButtonBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  130. {
  131. return new nsDisplayItemGenericImageGeometry(this, aBuilder);
  132. }
  133. void
  134. nsDisplayButtonBorder::ComputeInvalidationRegion(
  135. nsDisplayListBuilder* aBuilder,
  136. const nsDisplayItemGeometry* aGeometry,
  137. nsRegion *aInvalidRegion)
  138. {
  139. auto geometry =
  140. static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
  141. if (aBuilder->ShouldSyncDecodeImages() &&
  142. geometry->ShouldInvalidateToSyncDecodeImages()) {
  143. bool snap;
  144. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  145. }
  146. nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  147. }
  148. void nsDisplayButtonBorder::Paint(nsDisplayListBuilder* aBuilder,
  149. nsRenderingContext* aCtx)
  150. {
  151. NS_ASSERTION(mFrame, "No frame?");
  152. nsPresContext* pc = mFrame->PresContext();
  153. nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
  154. // draw the border and background inside the focus and outline borders
  155. DrawResult result =
  156. mBFR->PaintBorder(aBuilder, pc, *aCtx, mVisibleRect, r);
  157. nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
  158. }
  159. nsRect
  160. nsDisplayButtonBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
  161. *aSnap = false;
  162. return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize())
  163. : mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
  164. }
  165. class nsDisplayButtonForeground : public nsDisplayItem {
  166. public:
  167. nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder,
  168. nsButtonFrameRenderer* aRenderer)
  169. : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) {
  170. MOZ_COUNT_CTOR(nsDisplayButtonForeground);
  171. }
  172. #ifdef NS_BUILD_REFCNT_LOGGING
  173. virtual ~nsDisplayButtonForeground() {
  174. MOZ_COUNT_DTOR(nsDisplayButtonForeground);
  175. }
  176. #endif
  177. nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
  178. void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  179. const nsDisplayItemGeometry* aGeometry,
  180. nsRegion *aInvalidRegion) override;
  181. virtual void Paint(nsDisplayListBuilder* aBuilder,
  182. nsRenderingContext* aCtx) override;
  183. NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
  184. private:
  185. nsButtonFrameRenderer* mBFR;
  186. };
  187. nsDisplayItemGeometry*
  188. nsDisplayButtonForeground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  189. {
  190. return new nsDisplayItemGenericImageGeometry(this, aBuilder);
  191. }
  192. void
  193. nsDisplayButtonForeground::ComputeInvalidationRegion(
  194. nsDisplayListBuilder* aBuilder,
  195. const nsDisplayItemGeometry* aGeometry,
  196. nsRegion* aInvalidRegion)
  197. {
  198. auto geometry =
  199. static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
  200. if (aBuilder->ShouldSyncDecodeImages() &&
  201. geometry->ShouldInvalidateToSyncDecodeImages()) {
  202. bool snap;
  203. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  204. }
  205. nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  206. }
  207. void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
  208. nsRenderingContext* aCtx)
  209. {
  210. nsPresContext *presContext = mFrame->PresContext();
  211. const nsStyleDisplay *disp = mFrame->StyleDisplay();
  212. if (!mFrame->IsThemed(disp) ||
  213. !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
  214. nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
  215. // Draw the focus and outline borders.
  216. DrawResult result =
  217. mBFR->PaintOutlineAndFocusBorders(aBuilder, presContext, *aCtx,
  218. mVisibleRect, r);
  219. nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
  220. }
  221. }
  222. nsresult
  223. nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
  224. nsDisplayList* aBackground,
  225. nsDisplayList* aForeground)
  226. {
  227. if (mFrame->StyleEffects()->mBoxShadow) {
  228. aBackground->AppendNewToTop(new (aBuilder)
  229. nsDisplayButtonBoxShadowOuter(aBuilder, this));
  230. }
  231. nsRect buttonRect =
  232. mFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mFrame);
  233. nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
  234. aBuilder, mFrame, buttonRect, aBackground);
  235. aBackground->AppendNewToTop(new (aBuilder)
  236. nsDisplayButtonBorder(aBuilder, this));
  237. // Only display focus rings if we actually have them. Since at most one
  238. // button would normally display a focus ring, most buttons won't have them.
  239. if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) ||
  240. (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) {
  241. aForeground->AppendNewToTop(new (aBuilder)
  242. nsDisplayButtonForeground(aBuilder, this));
  243. }
  244. return NS_OK;
  245. }
  246. DrawResult
  247. nsButtonFrameRenderer::PaintOutlineAndFocusBorders(
  248. nsDisplayListBuilder* aBuilder,
  249. nsPresContext* aPresContext,
  250. nsRenderingContext& aRenderingContext,
  251. const nsRect& aDirtyRect,
  252. const nsRect& aRect)
  253. {
  254. // once we have all that we'll draw the focus if we have it. We will
  255. // need to draw 2 focuses, the inner and the outer. This is so we
  256. // can do any kind of look and feel. Some buttons have focus on the
  257. // outside like mac and motif. While others like windows have it
  258. // inside (dotted line). Usually only one will be specifed. But I
  259. // guess you could have both if you wanted to.
  260. nsRect rect;
  261. PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
  262. ? PaintBorderFlags::SYNC_DECODE_IMAGES
  263. : PaintBorderFlags();
  264. DrawResult result = DrawResult::SUCCESS;
  265. if (mOuterFocusStyle) {
  266. // ---------- paint the outer focus border -------------
  267. GetButtonOuterFocusRect(aRect, rect);
  268. result &=
  269. nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
  270. aDirtyRect, rect, mOuterFocusStyle, flags);
  271. }
  272. if (mInnerFocusStyle) {
  273. // ---------- paint the inner focus border -------------
  274. GetButtonInnerFocusRect(aRect, rect);
  275. result &=
  276. nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
  277. aDirtyRect, rect, mInnerFocusStyle, flags);
  278. }
  279. return result;
  280. }
  281. DrawResult
  282. nsButtonFrameRenderer::PaintBorder(
  283. nsDisplayListBuilder* aBuilder,
  284. nsPresContext* aPresContext,
  285. nsRenderingContext& aRenderingContext,
  286. const nsRect& aDirtyRect,
  287. const nsRect& aRect)
  288. {
  289. // get the button rect this is inside the focus and outline rects
  290. nsRect buttonRect;
  291. GetButtonRect(aRect, buttonRect);
  292. nsStyleContext* context = mFrame->StyleContext();
  293. PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
  294. ? PaintBorderFlags::SYNC_DECODE_IMAGES
  295. : PaintBorderFlags();
  296. nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
  297. mFrame, buttonRect);
  298. DrawResult result =
  299. nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
  300. aDirtyRect, buttonRect, context, borderFlags);
  301. return result;
  302. }
  303. void
  304. nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
  305. {
  306. focusRect = aRect;
  307. }
  308. void
  309. nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r)
  310. {
  311. r = aRect;
  312. r.Deflate(GetButtonOuterFocusBorderAndPadding());
  313. }
  314. void
  315. nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect)
  316. {
  317. GetButtonRect(aRect, focusRect);
  318. focusRect.Deflate(GetButtonBorderAndPadding());
  319. focusRect.Deflate(GetButtonInnerFocusMargin());
  320. }
  321. nsMargin
  322. nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding()
  323. {
  324. nsMargin result(0,0,0,0);
  325. if (mOuterFocusStyle) {
  326. mOuterFocusStyle->StylePadding()->GetPadding(result);
  327. result += mOuterFocusStyle->StyleBorder()->GetComputedBorder();
  328. }
  329. return result;
  330. }
  331. nsMargin
  332. nsButtonFrameRenderer::GetButtonBorderAndPadding()
  333. {
  334. return mFrame->GetUsedBorderAndPadding();
  335. }
  336. /**
  337. * Gets the size of the buttons border this is the union of the normal and disabled borders.
  338. */
  339. nsMargin
  340. nsButtonFrameRenderer::GetButtonInnerFocusMargin()
  341. {
  342. nsMargin innerFocusMargin(0,0,0,0);
  343. if (mInnerFocusStyle) {
  344. const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin();
  345. margin->GetMargin(innerFocusMargin);
  346. }
  347. return innerFocusMargin;
  348. }
  349. nsMargin
  350. nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding()
  351. {
  352. nsMargin result(0,0,0,0);
  353. if (mInnerFocusStyle) {
  354. mInnerFocusStyle->StylePadding()->GetPadding(result);
  355. result += mInnerFocusStyle->StyleBorder()->GetComputedBorder();
  356. }
  357. return result;
  358. }
  359. // gets all the focus borders and padding that will be added to the regular border
  360. nsMargin
  361. nsButtonFrameRenderer::GetAddedButtonBorderAndPadding()
  362. {
  363. return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding();
  364. }
  365. /**
  366. * Call this when styles change
  367. */
  368. void
  369. nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext)
  370. {
  371. // get all the styles
  372. nsStyleContext* context = mFrame->StyleContext();
  373. StyleSetHandle styleSet = aPresContext->StyleSet();
  374. #ifdef DEBUG
  375. if (mInnerFocusStyle) {
  376. mInnerFocusStyle->FrameRelease();
  377. }
  378. if (mOuterFocusStyle) {
  379. mOuterFocusStyle->FrameRelease();
  380. }
  381. #endif
  382. // style for the inner such as a dotted line (Windows)
  383. mInnerFocusStyle =
  384. styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
  385. CSSPseudoElementType::mozFocusInner,
  386. context);
  387. // style for outer focus like a ridged border (MAC).
  388. mOuterFocusStyle =
  389. styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(),
  390. CSSPseudoElementType::mozFocusOuter,
  391. context);
  392. #ifdef DEBUG
  393. if (mInnerFocusStyle) {
  394. mInnerFocusStyle->FrameAddRef();
  395. }
  396. if (mOuterFocusStyle) {
  397. mOuterFocusStyle->FrameAddRef();
  398. }
  399. #endif
  400. }
  401. nsStyleContext*
  402. nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const
  403. {
  404. switch (aIndex) {
  405. case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
  406. return mInnerFocusStyle;
  407. case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
  408. return mOuterFocusStyle;
  409. default:
  410. return nullptr;
  411. }
  412. }
  413. void
  414. nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext)
  415. {
  416. switch (aIndex) {
  417. case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX:
  418. #ifdef DEBUG
  419. if (mInnerFocusStyle) {
  420. mInnerFocusStyle->FrameRelease();
  421. }
  422. #endif
  423. mInnerFocusStyle = aStyleContext;
  424. break;
  425. case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX:
  426. #ifdef DEBUG
  427. if (mOuterFocusStyle) {
  428. mOuterFocusStyle->FrameRelease();
  429. }
  430. #endif
  431. mOuterFocusStyle = aStyleContext;
  432. break;
  433. }
  434. #ifdef DEBUG
  435. aStyleContext->FrameAddRef();
  436. #endif
  437. }