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