Coverage Report

Created: 2018-09-25 14:53

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