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