/src/3rdparty/webkit/Source/WebCore/rendering/RenderBlockLineLayout.cpp
https://bitbucket.org/ultra_iter/qt-vtl · C++ · 2461 lines · 1817 code · 339 blank · 305 comment · 737 complexity · 26d2e012c089f2a89fc45467119eb825 MD5 · raw file
Large files are truncated click here to view the full file
- /*
- * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
- * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- */
- #include "config.h"
- #include "BidiResolver.h"
- #include "Hyphenation.h"
- #include "InlineIterator.h"
- #include "InlineTextBox.h"
- #include "Logging.h"
- #include "RenderArena.h"
- #include "RenderCombineText.h"
- #include "RenderInline.h"
- #include "RenderLayer.h"
- #include "RenderListMarker.h"
- #include "RenderRubyRun.h"
- #include "RenderView.h"
- #include "Settings.h"
- #include "TextBreakIterator.h"
- #include "TextRun.h"
- #include "TrailingFloatsRootInlineBox.h"
- #include "VerticalPositionCache.h"
- #include "break_lines.h"
- #include <wtf/AlwaysInline.h>
- #include <wtf/RefCountedLeakCounter.h>
- #include <wtf/StdLibExtras.h>
- #include <wtf/Vector.h>
- #include <wtf/unicode/CharacterNames.h>
- #if ENABLE(SVG)
- #include "RenderSVGInlineText.h"
- #include "SVGRootInlineBox.h"
- #endif
- using namespace std;
- using namespace WTF;
- using namespace Unicode;
- namespace WebCore {
- // We don't let our line box tree for a single line get any deeper than this.
- const unsigned cMaxLineDepth = 200;
- class LineInfo {
- public:
- LineInfo()
- : m_isFirstLine(true)
- , m_isLastLine(false)
- , m_isEmpty(true)
- , m_previousLineBrokeCleanly(true)
- { }
- bool isFirstLine() const { return m_isFirstLine; }
- bool isLastLine() const { return m_isLastLine; }
- bool isEmpty() const { return m_isEmpty; }
- bool previousLineBrokeCleanly() const { return m_previousLineBrokeCleanly; }
- void setFirstLine(bool firstLine) { m_isFirstLine = firstLine; }
- void setLastLine(bool lastLine) { m_isLastLine = lastLine; }
- void setEmpty(bool empty) { m_isEmpty = empty; }
- void setPreviousLineBrokeCleanly(bool previousLineBrokeCleanly) { m_previousLineBrokeCleanly = previousLineBrokeCleanly; }
- private:
- bool m_isFirstLine;
- bool m_isLastLine;
- bool m_isEmpty;
- bool m_previousLineBrokeCleanly;
- };
- static inline int borderPaddingMarginStart(RenderInline* child)
- {
- return child->marginStart() + child->paddingStart() + child->borderStart();
- }
- static inline int borderPaddingMarginEnd(RenderInline* child)
- {
- return child->marginEnd() + child->paddingEnd() + child->borderEnd();
- }
- static int inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true)
- {
- unsigned lineDepth = 1;
- int extraWidth = 0;
- RenderObject* parent = child->parent();
- while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
- RenderInline* parentAsRenderInline = toRenderInline(parent);
- if (start && !child->previousSibling())
- extraWidth += borderPaddingMarginStart(parentAsRenderInline);
- if (end && !child->nextSibling())
- extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
- child = parent;
- parent = child->parent();
- }
- return extraWidth;
- }
- static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak)
- {
- // Check to see if our last midpoint is a start point beyond the line break. If so,
- // shave it off the list, and shave off a trailing space if the previous end point doesn't
- // preserve whitespace.
- if (lBreak.m_obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) {
- InlineIterator* midpoints = lineMidpointState.midpoints.data();
- InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2];
- const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1];
- InlineIterator currpoint = endpoint;
- while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
- currpoint.increment();
- if (currpoint == lBreak) {
- // We hit the line break before the start point. Shave off the start point.
- lineMidpointState.numMidpoints--;
- if (endpoint.m_obj->style()->collapseWhiteSpace())
- endpoint.m_pos--;
- }
- }
- }
- static void addMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint)
- {
- if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints)
- lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10);
- InlineIterator* midpoints = lineMidpointState.midpoints.data();
- midpoints[lineMidpointState.numMidpoints++] = midpoint;
- }
- static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
- {
- return new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir());
- }
- void RenderBlock::appendRunsForObject(BidiRunList<BidiRun>& runs, int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
- {
- if (start > end || obj->isFloating() ||
- (obj->isPositioned() && !obj->style()->isOriginalDisplayInlineType() && !obj->container()->isRenderInline()))
- return;
- LineMidpointState& lineMidpointState = resolver.midpointState();
- bool haveNextMidpoint = (lineMidpointState.currentMidpoint < lineMidpointState.numMidpoints);
- InlineIterator nextMidpoint;
- if (haveNextMidpoint)
- nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint];
- if (lineMidpointState.betweenMidpoints) {
- if (!(haveNextMidpoint && nextMidpoint.m_obj == obj))
- return;
- // This is a new start point. Stop ignoring objects and
- // adjust our start.
- lineMidpointState.betweenMidpoints = false;
- start = nextMidpoint.m_pos;
- lineMidpointState.currentMidpoint++;
- if (start < end)
- return appendRunsForObject(runs, start, end, obj, resolver);
- } else {
- if (!haveNextMidpoint || (obj != nextMidpoint.m_obj)) {
- runs.addRun(createRun(start, end, obj, resolver));
- return;
- }
- // An end midpoint has been encountered within our object. We
- // need to go ahead and append a run with our endpoint.
- if (static_cast<int>(nextMidpoint.m_pos + 1) <= end) {
- lineMidpointState.betweenMidpoints = true;
- lineMidpointState.currentMidpoint++;
- if (nextMidpoint.m_pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
- if (static_cast<int>(nextMidpoint.m_pos + 1) > start)
- runs.addRun(createRun(start, nextMidpoint.m_pos + 1, obj, resolver));
- return appendRunsForObject(runs, nextMidpoint.m_pos + 1, end, obj, resolver);
- }
- } else
- runs.addRun(createRun(start, end, obj, resolver));
- }
- }
- static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
- {
- if (isRootLineBox)
- return toRenderBlock(obj)->createAndAppendRootInlineBox();
- if (obj->isText()) {
- InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
- // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
- // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.)
- if (obj->isBR())
- textBox->setIsText(isOnlyRun || obj->document()->inNoQuirksMode());
- return textBox;
- }
- if (obj->isBox())
- return toRenderBox(obj)->createInlineBox();
- return toRenderInline(obj)->createAndAppendInlineFlowBox();
- }
- static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
- {
- if (o->isText()) {
- if (o->preferredLogicalWidthsDirty() && (o->isCounter() || o->isQuote()))
- toRenderText(o)->computePreferredLogicalWidths(0); // FIXME: Counters depend on this hack. No clue why. Should be investigated and removed.
- toRenderText(o)->dirtyLineBoxes(fullLayout);
- } else
- toRenderInline(o)->dirtyLineBoxes(fullLayout);
- }
- static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
- {
- do {
- if (parentBox->isConstructed() || parentBox->nextOnLine())
- return true;
- parentBox = parentBox->parent();
- } while (parentBox);
- return false;
- }
- InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox)
- {
- // See if we have an unconstructed line box for this object that is also
- // the last item on the line.
- unsigned lineDepth = 1;
- InlineFlowBox* parentBox = 0;
- InlineFlowBox* result = 0;
- bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
- do {
- ASSERT(obj->isRenderInline() || obj == this);
- RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
- // Get the last box we made for this render object.
- parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox();
- // If this box or its ancestor is constructed then it is from a previous line, and we need
- // to make a new box for our line. If this box or its ancestor is unconstructed but it has
- // something following it on the line, then we know we have to make a new box
- // as well. In this situation our inline has actually been split in two on
- // the same line (this can happen with very fancy language mixtures).
- bool constructedNewBox = false;
- bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
- bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox);
- if (allowedToConstructNewBox && !canUseExistingParentBox) {
- // We need to make a new box for this render object. Once
- // made, we need to place it at the end of the current line.
- InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
- ASSERT(newBox->isInlineFlowBox());
- parentBox = static_cast<InlineFlowBox*>(newBox);
- parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
- parentBox->setIsHorizontal(isHorizontalWritingMode());
- if (!hasDefaultLineBoxContain)
- parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
- constructedNewBox = true;
- }
- if (constructedNewBox || canUseExistingParentBox) {
- if (!result)
- result = parentBox;
- // If we have hit the block itself, then |box| represents the root
- // inline box for the line, and it doesn't have to be appended to any parent
- // inline.
- if (childBox)
- parentBox->addToLine(childBox);
- if (!constructedNewBox || obj == this)
- break;
- childBox = parentBox;
- }
- // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
- // intermediate inline flows.
- obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
- } while (true);
- return result;
- }
- static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
- {
- BidiRun* run = bidiRuns.logicallyLastRun();
- if (!run)
- return true;
- unsigned int pos = run->stop();
- RenderObject* r = run->m_object;
- if (!r->isText() || r->isBR())
- return false;
- RenderText* renderText = toRenderText(r);
- if (pos >= renderText->textLength())
- return true;
- while (isASCIISpace(renderText->characters()[pos])) {
- pos++;
- if (pos >= renderText->textLength())
- return true;
- }
- return false;
- }
- RootInlineBox* RenderBlock::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
- {
- ASSERT(bidiRuns.firstRun());
- bool rootHasSelectedChildren = false;
- InlineFlowBox* parentBox = 0;
- for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
- // Create a box for our object.
- bool isOnlyRun = (bidiRuns.runCount() == 1);
- if (bidiRuns.runCount() == 2 && !r->m_object->isListMarker())
- isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
- InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun);
- r->m_box = box;
- ASSERT(box);
- if (!box)
- continue;
- if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone)
- rootHasSelectedChildren = true;
- // If we have no parent box yet, or if the run is not simply a sibling,
- // then we need to construct inline boxes as necessary to properly enclose the
- // run's inline box.
- if (!parentBox || parentBox->renderer() != r->m_object->parent())
- // Create new inline boxes all the way back to the appropriate insertion point.
- parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box);
- else {
- // Append the inline box to this line.
- parentBox->addToLine(box);
- }
- bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
- box->setBidiLevel(r->level());
- if (box->isInlineTextBox()) {
- InlineTextBox* text = static_cast<InlineTextBox*>(box);
- text->setStart(r->m_start);
- text->setLen(r->m_stop - r->m_start);
- text->m_dirOverride = r->dirOverride(visuallyOrdered);
- if (r->m_hasHyphen)
- text->setHasHyphen(true);
- }
- }
- // We should have a root inline box. It should be unconstructed and
- // be the last continuation of our line list.
- ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
- // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
- // from the bidi runs walk above has a selection state.
- if (rootHasSelectedChildren)
- lastLineBox()->root()->setHasSelectedChildren(true);
- // Set bits on our inline flow boxes that indicate which sides should
- // paint borders/margins/padding. This knowledge will ultimately be used when
- // we determine the horizontal positions and widths of all the inline boxes on
- // the line.
- bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
- lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
- // Now mark the line boxes as being constructed.
- lastLineBox()->setConstructed();
- // Return the last line.
- return lastRootBox();
- }
- ETextAlign RenderBlock::textAlignmentForLine(bool endsWithSoftBreak) const
- {
- ETextAlign alignment = style()->textAlign();
- if (!endsWithSoftBreak && alignment == JUSTIFY)
- alignment = TAAUTO;
- return alignment;
- }
- static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
- {
- // The direction of the block should determine what happens with wide lines.
- // In particular with RTL blocks, wide lines should still spill out to the left.
- if (isLeftToRightDirection) {
- if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
- return;
- }
- if (trailingSpaceRun)
- trailingSpaceRun->m_box->setLogicalWidth(0);
- else if (totalLogicalWidth > availableLogicalWidth)
- logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
- }
- static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
- {
- // Wide lines spill out of the block based off direction.
- // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
- // side of the block.
- if (isLeftToRightDirection) {
- if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceRun->m_box->setLogicalWidth(0);
- }
- if (totalLogicalWidth < availableLogicalWidth)
- logicalLeft += availableLogicalWidth - totalLogicalWidth;
- return;
- }
- if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- } else
- logicalLeft += availableLogicalWidth - totalLogicalWidth;
- }
- static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
- {
- float trailingSpaceWidth = 0;
- if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth));
- }
- if (isLeftToRightDirection)
- logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
- else
- logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
- }
- void RenderBlock::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo)
- {
- int startOverhang;
- int endOverhang;
- RenderObject* nextObject = 0;
- for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
- if (!runWithNextObject->m_object->isPositioned() && !runWithNextObject->m_box->isLineBreak()) {
- nextObject = runWithNextObject->m_object;
- break;
- }
- }
- renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
- setMarginStartForChild(renderer, -startOverhang);
- setMarginEndForChild(renderer, -endOverhang);
- }
- static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo,
- GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache)
- {
- HashSet<const SimpleFontData*> fallbackFonts;
- GlyphOverflow glyphOverflow;
-
- // Always compute glyph overflow if the block's line-box-contain value is "glyphs".
- if (lineBox->fitsToGlyphs()) {
- // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
- // will keep us from computing glyph bounds in nearly all cases.
- bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
- int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
- int rootDescent = includeRootLine ? lineBox->renderer()->style(lineInfo.isFirstLine())->font().fontMetrics().descent() : 0;
- int rootAscent = includeRootLine ? lineBox->renderer()->style(lineInfo.isFirstLine())->font().fontMetrics().ascent() : 0;
- int boxAscent = renderer->style(lineInfo.isFirstLine())->font().fontMetrics().ascent() - baselineShift;
- int boxDescent = renderer->style(lineInfo.isFirstLine())->font().fontMetrics().descent() + baselineShift;
- if (boxAscent > rootDescent || boxDescent > rootAscent)
- glyphOverflow.computeBounds = true;
- }
-
- int hyphenWidth = 0;
- if (static_cast<InlineTextBox*>(run->m_box)->hasHyphen()) {
- const AtomicString& hyphenString = renderer->style()->hyphenString();
- hyphenWidth = renderer->style(lineInfo.isFirstLine())->font().width(TextRun(hyphenString.characters(), hyphenString.length()));
- }
- run->m_box->setLogicalWidth(renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow) + hyphenWidth);
- if (!fallbackFonts.isEmpty()) {
- ASSERT(run->m_box->isText());
- GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first;
- ASSERT(it->second.first.isEmpty());
- copyToVector(fallbackFonts, it->second.first);
- run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
- }
- if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
- ASSERT(run->m_box->isText());
- GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast<InlineTextBox*>(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).first;
- it->second.second = glyphOverflow;
- run->m_box->clearKnownToHaveNoOverflow();
- }
- }
- static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
- {
- if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
- return;
- size_t i = 0;
- for (BidiRun* r = firstRun; r; r = r->next()) {
- if (!r->m_box || r == trailingSpaceRun)
- continue;
-
- if (r->m_object->isText()) {
- unsigned opportunitiesInRun = expansionOpportunities[i++];
-
- ASSERT(opportunitiesInRun <= expansionOpportunityCount);
-
- // Only justify text if whitespace is collapsed.
- if (r->m_object->style()->collapseWhiteSpace()) {
- InlineTextBox* textBox = static_cast<InlineTextBox*>(r->m_box);
- int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
- textBox->setExpansion(expansion);
- totalLogicalWidth += expansion;
- }
- expansionOpportunityCount -= opportunitiesInRun;
- if (!expansionOpportunityCount)
- break;
- }
- }
- }
- void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
- GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache)
- {
- ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
- float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), lineInfo.isFirstLine());
- float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), lineInfo.isFirstLine()) - logicalLeft;
- bool needsWordSpacing = false;
- float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
- unsigned expansionOpportunityCount = 0;
- bool isAfterExpansion = true;
- Vector<unsigned, 16> expansionOpportunities;
- RenderObject* previousObject = 0;
- for (BidiRun* r = firstRun; r; r = r->next()) {
- if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak())
- continue; // Positioned objects are only participating to figure out their
- // correct static x position. They have no effect on the width.
- // Similarly, line break boxes have no effect on the width.
- if (r->m_object->isText()) {
- RenderText* rt = toRenderText(r->m_object);
- if (textAlign == JUSTIFY && r != trailingSpaceRun) {
- if (!isAfterExpansion)
- static_cast<InlineTextBox*>(r->m_box)->setCanHaveLeadingExpansion(true);
- unsigned opportunitiesInRun = Font::expansionOpportunityCount(rt->characters() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
- expansionOpportunities.append(opportunitiesInRun);
- expansionOpportunityCount += opportunitiesInRun;
- }
- if (int length = rt->textLength()) {
- if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start]))
- totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().wordSpacing();
- needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length;
- }
- setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache);
- } else {
- isAfterExpansion = false;
- if (!r->m_object->isRenderInline()) {
- RenderBox* renderBox = toRenderBox(r->m_object);
- if (renderBox->isRubyRun())
- setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
- r->m_box->setLogicalWidth(logicalWidthForChild(renderBox));
- totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
- }
- }
- totalLogicalWidth += r->m_box->logicalWidth();
- previousObject = r->m_object;
- }
- if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
- expansionOpportunities.last()--;
- expansionOpportunityCount--;
- }
- // Armed with the total width of the line (without justification),
- // we now examine our text-align property in order to determine where to position the
- // objects horizontally. The total width of the line can be increased if we end up
- // justifying text.
- switch (textAlign) {
- case LEFT:
- case WEBKIT_LEFT:
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- break;
- case JUSTIFY:
- adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
- if (expansionOpportunityCount) {
- if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceRun->m_box->setLogicalWidth(0);
- }
- break;
- }
- // fall through
- case TAAUTO:
- // for right to left fall through to right aligned
- if (style()->isLeftToRightDirection()) {
- if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
- break;
- }
- case RIGHT:
- case WEBKIT_RIGHT:
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- break;
- case CENTER:
- case WEBKIT_CENTER:
- updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- break;
- case TASTART:
- if (style()->isLeftToRightDirection())
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- else
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- break;
- case TAEND:
- if (style()->isLeftToRightDirection())
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- else
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
- break;
- }
- computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
- // The widths of all runs are now known. We can now place every inline box (and
- // compute accurate widths for the inline flow boxes).
- needsWordSpacing = false;
- lineBox->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap);
- }
- void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
- VerticalPositionCache& verticalPositionCache)
- {
- setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
- lineBox->setBlockLogicalHeight(logicalHeight());
- // Now make sure we place replaced render objects correctly.
- for (BidiRun* r = firstRun; r; r = r->next()) {
- ASSERT(r->m_box);
- if (!r->m_box)
- continue; // Skip runs with no line boxes.
- // Align positioned boxes with the top of the line box. This is
- // a reasonable approximation of an appropriate y position.
- if (r->m_object->isPositioned())
- r->m_box->setLogicalTop(logicalHeight());
- // Position is used to properly position both replaced elements and
- // to update the static normal flow x/y of positioned elements.
- if (r->m_object->isText())
- toRenderText(r->m_object)->positionLineBox(r->m_box);
- else if (r->m_object->isBox())
- toRenderBox(r->m_object)->positionLineBox(r->m_box);
- }
- // Positioned objects and zero-length text nodes destroy their boxes in
- // position(), which unnecessarily dirties the line.
- lineBox->markDirty(false);
- }
- static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
- {
- if (character == ' ' || character == '\t' || character == softHyphen)
- return true;
- if (character == '\n')
- return !renderer->style()->preserveNewline();
- if (character == noBreakSpace)
- return renderer->style()->nbspMode() == SPACE;
- return false;
- }
- static void setStaticPositions(RenderBlock* block, RenderBox* child)
- {
- // FIXME: The math here is actually not really right. It's a best-guess approximation that
- // will work for the common cases
- RenderObject* containerBlock = child->container();
- int blockHeight = block->logicalHeight();
- if (containerBlock->isRenderInline()) {
- // A relative positioned inline encloses us. In this case, we also have to determine our
- // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned
- // inline so that we can obtain the value later.
- toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startOffsetForLine(blockHeight, false));
- toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight);
- }
- if (child->style()->isOriginalDisplayInlineType())
- child->layer()->setStaticInlinePosition(block->startOffsetForLine(blockHeight, false));
- else
- child->layer()->setStaticInlinePosition(block->borderAndPaddingStart());
- child->layer()->setStaticBlockPosition(blockHeight);
- }
- inline BidiRun* RenderBlock::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext)
- {
- if (!bidiRuns.runCount()
- || !bidiRuns.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()
- || !bidiRuns.logicallyLastRun()->m_object->style()->autoWrap())
- return 0;
- BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun();
- RenderObject* lastObject = trailingSpaceRun->m_object;
- if (!lastObject->isText())
- return 0;
- RenderText* lastText = toRenderText(lastObject);
- const UChar* characters = lastText->characters();
- int firstSpace = trailingSpaceRun->stop();
- while (firstSpace > trailingSpaceRun->start()) {
- UChar current = characters[firstSpace - 1];
- if (!isCollapsibleSpace(current, lastText))
- break;
- firstSpace--;
- }
- if (firstSpace == trailingSpaceRun->stop())
- return 0;
- TextDirection direction = style()->direction();
- bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun());
- if (firstSpace != trailingSpaceRun->start()) {
- BidiContext* baseContext = currentContext;
- while (BidiContext* parent = baseContext->parent())
- baseContext = parent;
- BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
- trailingSpaceRun->m_stop = firstSpace;
- if (direction == LTR)
- bidiRuns.addRun(newTrailingRun);
- else
- bidiRuns.prependRun(newTrailingRun);
- trailingSpaceRun = newTrailingRun;
- return trailingSpaceRun;
- }
- if (!shouldReorder)
- return trailingSpaceRun;
- if (direction == LTR) {
- bidiRuns.moveRunToEnd(trailingSpaceRun);
- trailingSpaceRun->m_level = 0;
- } else {
- bidiRuns.moveRunToBeginning(trailingSpaceRun);
- trailingSpaceRun->m_level = 1;
- }
- return trailingSpaceRun;
- }
- void RenderBlock::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
- {
- ASSERT(!floatingObject->m_originatingLine);
- floatingObject->m_originatingLine = lastRootBox();
- lastRootBox()->appendFloat(floatingObject->renderer());
- }
- // This function constructs line boxes for all of the text runs in the resolver and computes their position.
- RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun)
- {
- if (!bidiRuns.runCount())
- return 0;
- // FIXME: Why is this only done when we had runs?
- lineInfo.setLastLine(!end.m_obj);
- RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
- if (!lineBox)
- return 0;
- lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
-
- #if ENABLE(SVG)
- bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
- #else
- bool isSVGRootInlineBox = false;
- #endif
-
- GlyphOverflowAndFallbackFontsMap textBoxDataMap;
-
- // Now we position all of our text runs horizontally.
- if (!isSVGRootInlineBox)
- computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache);
-
- // Now position our text runs vertically.
- computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
-
- #if ENABLE(SVG)
- // SVG text layout code computes vertical & horizontal positions on its own.
- // Note that we still need to execute computeVerticalPositionsForLine() as
- // it calls InlineTextBox::positionLineBox(), which tracks whether the box
- // contains reversed text or not. If we wouldn't do that editing and thus
- // text selection in RTL boxes would not work as expected.
- if (isSVGRootInlineBox) {
- ASSERT(isSVGText());
- static_cast<SVGRootInlineBox*>(lineBox)->computePerCharacterLayoutInformation();
- }
- #endif
-
- // Compute our overflow now.
- lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
-
- #if PLATFORM(MAC)
- // Highlight acts as an overflow inflation.
- if (style()->highlight() != nullAtom)
- lineBox->addHighlightOverflow();
- #endif
- return lineBox;
- }
- void RenderBlock::layoutRunsAndFloats(bool fullLayout, bool hasInlineChild, Vector<FloatWithRect>& floats, int& repaintLogicalTop, int& repaintLogicalBottom)
- {
- // We want to skip ahead to the first dirty line
- InlineBidiResolver resolver;
- unsigned floatIndex;
- LineInfo lineInfo;
- bool useRepaintBounds = false;
- RootInlineBox* startLine = determineStartPosition(lineInfo, fullLayout, resolver, floats, floatIndex,
- useRepaintBounds, repaintLogicalTop, repaintLogicalBottom);
- // FIXME: This would make more sense outside of this function, but since
- // determineStartPosition can change the fullLayout flag we have to do this here. Failure to call
- // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
- if (fullLayout && hasInlineChild && !selfNeedsLayout()) {
- setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like
- // we're supposed to.
- RenderView* v = view();
- if (v && !v->doingFullRepaint() && hasLayer()) {
- // Because we waited until we were already inside layout to discover
- // that the block really needed a full layout, we missed our chance to repaint the layer
- // before layout started. Luckily the layer has cached the repaint rect for its original
- // position and size, and so we can use that to make a repaint happen now.
- repaintUsingContainer(containerForRepaint(), layer()->repaintRect());
- }
- }
- FloatingObject* lastFloat = (m_floatingObjects && !m_floatingObjects->set().isEmpty()) ? m_floatingObjects->set().last() : 0;
- LineMidpointState& lineMidpointState = resolver.midpointState();
- // We also find the first clean line and extract these lines. We will add them back
- // if we determine that we're able to synchronize after handling all our dirty lines.
- InlineIterator cleanLineStart;
- BidiStatus cleanLineBidiStatus;
- int endLineLogicalTop = 0;
- RootInlineBox* endLine = (fullLayout || !startLine) ?
- 0 : determineEndPosition(startLine, floats, floatIndex, cleanLineStart, cleanLineBidiStatus, endLineLogicalTop);
- if (startLine) {
- if (!useRepaintBounds) {
- useRepaintBounds = true;
- repaintLogicalTop = logicalHeight();
- repaintLogicalBottom = logicalHeight();
- }
- RenderArena* arena = renderArena();
- RootInlineBox* box = startLine;
- while (box) {
- repaintLogicalTop = min(repaintLogicalTop, box->logicalTopVisualOverflow());
- repaintLogicalBottom = max(repaintLogicalBottom, box->logicalBottomVisualOverflow());
- RootInlineBox* next = box->nextRootBox();
- box->deleteLine(arena);
- box = next;
- }
- }
- InlineIterator end = resolver.position();
- if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) {
- // If the last line before the start line ends with a line break that clear floats,
- // adjust the height accordingly.
- // A line break can be either the first or the last object on a line, depending on its direction.
- if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
- RenderObject* lastObject = lastLeafChild->renderer();
- if (!lastObject->isBR())
- lastObject = lastRootBox()->firstLeafChild()->renderer();
- if (lastObject->isBR()) {
- EClear clear = lastObject->style()->clear();
- if (clear != CNONE)
- newLine(clear);
- }
- }
- }
- bool endLineMatched = false;
- bool checkForEndLineMatch = endLine;
- bool checkForFloatsFromLastLine = false;
- bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
- LineBreakIteratorInfo lineBreakIteratorInfo;
- VerticalPositionCache verticalPositionCache;
- LineBreaker lineBreaker(this);
- while (!end.atEnd()) {
- // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
- if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineLogicalTop, repaintLogicalBottom, repaintLogicalTop)))
- break;
- lineMidpointState.reset();
- lineInfo.setEmpty(true);
- InlineIterator oldEnd = end;
- FloatingObject* lastFloatFromPreviousLine = (m_floatingObjects && !m_floatingObjects->set().isEmpty()) ? m_floatingObjects->set().last() : 0;
- end = lineBreaker.nextLineBreak(resolver, lineInfo, lineBreakIteratorInfo, lastFloatFromPreviousLine);
- if (resolver.position().atEnd()) {
- // FIXME: We shouldn't be creating any runs in findNextLineBreak to begin with!
- // Once BidiRunList is separated from BidiResolver this will not be needed.
- resolver.runs().deleteRuns();
- resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
- checkForFloatsFromLastLine = true;
- break;
- }
- ASSERT(end != resolver.position());
- // This is a short-cut for empty lines.
- if (lineInfo.isEmpty()) {
- if (lastRootBox())
- lastRootBox()->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status());
- } else {
- VisualDirectionOverride override = (style()->visuallyOrdered() ? (style()->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
- // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
- BidiRunList<BidiRun>& bidiRuns = resolver.runs();
- resolver.createBidiRunsForLine(end, override, lineInfo.previousLineBrokeCleanly());
- ASSERT(resolver.position() == end);
- BidiRun* trailingSpaceRun = !lineInfo.previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0;
- if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated())
- bidiRuns.logicallyLastRun()->m_hasHyphen = true;
- // Now that the runs have been ordered, we create the line boxes.
- // At the same time we figure out where border/padding/margin should be applied for
- // inline flow boxes.
- int oldLogicalHeight = logicalHeight();
- RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, lineInfo, verticalPositionCache, trailingSpaceRun);
- bidiRuns.deleteRuns();
- resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
- if (lineBox) {
- lineBox->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status());
- if (useRepaintBounds) {
- repaintLogicalTop = min(repaintLogicalTop, lineBox->logicalTopVisualOverflow());
- repaintLogicalBottom = max(repaintLogicalBottom, lineBox->logicalBottomVisualOverflow());
- }
- if (paginated) {
- int adjustment = 0;
- adjustLinePositionForPagination(lineBox, adjustment);
- if (adjustment) {
- int oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, lineInfo.isFirstLine());
- lineBox->adjustBlockDirectionPosition(adjustment);
- if (useRepaintBounds) // This can only be a positive adjustment, so no need to update repaintTop.
- repaintLogicalBottom = max(repaintLogicalBottom, lineBox->logicalBottomVisualOverflow());
- if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, lineInfo.isFirstLine()) != oldLineWidth) {
- // We have to delete this line, remove all floats that got added, and let line layout re-run.
- lineBox->deleteLine(renderArena());
- removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
- setLogicalHeight(oldLogicalHeight + adjustment);
- resolver.setPosition(oldEnd);
- end = oldEnd;
- continue;
- }
- setLogicalHeight(lineBox->blockLogicalHeight());
- }
- }
- }
- for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
- setStaticPositions(this, lineBreaker.positionedObjects()[i]);
- lineInfo.setFirstLine(false);
- newLine(lineBreaker.clear());
- }
- if (m_floatingObjects && lastRootBox()) {
- FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
- FloatingObjectSetIterator it = floatingObjectSet.begin();
- FloatingObjectSetIterator end = floatingObjectSet.end();
- if (lastFloat) {
- FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(lastFloat);
- ASSERT(lastFloatIterator != end);
- ++lastFloatIterator;
- it = lastFloatIterator;
- }
- for (; it != end; ++it) {
- FloatingObject* f = *it;
- appendFloatingObjectToLastLine(f);
- ASSERT(f->m_renderer == floats[floatIndex].object);
- // If a float's geometry has changed, give up on syncing with clean lines.
- if (floats[floatIndex].rect != f->frameRect())
- checkForEndLineMatch = false;
- floatIndex++;
- }
- lastFloat = !floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0;
- }
- lineMidpointState.reset();
- resolver.setPosition(end);
- }
- if (endLine) {
- if (endLineMatched) {
- // Attach all the remaining lines, and then adjust their y-positions as needed.
- int delta = logicalHeight() - endLineLogicalTop;
- for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) {
- line->attachLine();
- if (paginated) {
- delta -= line->paginationStrut();
- adjustLinePositionForPagination(line, delta);
- }
- if (delta) {
- repaintLogicalTop = min(repaintLogicalTop, line->logicalTopVisualOverflow() + min(delta, 0));
- repaintLogicalBottom = max(repaintLogicalBottom, line->logicalBottomVisualOverflow() + max(delta, 0));
- line->adjustBlockDirectionPosition(delta);
- }
- if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
- Vector<RenderBox*>::iterator end = cleanLineFloats->end();
- for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
- FloatingObject* floatingObject = insertFloatingObject(*f);
- ASSERT(!floatingObject->m_originatingLine);
- floatingObject->m_originatingLine = line;
- setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
- positionNewFloats();
- }
- }
- }
- setLogicalHeight(lastRootBox()->blockLogicalHeight());
- } else {
- // Delete all the remaining lines.
- RootInlineBox* line = endLine;
- RenderArena* arena = renderArena();
- while (line) {
- repaintLogicalTop = min(repaintLogicalTop, line->logicalTopVisualOverflow());
- repaintLogicalBottom = max(repaintLogicalBottom, line->logicalBottomVisualOverflow());
- RootInlineBox* next = line->nextRootBox();
- line->deleteLine(arena);
- line = next;
- }
- }
- }
- if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) {
- // In case we have a float on the last line, it might not be positioned up to now.
- // This has to be done before adding in the bottom border/padding, or the float will
- // include the padding incorrectly. -dwh
- if (checkForFloatsFromLastLine) {
- int bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
- int bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
- TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this);
- m_lineBoxes.appendLineBox(trailingFloatsLineBox);
- trailingFloatsLineBox->setConstructed();
- GlyphOverflowAndFallbackFontsMap textBoxDataMap;
- VerticalPo…