1/*
2 * This file is part of the render object implementation for KHTML.
3 *
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2003 Apple Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "RenderDeprecatedFlexibleBox.h"
27
28#include "FontCascade.h"
29#include "LayoutRepainter.h"
30#include "RenderLayer.h"
31#include "RenderLayoutState.h"
32#include "RenderView.h"
33#include <wtf/IsoMallocInlines.h>
34#include <wtf/StdLibExtras.h>
35#include <wtf/unicode/CharacterNames.h>
36
37namespace WebCore {
38
39WTF_MAKE_ISO_ALLOCATED_IMPL(RenderDeprecatedFlexibleBox);
40
41class FlexBoxIterator {
42public:
43 FlexBoxIterator(RenderDeprecatedFlexibleBox* parent)
44 : m_box(parent)
45 , m_largestOrdinal(1)
46 {
47 if (m_box->style().boxOrient() == BoxOrient::Horizontal && !m_box->style().isLeftToRightDirection())
48 m_forward = m_box->style().boxDirection() != BoxDirection::Normal;
49 else
50 m_forward = m_box->style().boxDirection() == BoxDirection::Normal;
51 if (!m_forward) {
52 // No choice, since we're going backwards, we have to find out the highest ordinal up front.
53 RenderBox* child = m_box->firstChildBox();
54 while (child) {
55 if (child->style().boxOrdinalGroup() > m_largestOrdinal)
56 m_largestOrdinal = child->style().boxOrdinalGroup();
57 child = child->nextSiblingBox();
58 }
59 }
60
61 reset();
62 }
63
64 void reset()
65 {
66 m_currentChild = nullptr;
67 m_ordinalIteration = std::numeric_limits<unsigned>::max();
68 }
69
70 RenderBox* first()
71 {
72 reset();
73 return next();
74 }
75
76 RenderBox* next()
77 {
78 do {
79 if (!m_currentChild) {
80 ++m_ordinalIteration;
81
82 if (!m_ordinalIteration)
83 m_currentOrdinal = m_forward ? 1 : m_largestOrdinal;
84 else {
85 if (m_ordinalIteration > m_ordinalValues.size())
86 return nullptr;
87
88 // Only copy+sort the values once per layout even if the iterator is reset.
89 if (static_cast<size_t>(m_ordinalValues.size()) != m_sortedOrdinalValues.size()) {
90 m_sortedOrdinalValues = copyToVector(m_ordinalValues);
91 std::sort(m_sortedOrdinalValues.begin(), m_sortedOrdinalValues.end());
92 }
93 m_currentOrdinal = m_forward ? m_sortedOrdinalValues[m_ordinalIteration - 1] : m_sortedOrdinalValues[m_sortedOrdinalValues.size() - m_ordinalIteration];
94 }
95
96 m_currentChild = m_forward ? m_box->firstChildBox() : m_box->lastChildBox();
97 } else
98 m_currentChild = m_forward ? m_currentChild->nextSiblingBox() : m_currentChild->previousSiblingBox();
99
100 if (m_currentChild && notFirstOrdinalValue())
101 m_ordinalValues.add(m_currentChild->style().boxOrdinalGroup());
102 } while (!m_currentChild || m_currentChild->isExcludedFromNormalLayout() || (!m_currentChild->isAnonymous()
103 && m_currentChild->style().boxOrdinalGroup() != m_currentOrdinal));
104 return m_currentChild;
105 }
106
107private:
108 bool notFirstOrdinalValue()
109 {
110 unsigned int firstOrdinalValue = m_forward ? 1 : m_largestOrdinal;
111 return m_currentOrdinal == firstOrdinalValue && m_currentChild->style().boxOrdinalGroup() != firstOrdinalValue;
112 }
113
114 RenderDeprecatedFlexibleBox* m_box;
115 RenderBox* m_currentChild;
116 bool m_forward;
117 unsigned m_currentOrdinal;
118 unsigned m_largestOrdinal;
119 HashSet<unsigned> m_ordinalValues;
120 Vector<unsigned> m_sortedOrdinalValues;
121 unsigned m_ordinalIteration;
122};
123
124RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element& element, RenderStyle&& style)
125 : RenderBlock(element, WTFMove(style), 0)
126{
127 setChildrenInline(false); // All of our children must be block-level
128 m_stretchingChildren = false;
129}
130
131RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox() = default;
132
133static LayoutUnit marginWidthForChild(RenderBox* child)
134{
135 // A margin basically has three types: fixed, percentage, and auto (variable).
136 // Auto and percentage margins simply become 0 when computing min/max width.
137 // Fixed margins can be added in as is.
138 Length marginLeft = child->style().marginLeft();
139 Length marginRight = child->style().marginRight();
140 LayoutUnit margin;
141 if (marginLeft.isFixed())
142 margin += marginLeft.value();
143 if (marginRight.isFixed())
144 margin += marginRight.value();
145 return margin;
146}
147
148static bool childDoesNotAffectWidthOrFlexing(RenderObject* child)
149{
150 // Positioned children and collapsed children don't affect the min/max width.
151 return child->isOutOfFlowPositioned() || child->style().visibility() == Visibility::Collapse;
152}
153
154static LayoutUnit contentWidthForChild(RenderBox* child)
155{
156 if (child->hasOverrideContentLogicalWidth())
157 return child->overrideContentLogicalWidth();
158 return child->logicalWidth() - child->borderAndPaddingLogicalWidth();
159}
160
161static LayoutUnit contentHeightForChild(RenderBox* child)
162{
163 if (child->hasOverrideContentLogicalHeight())
164 return child->overrideContentLogicalHeight();
165 return child->logicalHeight() - child->borderAndPaddingLogicalHeight();
166}
167
168void RenderDeprecatedFlexibleBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
169{
170 auto* oldStyle = hasInitializedStyle() ? &style() : nullptr;
171 if (oldStyle && !oldStyle->lineClamp().isNone() && newStyle.lineClamp().isNone())
172 clearLineClamp();
173
174 RenderBlock::styleWillChange(diff, newStyle);
175}
176
177void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
178{
179 if (hasMultipleLines() || isVertical()) {
180 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
181 if (childDoesNotAffectWidthOrFlexing(child))
182 continue;
183
184 LayoutUnit margin = marginWidthForChild(child);
185 LayoutUnit width = child->minPreferredLogicalWidth() + margin;
186 minLogicalWidth = std::max(width, minLogicalWidth);
187
188 width = child->maxPreferredLogicalWidth() + margin;
189 maxLogicalWidth = std::max(width, maxLogicalWidth);
190 }
191 } else {
192 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) {
193 if (childDoesNotAffectWidthOrFlexing(child))
194 continue;
195
196 LayoutUnit margin = marginWidthForChild(child);
197 minLogicalWidth += child->minPreferredLogicalWidth() + margin;
198 maxLogicalWidth += child->maxPreferredLogicalWidth() + margin;
199 }
200 }
201
202 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth);
203
204 LayoutUnit scrollbarWidth = intrinsicScrollbarLogicalWidth();
205 maxLogicalWidth += scrollbarWidth;
206 minLogicalWidth += scrollbarWidth;
207}
208
209void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths()
210{
211 ASSERT(preferredLogicalWidthsDirty());
212
213 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
214 if (style().width().isFixed() && style().width().value() > 0)
215 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style().width().value());
216 else
217 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
218
219 if (style().minWidth().isFixed() && style().minWidth().value() > 0) {
220 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().minWidth().value()));
221 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().minWidth().value()));
222 }
223
224 if (style().maxWidth().isFixed()) {
225 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().maxWidth().value()));
226 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style().maxWidth().value()));
227 }
228
229 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth();
230 m_minPreferredLogicalWidth += borderAndPadding;
231 m_maxPreferredLogicalWidth += borderAndPadding;
232
233 setPreferredLogicalWidthsDirty(false);
234}
235
236// Use an inline capacity of 8, since flexbox containers usually have less than 8 children.
237typedef Vector<LayoutRect, 8> ChildFrameRects;
238typedef Vector<LayoutSize, 8> ChildLayoutDeltas;
239
240static void appendChildFrameRects(RenderDeprecatedFlexibleBox* box, ChildFrameRects& childFrameRects)
241{
242 FlexBoxIterator iterator(box);
243 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
244 if (!child->isOutOfFlowPositioned())
245 childFrameRects.append(child->frameRect());
246 }
247}
248
249static void appendChildLayoutDeltas(RenderDeprecatedFlexibleBox* box, ChildLayoutDeltas& childLayoutDeltas)
250{
251 FlexBoxIterator iterator(box);
252 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
253 if (!child->isOutOfFlowPositioned())
254 childLayoutDeltas.append(LayoutSize());
255 }
256}
257
258static void repaintChildrenDuringLayoutIfMoved(RenderDeprecatedFlexibleBox* box, const ChildFrameRects& oldChildRects)
259{
260 size_t childIndex = 0;
261 FlexBoxIterator iterator(box);
262 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
263 if (child->isOutOfFlowPositioned())
264 continue;
265
266 // If the child moved, we have to repaint it as well as any floating/positioned
267 // descendants. An exception is if we need a layout. In this case, we know we're going to
268 // repaint ourselves (and the child) anyway.
269 if (!box->selfNeedsLayout() && child->checkForRepaintDuringLayout())
270 child->repaintDuringLayoutIfMoved(oldChildRects[childIndex]);
271
272 ++childIndex;
273 }
274 ASSERT(childIndex == oldChildRects.size());
275}
276
277void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren, LayoutUnit)
278{
279 ASSERT(needsLayout());
280
281 if (!relayoutChildren && simplifiedLayout())
282 return;
283
284 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
285 {
286 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
287
288 preparePaginationBeforeBlockLayout(relayoutChildren);
289
290 LayoutSize previousSize = size();
291
292 updateLogicalWidth();
293 updateLogicalHeight();
294
295 if (previousSize != size()
296 || (parent()->isDeprecatedFlexibleBox() && parent()->style().boxOrient() == BoxOrient::Horizontal
297 && parent()->style().boxAlign() == BoxAlignment::Stretch))
298 relayoutChildren = true;
299
300 setHeight(0);
301
302 m_stretchingChildren = false;
303
304#if !ASSERT_DISABLED
305 LayoutSize oldLayoutDelta = view().frameView().layoutContext().layoutDelta();
306#endif
307
308 // Fieldsets need to find their legend and position it inside the border of the object.
309 // The legend then gets skipped during normal layout. The same is true for ruby text.
310 // It doesn't get included in the normal layout process but is instead skipped.
311 layoutExcludedChildren(relayoutChildren);
312
313 ChildFrameRects oldChildRects;
314 appendChildFrameRects(this, oldChildRects);
315
316 if (isHorizontal())
317 layoutHorizontalBox(relayoutChildren);
318 else
319 layoutVerticalBox(relayoutChildren);
320
321 repaintChildrenDuringLayoutIfMoved(this, oldChildRects);
322 ASSERT(view().frameView().layoutContext().layoutDeltaMatches(oldLayoutDelta));
323
324 LayoutUnit oldClientAfterEdge = clientLogicalBottom();
325 updateLogicalHeight();
326
327 if (previousSize.height() != height())
328 relayoutChildren = true;
329
330 layoutPositionedObjects(relayoutChildren || isDocumentElementRenderer());
331
332 computeOverflow(oldClientAfterEdge);
333 }
334
335 updateLayerTransform();
336
337 auto* layoutState = view().frameView().layoutContext().layoutState();
338 if (layoutState && layoutState->pageLogicalHeight())
339 setPageLogicalOffset(layoutState->pageLogicalOffset(this, logicalTop()));
340
341 // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if
342 // we overflow or not.
343 updateScrollInfoAfterLayout();
344
345 // Repaint with our new bounds if they are different from our old bounds.
346 repainter.repaintAfterLayout();
347
348 clearNeedsLayout();
349}
350
351// The first walk over our kids is to find out if we have any flexible children.
352static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex)
353{
354 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
355 // Check to see if this child flexes.
356 if (!childDoesNotAffectWidthOrFlexing(child) && child->style().boxFlex() > 0.0f) {
357 // We always have to lay out flexible objects again, since the flex distribution
358 // may have changed, and we need to reallocate space.
359 child->clearOverrideContentSize();
360 if (!relayoutChildren)
361 child->setChildNeedsLayout(MarkOnlyThis);
362 haveFlex = true;
363 unsigned flexGroup = child->style().boxFlexGroup();
364 if (lowestFlexGroup == 0)
365 lowestFlexGroup = flexGroup;
366 if (flexGroup < lowestFlexGroup)
367 lowestFlexGroup = flexGroup;
368 if (flexGroup > highestFlexGroup)
369 highestFlexGroup = flexGroup;
370 }
371 }
372}
373
374static void layoutChildIfNeededApplyingDelta(RenderBox* child, const LayoutSize& layoutDelta)
375{
376 if (!child->needsLayout())
377 return;
378
379 child->view().frameView().layoutContext().addLayoutDelta(layoutDelta);
380 child->layoutIfNeeded();
381 child->view().frameView().layoutContext().addLayoutDelta(-layoutDelta);
382}
383
384void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren)
385{
386 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
387 LayoutUnit yPos = borderTop() + paddingTop();
388 LayoutUnit xPos = borderLeft() + paddingLeft();
389 bool heightSpecified = false;
390 LayoutUnit oldHeight;
391
392 LayoutUnit remainingSpace;
393
394 FlexBoxIterator iterator(this);
395 unsigned int highestFlexGroup = 0;
396 unsigned int lowestFlexGroup = 0;
397 bool haveFlex = false, flexingChildren = false;
398 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);
399
400 beginUpdateScrollInfoAfterLayoutTransaction();
401
402 ChildLayoutDeltas childLayoutDeltas;
403 appendChildLayoutDeltas(this, childLayoutDeltas);
404
405 // We do 2 passes. The first pass is simply to lay everyone out at
406 // their preferred widths. The subsequent passes handle flexing the children.
407 // The first pass skips flexible objects completely.
408 do {
409 // Reset our height.
410 setHeight(yPos);
411
412 xPos = borderLeft() + paddingLeft();
413
414 size_t childIndex = 0;
415
416 // Our first pass is done without flexing. We simply lay the children
417 // out within the box. We have to do a layout first in order to determine
418 // our box's intrinsic height.
419 LayoutUnit maxAscent, maxDescent;
420 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
421 if (relayoutChildren)
422 child->setChildNeedsLayout(MarkOnlyThis);
423
424 if (child->isOutOfFlowPositioned())
425 continue;
426
427 LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++];
428
429 // Compute the child's vertical margins.
430 child->computeAndSetBlockDirectionMargins(*this);
431
432 child->markForPaginationRelayoutIfNeeded();
433
434 // Apply the child's current layout delta.
435 layoutChildIfNeededApplyingDelta(child, childLayoutDelta);
436
437 // Update our height and overflow height.
438 if (style().boxAlign() == BoxAlignment::Baseline) {
439 LayoutUnit ascent = child->firstLineBaseline().valueOr(child->height() + child->marginBottom());
440 ascent += child->marginTop();
441 LayoutUnit descent = (child->height() + child->verticalMarginExtent()) - ascent;
442
443 // Update our maximum ascent.
444 maxAscent = std::max(maxAscent, ascent);
445
446 // Update our maximum descent.
447 maxDescent = std::max(maxDescent, descent);
448
449 // Now update our height.
450 setHeight(std::max(yPos + maxAscent + maxDescent, height()));
451 }
452 else
453 setHeight(std::max(height(), yPos + child->height() + child->verticalMarginExtent()));
454 }
455 ASSERT(childIndex == childLayoutDeltas.size());
456
457 if (!iterator.first() && hasLineIfEmpty())
458 setHeight(height() + lineHeight(true, style().isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
459
460 setHeight(height() + toAdd);
461
462 oldHeight = height();
463 updateLogicalHeight();
464
465 relayoutChildren = false;
466 if (oldHeight != height())
467 heightSpecified = true;
468
469 // Now that our height is actually known, we can place our boxes.
470 childIndex = 0;
471 m_stretchingChildren = (style().boxAlign() == BoxAlignment::Stretch);
472 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
473 if (child->isOutOfFlowPositioned()) {
474 child->containingBlock()->insertPositionedObject(*child);
475 RenderLayer* childLayer = child->layer();
476 childLayer->setStaticInlinePosition(xPos); // FIXME: Not right for regions.
477 if (childLayer->staticBlockPosition() != yPos) {
478 childLayer->setStaticBlockPosition(yPos);
479 if (child->style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
480 child->setChildNeedsLayout(MarkOnlyThis);
481 }
482 continue;
483 }
484
485 LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++];
486
487 if (child->style().visibility() == Visibility::Collapse) {
488 // visibility: collapsed children do not participate in our positioning.
489 // But we need to lay them out.
490 layoutChildIfNeededApplyingDelta(child, childLayoutDelta);
491 continue;
492 }
493
494 // We need to see if this child's height has changed, since we make block elements
495 // fill the height of a containing box by default.
496 // Now do a layout.
497 LayoutUnit oldChildHeight = child->height();
498 child->updateLogicalHeight();
499 if (oldChildHeight != child->height())
500 child->setChildNeedsLayout(MarkOnlyThis);
501
502 child->markForPaginationRelayoutIfNeeded();
503
504 layoutChildIfNeededApplyingDelta(child, childLayoutDelta);
505
506 // We can place the child now, using our value of box-align.
507 xPos += child->marginLeft();
508 LayoutUnit childY = yPos;
509 switch (style().boxAlign()) {
510 case BoxAlignment::Center:
511 childY += child->marginTop() + std::max<LayoutUnit>(0, (contentHeight() - (child->height() + child->verticalMarginExtent())) / 2);
512 break;
513 case BoxAlignment::Baseline: {
514 LayoutUnit ascent = child->firstLineBaseline().valueOr(child->height() + child->marginBottom());
515 ascent += child->marginTop();
516 childY += child->marginTop() + (maxAscent - ascent);
517 break;
518 }
519 case BoxAlignment::End:
520 childY += contentHeight() - child->marginBottom() - child->height();
521 break;
522 default: // BoxAlignment::Start
523 childY += child->marginTop();
524 break;
525 }
526
527 placeChild(child, LayoutPoint(xPos, childY), &childLayoutDelta);
528
529 xPos += child->width() + child->marginRight();
530 }
531 ASSERT(childIndex == childLayoutDeltas.size());
532
533 remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos;
534
535 m_stretchingChildren = false;
536 if (flexingChildren)
537 haveFlex = false; // We're done.
538 else if (haveFlex) {
539 // We have some flexible objects. See if we need to grow/shrink them at all.
540 if (!remainingSpace)
541 break;
542
543 // Allocate the remaining space among the flexible objects. If we are trying to
544 // grow, then we go from the lowest flex group to the highest flex group. For shrinking,
545 // we go from the highest flex group to the lowest group.
546 bool expanding = remainingSpace > 0;
547 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
548 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
549 for (unsigned int i = start; i <= end && remainingSpace; i++) {
550 // Always start off by assuming the group can get all the remaining space.
551 LayoutUnit groupRemainingSpace = remainingSpace;
552 do {
553 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
554 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
555 // computing the allowed growth before an object hits its min/max width (and thus
556 // forces a totalFlex recomputation).
557 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
558 float totalFlex = 0.0f;
559 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
560 if (allowedChildFlex(child, expanding, i))
561 totalFlex += child->style().boxFlex();
562 }
563 LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
564 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
565 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
566 if (allowedFlex) {
567 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex()));
568 spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex);
569 }
570 }
571
572 // The flex groups may not have any flexible objects this time around.
573 if (!spaceAvailableThisPass || totalFlex == 0.0f) {
574 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
575 groupRemainingSpace = 0;
576 continue;
577 }
578
579 // Now distribute the space to objects.
580 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
581 if (child->style().visibility() == Visibility::Collapse)
582 continue;
583
584 if (allowedChildFlex(child, expanding, i)) {
585 LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style().boxFlex() / totalFlex));
586 if (spaceAdd) {
587 child->setOverrideContentLogicalWidth(contentWidthForChild(child) + spaceAdd);
588 flexingChildren = true;
589 relayoutChildren = true;
590 }
591
592 spaceAvailableThisPass -= spaceAdd;
593 remainingSpace -= spaceAdd;
594 groupRemainingSpace -= spaceAdd;
595
596 totalFlex -= child->style().boxFlex();
597 }
598 }
599 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
600 // This is not advancing, avoid getting stuck by distributing the remaining pixels.
601 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
602 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
603 if (allowedChildFlex(child, expanding, i)) {
604 child->setOverrideContentLogicalWidth(contentWidthForChild(child) + spaceAdd);
605 flexingChildren = true;
606 relayoutChildren = true;
607 remainingSpace -= spaceAdd;
608 groupRemainingSpace -= spaceAdd;
609 }
610 }
611 }
612 } while (absoluteValue(groupRemainingSpace) >= 1);
613 }
614
615 // We didn't find any children that could grow.
616 if (haveFlex && !flexingChildren)
617 haveFlex = false;
618 }
619 } while (haveFlex);
620
621 endAndCommitUpdateScrollInfoAfterLayoutTransaction();
622
623 if (remainingSpace > 0 && ((style().isLeftToRightDirection() && style().boxPack() != BoxPack::Start)
624 || (!style().isLeftToRightDirection() && style().boxPack() != BoxPack::End))) {
625 // Children must be repositioned.
626 LayoutUnit offset;
627 if (style().boxPack() == BoxPack::Justify) {
628 // Determine the total number of children.
629 int totalChildren = 0;
630 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
631 if (childDoesNotAffectWidthOrFlexing(child))
632 continue;
633 ++totalChildren;
634 }
635
636 // Iterate over the children and space them out according to the
637 // justification level.
638 if (totalChildren > 1) {
639 --totalChildren;
640 bool firstChild = true;
641 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
642 if (childDoesNotAffectWidthOrFlexing(child))
643 continue;
644
645 if (firstChild) {
646 firstChild = false;
647 continue;
648 }
649
650 offset += remainingSpace/totalChildren;
651 remainingSpace -= (remainingSpace/totalChildren);
652 --totalChildren;
653
654 placeChild(child, child->location() + LayoutSize(offset, 0_lu));
655 }
656 }
657 } else {
658 if (style().boxPack() == BoxPack::Center)
659 offset += remainingSpace / 2;
660 else // BoxPack::End for LTR, BoxPack::Start for RTL
661 offset += remainingSpace;
662 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
663 if (childDoesNotAffectWidthOrFlexing(child))
664 continue;
665
666 placeChild(child, child->location() + LayoutSize(offset, 0_lu));
667 }
668 }
669 }
670
671 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
672 // a height change, we revert our height back to the intrinsic height before returning.
673 if (heightSpecified)
674 setHeight(oldHeight);
675}
676
677void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren)
678{
679 LayoutUnit yPos = borderTop() + paddingTop();
680 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
681 bool heightSpecified = false;
682 LayoutUnit oldHeight;
683
684 LayoutUnit remainingSpace;
685
686 FlexBoxIterator iterator(this);
687 unsigned int highestFlexGroup = 0;
688 unsigned int lowestFlexGroup = 0;
689 bool haveFlex = false, flexingChildren = false;
690 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex);
691
692 // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of
693 // mainstream block layout); this is not really part of the XUL box model.
694 bool haveLineClamp = !style().lineClamp().isNone();
695 if (haveLineClamp)
696 applyLineClamp(iterator, relayoutChildren);
697
698 beginUpdateScrollInfoAfterLayoutTransaction();
699
700 ChildLayoutDeltas childLayoutDeltas;
701 appendChildLayoutDeltas(this, childLayoutDeltas);
702
703 // We do 2 passes. The first pass is simply to lay everyone out at
704 // their preferred widths. The second pass handles flexing the children.
705 // Our first pass is done without flexing. We simply lay the children
706 // out within the box.
707 do {
708 setHeight(borderTop() + paddingTop());
709 LayoutUnit minHeight = height() + toAdd;
710
711 size_t childIndex = 0;
712 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
713 // Make sure we relayout children if we need it.
714 if (!haveLineClamp && relayoutChildren)
715 child->setChildNeedsLayout(MarkOnlyThis);
716
717 if (child->isOutOfFlowPositioned()) {
718 child->containingBlock()->insertPositionedObject(*child);
719 RenderLayer* childLayer = child->layer();
720 childLayer->setStaticInlinePosition(borderStart() + paddingStart()); // FIXME: Not right for regions.
721 if (childLayer->staticBlockPosition() != height()) {
722 childLayer->setStaticBlockPosition(height());
723 if (child->style().hasStaticBlockPosition(style().isHorizontalWritingMode()))
724 child->setChildNeedsLayout(MarkOnlyThis);
725 }
726 continue;
727 }
728
729 LayoutSize& childLayoutDelta = childLayoutDeltas[childIndex++];
730
731 if (child->style().visibility() == Visibility::Collapse) {
732 // visibility: collapsed children do not participate in our positioning.
733 // But we need to lay them down.
734 layoutChildIfNeededApplyingDelta(child, childLayoutDelta);
735 continue;
736 }
737
738 // Compute the child's vertical margins.
739 child->computeAndSetBlockDirectionMargins(*this);
740
741 // Add in the child's marginTop to our height.
742 setHeight(height() + child->marginTop());
743
744 child->markForPaginationRelayoutIfNeeded();
745
746 // Now do a layout.
747 layoutChildIfNeededApplyingDelta(child, childLayoutDelta);
748
749 // We can place the child now, using our value of box-align.
750 LayoutUnit childX = borderLeft() + paddingLeft();
751 switch (style().boxAlign()) {
752 case BoxAlignment::Center:
753 case BoxAlignment::Baseline: // Baseline just maps to center for vertical boxes
754 childX += child->marginLeft() + std::max<LayoutUnit>(0, (contentWidth() - (child->width() + child->horizontalMarginExtent())) / 2);
755 break;
756 case BoxAlignment::End:
757 if (!style().isLeftToRightDirection())
758 childX += child->marginLeft();
759 else
760 childX += contentWidth() - child->marginRight() - child->width();
761 break;
762 default: // BoxAlignment::Start/BoxAlignment::Stretch
763 if (style().isLeftToRightDirection())
764 childX += child->marginLeft();
765 else
766 childX += contentWidth() - child->marginRight() - child->width();
767 break;
768 }
769
770 // Place the child.
771 placeChild(child, LayoutPoint(childX, height()), &childLayoutDelta);
772 setHeight(height() + child->height() + child->marginBottom());
773 }
774 ASSERT(childIndex == childLayoutDeltas.size());
775
776 yPos = height();
777
778 if (!iterator.first() && hasLineIfEmpty())
779 setHeight(height() + lineHeight(true, style().isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
780
781 setHeight(height() + toAdd);
782
783 // Negative margins can cause our height to shrink below our minimal height (border/padding).
784 // If this happens, ensure that the computed height is increased to the minimal height.
785 if (height() < minHeight)
786 setHeight(minHeight);
787
788 // Now we have to calc our height, so we know how much space we have remaining.
789 oldHeight = height();
790 updateLogicalHeight();
791 if (oldHeight != height())
792 heightSpecified = true;
793
794 remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos;
795
796 if (flexingChildren)
797 haveFlex = false; // We're done.
798 else if (haveFlex) {
799 // We have some flexible objects. See if we need to grow/shrink them at all.
800 if (!remainingSpace)
801 break;
802
803 // Allocate the remaining space among the flexible objects. If we are trying to
804 // grow, then we go from the lowest flex group to the highest flex group. For shrinking,
805 // we go from the highest flex group to the lowest group.
806 bool expanding = remainingSpace > 0;
807 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup;
808 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup;
809 for (unsigned int i = start; i <= end && remainingSpace; i++) {
810 // Always start off by assuming the group can get all the remaining space.
811 LayoutUnit groupRemainingSpace = remainingSpace;
812 do {
813 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width
814 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and
815 // computing the allowed growth before an object hits its min/max width (and thus
816 // forces a totalFlex recomputation).
817 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace;
818 float totalFlex = 0.0f;
819 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
820 if (allowedChildFlex(child, expanding, i))
821 totalFlex += child->style().boxFlex();
822 }
823 LayoutUnit spaceAvailableThisPass = groupRemainingSpace;
824 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
825 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i);
826 if (allowedFlex) {
827 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style().boxFlex()));
828 spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex);
829 }
830 }
831
832 // The flex groups may not have any flexible objects this time around.
833 if (!spaceAvailableThisPass || totalFlex == 0.0f) {
834 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group.
835 groupRemainingSpace = 0;
836 continue;
837 }
838
839 // Now distribute the space to objects.
840 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) {
841 if (allowedChildFlex(child, expanding, i)) {
842 LayoutUnit spaceAdd = spaceAvailableThisPass * (child->style().boxFlex() / totalFlex);
843 if (spaceAdd) {
844 child->setOverrideContentLogicalHeight(contentHeightForChild(child) + spaceAdd);
845 flexingChildren = true;
846 relayoutChildren = true;
847 }
848
849 spaceAvailableThisPass -= spaceAdd;
850 remainingSpace -= spaceAdd;
851 groupRemainingSpace -= spaceAdd;
852
853 totalFlex -= child->style().boxFlex();
854 }
855 }
856 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) {
857 // This is not advancing, avoid getting stuck by distributing the remaining pixels.
858 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1;
859 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) {
860 if (allowedChildFlex(child, expanding, i)) {
861 child->setOverrideContentLogicalHeight(contentHeightForChild(child) + spaceAdd);
862 flexingChildren = true;
863 relayoutChildren = true;
864 remainingSpace -= spaceAdd;
865 groupRemainingSpace -= spaceAdd;
866 }
867 }
868 }
869 } while (absoluteValue(groupRemainingSpace) >= 1);
870 }
871
872 // We didn't find any children that could grow.
873 if (haveFlex && !flexingChildren)
874 haveFlex = false;
875 }
876 } while (haveFlex);
877
878 endAndCommitUpdateScrollInfoAfterLayoutTransaction();
879
880 if (style().boxPack() != BoxPack::Start && remainingSpace > 0) {
881 // Children must be repositioned.
882 LayoutUnit offset;
883 if (style().boxPack() == BoxPack::Justify) {
884 // Determine the total number of children.
885 int totalChildren = 0;
886 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
887 if (childDoesNotAffectWidthOrFlexing(child))
888 continue;
889
890 ++totalChildren;
891 }
892
893 // Iterate over the children and space them out according to the
894 // justification level.
895 if (totalChildren > 1) {
896 --totalChildren;
897 bool firstChild = true;
898 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
899 if (childDoesNotAffectWidthOrFlexing(child))
900 continue;
901
902 if (firstChild) {
903 firstChild = false;
904 continue;
905 }
906
907 offset += remainingSpace/totalChildren;
908 remainingSpace -= (remainingSpace/totalChildren);
909 --totalChildren;
910 placeChild(child, child->location() + LayoutSize(0_lu, offset));
911 }
912 }
913 } else {
914 if (style().boxPack() == BoxPack::Center)
915 offset += remainingSpace / 2;
916 else // BoxPack::End
917 offset += remainingSpace;
918 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
919 if (childDoesNotAffectWidthOrFlexing(child))
920 continue;
921 placeChild(child, child->location() + LayoutSize(0_lu, offset));
922 }
923 }
924 }
925
926 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of
927 // a height change, we revert our height back to the intrinsic height before returning.
928 if (heightSpecified)
929 setHeight(oldHeight);
930}
931
932void RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren)
933{
934 int maxLineCount = 0;
935 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
936 if (childDoesNotAffectWidthOrFlexing(child))
937 continue;
938
939 child->clearOverrideContentSize();
940 if (relayoutChildren || (child->isReplaced() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated()))
941 || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) {
942 child->setChildNeedsLayout(MarkOnlyThis);
943
944 // Dirty all the positioned objects.
945 if (is<RenderBlockFlow>(*child)) {
946 downcast<RenderBlockFlow>(*child).markPositionedObjectsForLayout();
947 downcast<RenderBlockFlow>(*child).clearTruncation();
948 }
949 }
950 child->layoutIfNeeded();
951 if (child->style().height().isAuto() && is<RenderBlockFlow>(*child))
952 maxLineCount = std::max(maxLineCount, downcast<RenderBlockFlow>(*child).lineCount());
953 }
954
955 // Get the number of lines and then alter all block flow children with auto height to use the
956 // specified height. We always try to leave room for at least one line.
957 LineClampValue lineClamp = style().lineClamp();
958 int numVisibleLines = lineClamp.isPercentage() ? std::max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value();
959 if (numVisibleLines >= maxLineCount)
960 return;
961
962 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
963 if (childDoesNotAffectWidthOrFlexing(child) || !child->style().height().isAuto() || !is<RenderBlockFlow>(*child))
964 continue;
965
966 RenderBlockFlow& blockChild = downcast<RenderBlockFlow>(*child);
967 int lineCount = blockChild.lineCount();
968 if (lineCount <= numVisibleLines)
969 continue;
970
971 LayoutUnit newHeight = blockChild.heightForLineCount(numVisibleLines);
972 if (newHeight == child->height())
973 continue;
974
975 child->setChildNeedsLayout(MarkOnlyThis);
976 child->setOverrideContentLogicalHeight(newHeight - child->verticalBorderAndPaddingExtent());
977 child->layoutIfNeeded();
978
979 // FIXME: For now don't support RTL.
980 if (style().direction() != TextDirection::LTR)
981 continue;
982
983 // Get the last line
984 RootInlineBox* lastLine = blockChild.lineAtIndex(lineCount - 1);
985 if (!lastLine)
986 continue;
987
988 RootInlineBox* lastVisibleLine = blockChild.lineAtIndex(numVisibleLines - 1);
989 if (!lastVisibleLine)
990 continue;
991
992 const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' };
993 static NeverDestroyed<AtomicString> ellipsisAndSpaceStr(ellipsisAndSpace, 2);
994 static NeverDestroyed<AtomicString> ellipsisStr(&horizontalEllipsis, 1);
995 const RenderStyle& lineStyle = numVisibleLines == 1 ? firstLineStyle() : style();
996 const FontCascade& font = lineStyle.fontCascade();
997
998 // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too
999 LayoutUnit totalWidth;
1000 InlineBox* anchorBox = lastLine->lastChild();
1001 if (anchorBox && anchorBox->renderer().style().isLink())
1002 totalWidth = anchorBox->logicalWidth() + font.width(constructTextRun(ellipsisAndSpace, 2, style()));
1003 else {
1004 anchorBox = nullptr;
1005 totalWidth = font.width(constructTextRun(&horizontalEllipsis, 1, style()));
1006 }
1007
1008 // See if this width can be accommodated on the last visible line
1009 RenderBlockFlow& destBlock = lastVisibleLine->blockFlow();
1010 RenderBlockFlow& srcBlock = lastLine->blockFlow();
1011
1012 // FIXME: Directions of src/destBlock could be different from our direction and from one another.
1013 if (!srcBlock.style().isLeftToRightDirection())
1014 continue;
1015
1016 bool leftToRight = destBlock.style().isLeftToRightDirection();
1017 if (!leftToRight)
1018 continue;
1019
1020 LayoutUnit blockRightEdge = destBlock.logicalRightOffsetForLine(lastVisibleLine->y(), DoNotIndentText);
1021 if (!lastVisibleLine->lineCanAccommodateEllipsis(leftToRight, blockRightEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth))
1022 continue;
1023
1024 // Let the truncation code kick in.
1025 // FIXME: the text alignment should be recomputed after the width changes due to truncation.
1026 LayoutUnit blockLeftEdge = destBlock.logicalLeftOffsetForLine(lastVisibleLine->y(), DoNotIndentText);
1027 lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox);
1028 destBlock.setHasMarkupTruncation(true);
1029 }
1030}
1031
1032void RenderDeprecatedFlexibleBox::clearLineClamp()
1033{
1034 FlexBoxIterator iterator(this);
1035 for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
1036 if (childDoesNotAffectWidthOrFlexing(child))
1037 continue;
1038
1039 child->clearOverrideContentSize();
1040 if ((child->isReplaced() && (child->style().width().isPercentOrCalculated() || child->style().height().isPercentOrCalculated()))
1041 || (child->style().height().isAuto() && is<RenderBlockFlow>(*child))) {
1042 child->setChildNeedsLayout();
1043
1044 if (is<RenderBlockFlow>(*child)) {
1045 downcast<RenderBlockFlow>(*child).markPositionedObjectsForLayout();
1046 downcast<RenderBlockFlow>(*child).clearTruncation();
1047 }
1048 }
1049 }
1050}
1051
1052void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location, LayoutSize* childLayoutDelta)
1053{
1054 // Place the child and track the layout delta so we can apply it if we do another layout.
1055 if (childLayoutDelta)
1056 *childLayoutDelta += LayoutSize(child->x() - location.x(), child->y() - location.y());
1057 child->setLocation(location);
1058}
1059
1060LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group)
1061{
1062 if (childDoesNotAffectWidthOrFlexing(child) || child->style().boxFlex() == 0.0f || child->style().boxFlexGroup() != group)
1063 return 0;
1064
1065 if (expanding) {
1066 if (isHorizontal()) {
1067 // FIXME: For now just handle fixed values.
1068 LayoutUnit maxWidth = LayoutUnit::max();
1069 LayoutUnit width = contentWidthForChild(child);
1070 if (!child->style().maxWidth().isUndefined() && child->style().maxWidth().isFixed())
1071 maxWidth = child->style().maxWidth().value();
1072 else if (child->style().maxWidth().type() == Intrinsic)
1073 maxWidth = child->maxPreferredLogicalWidth();
1074 else if (child->style().maxWidth().type() == MinIntrinsic)
1075 maxWidth = child->minPreferredLogicalWidth();
1076 if (maxWidth == LayoutUnit::max())
1077 return maxWidth;
1078 return std::max<LayoutUnit>(0, maxWidth - width);
1079 } else {
1080 // FIXME: For now just handle fixed values.
1081 LayoutUnit maxHeight = LayoutUnit::max();
1082 LayoutUnit height = contentHeightForChild(child);
1083 if (!child->style().maxHeight().isUndefined() && child->style().maxHeight().isFixed())
1084 maxHeight = child->style().maxHeight().value();
1085 if (maxHeight == LayoutUnit::max())
1086 return maxHeight;
1087 return std::max<LayoutUnit>(0, maxHeight - height);
1088 }
1089 }
1090
1091 // FIXME: For now just handle fixed values.
1092 if (isHorizontal()) {
1093 LayoutUnit minWidth = child->minPreferredLogicalWidth();
1094 LayoutUnit width = contentWidthForChild(child);
1095 if (child->style().minWidth().isFixed())
1096 minWidth = child->style().minWidth().value();
1097 else if (child->style().minWidth().type() == Intrinsic)
1098 minWidth = child->maxPreferredLogicalWidth();
1099 else if (child->style().minWidth().type() == MinIntrinsic)
1100 minWidth = child->minPreferredLogicalWidth();
1101 else if (child->style().minWidth().type() == Auto)
1102 minWidth = 0;
1103
1104 LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minWidth - width);
1105 return allowedShrinkage;
1106 } else {
1107 Length minHeight = child->style().minHeight();
1108 if (minHeight.isFixed() || minHeight.isAuto()) {
1109 LayoutUnit minHeight = child->style().minHeight().value();
1110 LayoutUnit height = contentHeightForChild(child);
1111 LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minHeight - height);
1112 return allowedShrinkage;
1113 }
1114 }
1115
1116 return 0;
1117}
1118
1119const char* RenderDeprecatedFlexibleBox::renderName() const
1120{
1121 if (isFloating())
1122 return "RenderDeprecatedFlexibleBox (floating)";
1123 if (isOutOfFlowPositioned())
1124 return "RenderDeprecatedFlexibleBox (positioned)";
1125 // FIXME: Temporary hack while the new generated content system is being implemented.
1126 if (isPseudoElement())
1127 return "RenderDeprecatedFlexibleBox (generated)";
1128 if (isAnonymous())
1129 return "RenderDeprecatedFlexibleBox (generated)";
1130 if (isRelativelyPositioned())
1131 return "RenderDeprecatedFlexibleBox (relative positioned)";
1132 return "RenderDeprecatedFlexibleBox";
1133}
1134
1135} // namespace WebCore
1136