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