nsFieldSetFrame.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  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 "nsFieldSetFrame.h"
  6. #include <algorithm>
  7. #include "mozilla/gfx/2D.h"
  8. #include "mozilla/Likely.h"
  9. #include "mozilla/Maybe.h"
  10. #include "nsCSSAnonBoxes.h"
  11. #include "nsCSSRendering.h"
  12. #include "nsDisplayList.h"
  13. #include "nsGkAtoms.h"
  14. #include "nsIFrameInlines.h"
  15. #include "nsLayoutUtils.h"
  16. #include "nsLegendFrame.h"
  17. #include "nsRenderingContext.h"
  18. #include "nsStyleConsts.h"
  19. using namespace mozilla;
  20. using namespace mozilla::gfx;
  21. using namespace mozilla::layout;
  22. nsContainerFrame*
  23. NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  24. {
  25. return new (aPresShell) nsFieldSetFrame(aContext);
  26. }
  27. NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame)
  28. nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext)
  29. : nsContainerFrame(aContext)
  30. , mLegendRect(GetWritingMode())
  31. {
  32. mLegendSpace = 0;
  33. }
  34. nsIAtom*
  35. nsFieldSetFrame::GetType() const
  36. {
  37. return nsGkAtoms::fieldSetFrame;
  38. }
  39. nsRect
  40. nsFieldSetFrame::VisualBorderRectRelativeToSelf() const
  41. {
  42. WritingMode wm = GetWritingMode();
  43. css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
  44. nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide);
  45. LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
  46. nsSize containerSize = r.Size(wm).GetPhysicalSize(wm);
  47. if (legendBorder < mLegendRect.BSize(wm)) {
  48. nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2;
  49. r.BStart(wm) += off;
  50. r.BSize(wm) -= off;
  51. }
  52. return r.GetPhysicalRect(wm, containerSize);
  53. }
  54. nsIFrame*
  55. nsFieldSetFrame::GetInner() const
  56. {
  57. nsIFrame* last = mFrames.LastChild();
  58. if (last &&
  59. last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) {
  60. return last;
  61. }
  62. MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
  63. return nullptr;
  64. }
  65. nsIFrame*
  66. nsFieldSetFrame::GetLegend() const
  67. {
  68. if (mFrames.FirstChild() == GetInner()) {
  69. MOZ_ASSERT(mFrames.LastChild() == mFrames.FirstChild());
  70. return nullptr;
  71. }
  72. MOZ_ASSERT(mFrames.FirstChild() &&
  73. mFrames.FirstChild()->GetContentInsertionFrame()->GetType() ==
  74. nsGkAtoms::legendFrame);
  75. return mFrames.FirstChild();
  76. }
  77. class nsDisplayFieldSetBorderBackground : public nsDisplayItem {
  78. public:
  79. nsDisplayFieldSetBorderBackground(nsDisplayListBuilder* aBuilder,
  80. nsFieldSetFrame* aFrame)
  81. : nsDisplayItem(aBuilder, aFrame) {
  82. MOZ_COUNT_CTOR(nsDisplayFieldSetBorderBackground);
  83. }
  84. #ifdef NS_BUILD_REFCNT_LOGGING
  85. virtual ~nsDisplayFieldSetBorderBackground() {
  86. MOZ_COUNT_DTOR(nsDisplayFieldSetBorderBackground);
  87. }
  88. #endif
  89. virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  90. HitTestState* aState,
  91. nsTArray<nsIFrame*> *aOutFrames) override;
  92. virtual void Paint(nsDisplayListBuilder* aBuilder,
  93. nsRenderingContext* aCtx) override;
  94. virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
  95. virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  96. const nsDisplayItemGeometry* aGeometry,
  97. nsRegion *aInvalidRegion) override;
  98. NS_DISPLAY_DECL_NAME("FieldSetBorderBackground", TYPE_FIELDSET_BORDER_BACKGROUND)
  99. };
  100. void nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  101. HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
  102. {
  103. // aPt is guaranteed to be in this item's bounds. We do the hit test based on the
  104. // frame bounds even though our background doesn't cover the whole frame.
  105. // It's not clear whether this is correct.
  106. aOutFrames->AppendElement(mFrame);
  107. }
  108. void
  109. nsDisplayFieldSetBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
  110. nsRenderingContext* aCtx)
  111. {
  112. image::DrawResult result = static_cast<nsFieldSetFrame*>(mFrame)->
  113. PaintBorder(aBuilder, *aCtx, ToReferenceFrame(), mVisibleRect);
  114. nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
  115. }
  116. nsDisplayItemGeometry*
  117. nsDisplayFieldSetBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  118. {
  119. return new nsDisplayItemGenericImageGeometry(this, aBuilder);
  120. }
  121. void
  122. nsDisplayFieldSetBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  123. const nsDisplayItemGeometry* aGeometry,
  124. nsRegion *aInvalidRegion)
  125. {
  126. auto geometry =
  127. static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
  128. if (aBuilder->ShouldSyncDecodeImages() &&
  129. geometry->ShouldInvalidateToSyncDecodeImages()) {
  130. bool snap;
  131. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  132. }
  133. nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  134. }
  135. void
  136. nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  137. const nsDisplayListSet& aLists) {
  138. // Paint our background and border in a special way.
  139. // REVIEW: We don't really need to check frame emptiness here; if it's empty,
  140. // the background/border display item won't do anything, and if it isn't empty,
  141. // we need to paint the outline
  142. if (!(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) &&
  143. IsVisibleForPainting(aBuilder)) {
  144. if (StyleEffects()->mBoxShadow) {
  145. aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
  146. nsDisplayBoxShadowOuter(aBuilder, this));
  147. }
  148. const nsRect rect =
  149. VisualBorderRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
  150. nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
  151. aBuilder, this, rect, aLists.BorderBackground(),
  152. /* aAllowWillPaintBorderOptimization = */ false);
  153. aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
  154. nsDisplayFieldSetBorderBackground(aBuilder, this));
  155. DisplayOutlineUnconditional(aBuilder, aLists);
  156. DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame");
  157. }
  158. if (GetPrevInFlow()) {
  159. DisplayOverflowContainers(aBuilder, aLists);
  160. }
  161. nsDisplayListCollection contentDisplayItems(aBuilder);
  162. if (nsIFrame* inner = GetInner()) {
  163. // Collect the inner frame's display items into their own collection.
  164. // We need to be calling BuildDisplayList on it before the legend in
  165. // case it contains out-of-flow frames whose placeholders are in the
  166. // legend. However, we want the inner frame's display items to be
  167. // after the legend's display items in z-order, so we need to save them
  168. // and append them later.
  169. BuildDisplayListForChild(aBuilder, inner, contentDisplayItems);
  170. }
  171. if (nsIFrame* legend = GetLegend()) {
  172. // The legend's background goes on our BlockBorderBackgrounds list because
  173. // it's a block child.
  174. nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
  175. BuildDisplayListForChild(aBuilder, legend, set);
  176. }
  177. // Put the inner frame's display items on the master list. Note that this
  178. // moves its border/background display items to our BorderBackground() list,
  179. // which isn't really correct, but it's OK because the inner frame is
  180. // anonymous and can't have its own border and background.
  181. contentDisplayItems.MoveTo(aLists);
  182. }
  183. image::DrawResult
  184. nsFieldSetFrame::PaintBorder(
  185. nsDisplayListBuilder* aBuilder,
  186. nsRenderingContext& aRenderingContext,
  187. nsPoint aPt,
  188. const nsRect& aDirtyRect)
  189. {
  190. // if the border is smaller than the legend. Move the border down
  191. // to be centered on the legend.
  192. // FIXME: This means border-radius clamping is incorrect; we should
  193. // override nsIFrame::GetBorderRadii.
  194. WritingMode wm = GetWritingMode();
  195. nsRect rect = VisualBorderRectRelativeToSelf();
  196. nscoord off = wm.IsVertical() ? rect.x : rect.y;
  197. rect += aPt;
  198. nsPresContext* presContext = PresContext();
  199. PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
  200. ? PaintBorderFlags::SYNC_DECODE_IMAGES
  201. : PaintBorderFlags();
  202. DrawResult result = DrawResult::SUCCESS;
  203. nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
  204. this, rect);
  205. if (nsIFrame* legend = GetLegend()) {
  206. css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
  207. nscoord legendBorderWidth =
  208. StyleBorder()->GetComputedBorderWidth(legendSide);
  209. // Use the rect of the legend frame, not mLegendRect, so we draw our
  210. // border under the legend's inline-start and -end margins.
  211. LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size());
  212. // Compute clipRect using logical coordinates, so that the legend space
  213. // will be clipped out of the appropriate physical side depending on mode.
  214. LogicalRect clipRect = LogicalRect(wm, rect, rect.Size());
  215. DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
  216. gfxContext* gfx = aRenderingContext.ThebesContext();
  217. int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
  218. // draw inline-start portion of the block-start side of the border
  219. clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm);
  220. clipRect.BSize(wm) = legendBorderWidth;
  221. gfx->Save();
  222. gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
  223. appUnitsPerDevPixel, *drawTarget));
  224. result &=
  225. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  226. aDirtyRect, rect, mStyleContext, borderFlags);
  227. gfx->Restore();
  228. // draw inline-end portion of the block-start side of the border
  229. clipRect = LogicalRect(wm, rect, rect.Size());
  230. clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm);
  231. clipRect.IStart(wm) = legendRect.IEnd(wm);
  232. clipRect.BSize(wm) = legendBorderWidth;
  233. gfx->Save();
  234. gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
  235. appUnitsPerDevPixel, *drawTarget));
  236. result &=
  237. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  238. aDirtyRect, rect, mStyleContext, borderFlags);
  239. gfx->Restore();
  240. // draw remainder of the border (omitting the block-start side)
  241. clipRect = LogicalRect(wm, rect, rect.Size());
  242. clipRect.BStart(wm) += legendBorderWidth;
  243. clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth);
  244. gfx->Save();
  245. gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
  246. appUnitsPerDevPixel, *drawTarget));
  247. result &=
  248. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  249. aDirtyRect, rect, mStyleContext, borderFlags);
  250. gfx->Restore();
  251. } else {
  252. result &=
  253. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  254. aDirtyRect, nsRect(aPt, mRect.Size()),
  255. mStyleContext, borderFlags);
  256. }
  257. return result;
  258. }
  259. nscoord
  260. nsFieldSetFrame::GetIntrinsicISize(nsRenderingContext* aRenderingContext,
  261. nsLayoutUtils::IntrinsicISizeType aType)
  262. {
  263. nscoord legendWidth = 0;
  264. nscoord contentWidth = 0;
  265. if (nsIFrame* legend = GetLegend()) {
  266. legendWidth =
  267. nsLayoutUtils::IntrinsicForContainer(aRenderingContext, legend, aType);
  268. }
  269. if (nsIFrame* inner = GetInner()) {
  270. // Ignore padding on the inner, since the padding will be applied to the
  271. // outer instead, and the padding computed for the inner is wrong
  272. // for percentage padding.
  273. contentWidth =
  274. nsLayoutUtils::IntrinsicForContainer(aRenderingContext, inner, aType,
  275. nsLayoutUtils::IGNORE_PADDING);
  276. }
  277. return std::max(legendWidth, contentWidth);
  278. }
  279. nscoord
  280. nsFieldSetFrame::GetMinISize(nsRenderingContext* aRenderingContext)
  281. {
  282. nscoord result = 0;
  283. DISPLAY_MIN_WIDTH(this, result);
  284. result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::MIN_ISIZE);
  285. return result;
  286. }
  287. nscoord
  288. nsFieldSetFrame::GetPrefISize(nsRenderingContext* aRenderingContext)
  289. {
  290. nscoord result = 0;
  291. DISPLAY_PREF_WIDTH(this, result);
  292. result = GetIntrinsicISize(aRenderingContext, nsLayoutUtils::PREF_ISIZE);
  293. return result;
  294. }
  295. /* virtual */
  296. LogicalSize
  297. nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext,
  298. WritingMode aWM,
  299. const LogicalSize& aCBSize,
  300. nscoord aAvailableISize,
  301. const LogicalSize& aMargin,
  302. const LogicalSize& aBorder,
  303. const LogicalSize& aPadding,
  304. ComputeSizeFlags aFlags)
  305. {
  306. LogicalSize result =
  307. nsContainerFrame::ComputeSize(aRenderingContext, aWM,
  308. aCBSize, aAvailableISize,
  309. aMargin, aBorder, aPadding, aFlags);
  310. // XXX The code below doesn't make sense if the caller's writing mode
  311. // is orthogonal to this frame's. Not sure yet what should happen then;
  312. // for now, just bail out.
  313. if (aWM.IsVertical() != GetWritingMode().IsVertical()) {
  314. return result;
  315. }
  316. // Fieldsets never shrink below their min width.
  317. // If we're a container for font size inflation, then shrink
  318. // wrapping inside of us should not apply font size inflation.
  319. AutoMaybeDisableFontInflation an(this);
  320. nscoord minISize = GetMinISize(aRenderingContext);
  321. if (minISize > result.ISize(aWM)) {
  322. result.ISize(aWM) = minISize;
  323. }
  324. return result;
  325. }
  326. void
  327. nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
  328. ReflowOutput& aDesiredSize,
  329. const ReflowInput& aReflowInput,
  330. nsReflowStatus& aStatus)
  331. {
  332. MarkInReflow();
  333. DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
  334. DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
  335. NS_PRECONDITION(aReflowInput.ComputedISize() != NS_INTRINSICSIZE,
  336. "Should have a precomputed inline-size!");
  337. // Initialize OUT parameter
  338. aStatus = NS_FRAME_COMPLETE;
  339. nsOverflowAreas ocBounds;
  340. nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
  341. if (GetPrevInFlow()) {
  342. ReflowOverflowContainerChildren(aPresContext, aReflowInput, ocBounds, 0,
  343. ocStatus);
  344. }
  345. //------------ Handle Incremental Reflow -----------------
  346. bool reflowInner;
  347. bool reflowLegend;
  348. nsIFrame* legend = GetLegend();
  349. nsIFrame* inner = GetInner();
  350. if (aReflowInput.ShouldReflowAllKids()) {
  351. reflowInner = inner != nullptr;
  352. reflowLegend = legend != nullptr;
  353. } else {
  354. reflowInner = inner && NS_SUBTREE_DIRTY(inner);
  355. reflowLegend = legend && NS_SUBTREE_DIRTY(legend);
  356. }
  357. // We don't allow fieldsets to break vertically. If we did, we'd
  358. // need logic here to push and pull overflow frames.
  359. // Since we're not applying our padding in this frame, we need to add it here
  360. // to compute the available width for our children.
  361. WritingMode wm = GetWritingMode();
  362. WritingMode innerWM = inner ? inner->GetWritingMode() : wm;
  363. WritingMode legendWM = legend ? legend->GetWritingMode() : wm;
  364. LogicalSize innerAvailSize = aReflowInput.ComputedSizeWithPadding(innerWM);
  365. LogicalSize legendAvailSize = aReflowInput.ComputedSizeWithPadding(legendWM);
  366. innerAvailSize.BSize(innerWM) = legendAvailSize.BSize(legendWM) =
  367. NS_UNCONSTRAINEDSIZE;
  368. NS_ASSERTION(!inner ||
  369. nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
  370. inner,
  371. nsLayoutUtils::MIN_ISIZE) <=
  372. innerAvailSize.ISize(innerWM),
  373. "Bogus availSize.ISize; should be bigger");
  374. NS_ASSERTION(!legend ||
  375. nsLayoutUtils::IntrinsicForContainer(aReflowInput.mRenderingContext,
  376. legend,
  377. nsLayoutUtils::MIN_ISIZE) <=
  378. legendAvailSize.ISize(legendWM),
  379. "Bogus availSize.ISize; should be bigger");
  380. // get our border and padding
  381. LogicalMargin border = aReflowInput.ComputedLogicalBorderPadding() -
  382. aReflowInput.ComputedLogicalPadding();
  383. // Figure out how big the legend is if there is one.
  384. // get the legend's margin
  385. LogicalMargin legendMargin(wm);
  386. // reflow the legend only if needed
  387. Maybe<ReflowInput> legendReflowInput;
  388. if (legend) {
  389. legendReflowInput.emplace(aPresContext, aReflowInput, legend,
  390. legendAvailSize);
  391. }
  392. if (reflowLegend) {
  393. ReflowOutput legendDesiredSize(aReflowInput);
  394. // We'll move the legend to its proper place later, so the position
  395. // and containerSize passed here are unimportant.
  396. const nsSize dummyContainerSize;
  397. ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowInput,
  398. wm, LogicalPoint(wm), dummyContainerSize,
  399. NS_FRAME_NO_MOVE_FRAME, aStatus);
  400. #ifdef NOISY_REFLOW
  401. printf(" returned (%d, %d)\n",
  402. legendDesiredSize.Width(), legendDesiredSize.Height());
  403. #endif
  404. // figure out the legend's rectangle
  405. legendMargin = legend->GetLogicalUsedMargin(wm);
  406. mLegendRect =
  407. LogicalRect(wm, 0, 0,
  408. legendDesiredSize.ISize(wm) + legendMargin.IStartEnd(wm),
  409. legendDesiredSize.BSize(wm) + legendMargin.BStartEnd(wm));
  410. nscoord oldSpace = mLegendSpace;
  411. mLegendSpace = 0;
  412. if (mLegendRect.BSize(wm) > border.BStart(wm)) {
  413. // center the border on the legend
  414. mLegendSpace = mLegendRect.BSize(wm) - border.BStart(wm);
  415. } else {
  416. mLegendRect.BStart(wm) =
  417. (border.BStart(wm) - mLegendRect.BSize(wm)) / 2;
  418. }
  419. // if the legend space changes then we need to reflow the
  420. // content area as well.
  421. if (mLegendSpace != oldSpace && inner) {
  422. reflowInner = true;
  423. }
  424. FinishReflowChild(legend, aPresContext, legendDesiredSize,
  425. legendReflowInput.ptr(), wm, LogicalPoint(wm),
  426. dummyContainerSize, NS_FRAME_NO_MOVE_FRAME);
  427. } else if (!legend) {
  428. mLegendRect.SetEmpty();
  429. mLegendSpace = 0;
  430. } else {
  431. // mLegendSpace and mLegendRect haven't changed, but we need
  432. // the used margin when placing the legend.
  433. legendMargin = legend->GetLogicalUsedMargin(wm);
  434. }
  435. // This containerSize is incomplete as yet: it does not include the size
  436. // of the |inner| frame itself.
  437. nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) +
  438. border.Size(wm)).GetPhysicalSize(wm);
  439. // reflow the content frame only if needed
  440. if (reflowInner) {
  441. ReflowInput kidReflowInput(aPresContext, aReflowInput, inner,
  442. innerAvailSize, nullptr,
  443. ReflowInput::CALLER_WILL_INIT);
  444. // Override computed padding, in case it's percentage padding
  445. kidReflowInput.Init(aPresContext, nullptr, nullptr,
  446. &aReflowInput.ComputedPhysicalPadding());
  447. // Our child is "height:100%" but we actually want its height to be reduced
  448. // by the amount of content-height the legend is eating up, unless our
  449. // height is unconstrained (in which case the child's will be too).
  450. if (aReflowInput.ComputedBSize() != NS_UNCONSTRAINEDSIZE) {
  451. kidReflowInput.SetComputedBSize(
  452. std::max(0, aReflowInput.ComputedBSize() - mLegendSpace));
  453. }
  454. if (aReflowInput.ComputedMinBSize() > 0) {
  455. kidReflowInput.ComputedMinBSize() =
  456. std::max(0, aReflowInput.ComputedMinBSize() - mLegendSpace);
  457. }
  458. if (aReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
  459. kidReflowInput.ComputedMaxBSize() =
  460. std::max(0, aReflowInput.ComputedMaxBSize() - mLegendSpace);
  461. }
  462. ReflowOutput kidDesiredSize(kidReflowInput,
  463. aDesiredSize.mFlags);
  464. // Reflow the frame
  465. NS_ASSERTION(kidReflowInput.ComputedPhysicalMargin() == nsMargin(0,0,0,0),
  466. "Margins on anonymous fieldset child not supported!");
  467. LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace);
  468. // We don't know the correct containerSize until we have reflowed |inner|,
  469. // so we use a dummy value for now; FinishReflowChild will fix the position
  470. // if necessary.
  471. const nsSize dummyContainerSize;
  472. ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowInput,
  473. wm, pt, dummyContainerSize, 0, aStatus);
  474. // Update containerSize to account for size of the inner frame, so that
  475. // FinishReflowChild can position it correctly.
  476. containerSize += kidDesiredSize.PhysicalSize();
  477. FinishReflowChild(inner, aPresContext, kidDesiredSize,
  478. &kidReflowInput, wm, pt, containerSize, 0);
  479. NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
  480. } else if (inner) {
  481. // |inner| didn't need to be reflowed but we do need to include its size
  482. // in containerSize.
  483. containerSize += inner->GetSize();
  484. }
  485. LogicalRect contentRect(wm);
  486. if (inner) {
  487. // We don't support margins on inner, so our content rect is just the
  488. // inner's border-box. (We don't really care about container size at this
  489. // point, as we'll figure out the actual positioning later.)
  490. contentRect = inner->GetLogicalRect(wm, containerSize);
  491. }
  492. // Our content rect must fill up the available width
  493. LogicalSize availSize = aReflowInput.ComputedSizeWithPadding(wm);
  494. if (availSize.ISize(wm) > contentRect.ISize(wm)) {
  495. contentRect.ISize(wm) = innerAvailSize.ISize(wm);
  496. }
  497. if (legend) {
  498. // The legend is positioned inline-wards within the inner's content rect
  499. // (so that padding on the fieldset affects the legend position).
  500. LogicalRect innerContentRect = contentRect;
  501. innerContentRect.Deflate(wm, aReflowInput.ComputedLogicalPadding());
  502. // If the inner content rect is larger than the legend, we can align the
  503. // legend.
  504. if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
  505. // NOTE legend @align values are: left/right/center/top/bottom.
  506. // GetLogicalAlign converts left/right to start/end for the given WM.
  507. // @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign
  508. int32_t align = static_cast<nsLegendFrame*>
  509. (legend->GetContentInsertionFrame())->GetLogicalAlign(wm);
  510. switch (align) {
  511. case NS_STYLE_TEXT_ALIGN_END:
  512. mLegendRect.IStart(wm) =
  513. innerContentRect.IEnd(wm) - mLegendRect.ISize(wm);
  514. break;
  515. case NS_STYLE_TEXT_ALIGN_CENTER:
  516. // Note: rounding removed; there doesn't seem to be any need
  517. mLegendRect.IStart(wm) = innerContentRect.IStart(wm) +
  518. (innerContentRect.ISize(wm) - mLegendRect.ISize(wm)) / 2;
  519. break;
  520. case NS_STYLE_TEXT_ALIGN_START:
  521. case NS_STYLE_VERTICAL_ALIGN_TOP:
  522. case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
  523. mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
  524. break;
  525. default:
  526. MOZ_ASSERT_UNREACHABLE("unexpected GetLogicalAlign value");
  527. }
  528. } else {
  529. // otherwise make place for the legend
  530. mLegendRect.IStart(wm) = innerContentRect.IStart(wm);
  531. innerContentRect.ISize(wm) = mLegendRect.ISize(wm);
  532. contentRect.ISize(wm) = mLegendRect.ISize(wm) +
  533. aReflowInput.ComputedLogicalPadding().IStartEnd(wm);
  534. }
  535. // place the legend
  536. LogicalRect actualLegendRect = mLegendRect;
  537. actualLegendRect.Deflate(wm, legendMargin);
  538. LogicalPoint actualLegendPos(actualLegendRect.Origin(wm));
  539. // Note that legend's writing mode may be different from the fieldset's,
  540. // so we need to convert offsets before applying them to it (bug 1134534).
  541. LogicalMargin offsets =
  542. legendReflowInput->ComputedLogicalOffsets().
  543. ConvertTo(wm, legendReflowInput->GetWritingMode());
  544. ReflowInput::ApplyRelativePositioning(legend, wm, offsets,
  545. &actualLegendPos,
  546. containerSize);
  547. legend->SetPosition(wm, actualLegendPos, containerSize);
  548. nsContainerFrame::PositionFrameView(legend);
  549. nsContainerFrame::PositionChildViews(legend);
  550. }
  551. // Return our size and our result.
  552. LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm),
  553. mLegendSpace + border.BStartEnd(wm) +
  554. (inner ? inner->BSize(wm) : 0));
  555. aDesiredSize.SetSize(wm, finalSize);
  556. aDesiredSize.SetOverflowAreasToDesiredBounds();
  557. if (legend) {
  558. ConsiderChildOverflow(aDesiredSize.mOverflowAreas, legend);
  559. }
  560. if (inner) {
  561. ConsiderChildOverflow(aDesiredSize.mOverflowAreas, inner);
  562. }
  563. // Merge overflow container bounds and status.
  564. aDesiredSize.mOverflowAreas.UnionWith(ocBounds);
  565. NS_MergeReflowStatusInto(&aStatus, ocStatus);
  566. FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
  567. InvalidateFrame();
  568. NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
  569. }
  570. #ifdef DEBUG
  571. void
  572. nsFieldSetFrame::SetInitialChildList(ChildListID aListID,
  573. nsFrameList& aChildList)
  574. {
  575. nsContainerFrame::SetInitialChildList(aListID, aChildList);
  576. MOZ_ASSERT(aListID != kPrincipalList || GetInner(),
  577. "Setting principal child list should populate our inner frame");
  578. }
  579. void
  580. nsFieldSetFrame::AppendFrames(ChildListID aListID,
  581. nsFrameList& aFrameList)
  582. {
  583. MOZ_CRASH("nsFieldSetFrame::AppendFrames not supported");
  584. }
  585. void
  586. nsFieldSetFrame::InsertFrames(ChildListID aListID,
  587. nsIFrame* aPrevFrame,
  588. nsFrameList& aFrameList)
  589. {
  590. MOZ_CRASH("nsFieldSetFrame::InsertFrames not supported");
  591. }
  592. void
  593. nsFieldSetFrame::RemoveFrame(ChildListID aListID,
  594. nsIFrame* aOldFrame)
  595. {
  596. MOZ_CRASH("nsFieldSetFrame::RemoveFrame not supported");
  597. }
  598. #endif
  599. #ifdef ACCESSIBILITY
  600. a11y::AccType
  601. nsFieldSetFrame::AccessibleType()
  602. {
  603. return a11y::eHTMLGroupboxType;
  604. }
  605. #endif
  606. nscoord
  607. nsFieldSetFrame::GetLogicalBaseline(WritingMode aWM) const
  608. {
  609. switch (StyleDisplay()->mDisplay) {
  610. case mozilla::StyleDisplay::Grid:
  611. case mozilla::StyleDisplay::InlineGrid:
  612. case mozilla::StyleDisplay::Flex:
  613. case mozilla::StyleDisplay::InlineFlex:
  614. return BaselineBOffset(aWM, BaselineSharingGroup::eFirst,
  615. AlignmentContext::eInline);
  616. default:
  617. return BSize(aWM) - BaselineBOffset(aWM, BaselineSharingGroup::eLast,
  618. AlignmentContext::eInline);
  619. }
  620. }
  621. bool
  622. nsFieldSetFrame::GetVerticalAlignBaseline(WritingMode aWM,
  623. nscoord* aBaseline) const
  624. {
  625. nsIFrame* inner = GetInner();
  626. MOZ_ASSERT(!inner->GetWritingMode().IsOrthogonalTo(aWM));
  627. if (!inner->GetVerticalAlignBaseline(aWM, aBaseline)) {
  628. return false;
  629. }
  630. nscoord innerBStart = inner->BStart(aWM, GetSize());
  631. *aBaseline += innerBStart;
  632. return true;
  633. }
  634. bool
  635. nsFieldSetFrame::GetNaturalBaselineBOffset(WritingMode aWM,
  636. BaselineSharingGroup aBaselineGroup,
  637. nscoord* aBaseline) const
  638. {
  639. nsIFrame* inner = GetInner();
  640. MOZ_ASSERT(!inner->GetWritingMode().IsOrthogonalTo(aWM));
  641. if (!inner->GetNaturalBaselineBOffset(aWM, aBaselineGroup, aBaseline)) {
  642. return false;
  643. }
  644. nscoord innerBStart = inner->BStart(aWM, GetSize());
  645. if (aBaselineGroup == BaselineSharingGroup::eFirst) {
  646. *aBaseline += innerBStart;
  647. } else {
  648. *aBaseline += BSize(aWM) - (innerBStart + inner->BSize(aWM));
  649. }
  650. return true;
  651. }