This source file includes following definitions.
- firstRenderObjectForDirectionalityDetermination
- determinePlaintextDirectionality
- createInlineBoxForRenderer
- dirtyLineBoxesForRenderer
- parentIsConstructedOrHaveNext
- createLineBoxes
- endsWithASCIISpaces
- reachedEndOfTextRenderer
- constructLine
- textAlignmentForLine
- updateLogicalWidthForLeftAlignedBlock
- updateLogicalWidthForRightAlignedBlock
- updateLogicalWidthForCenterAlignedBlock
- setMarginsForRubyRun
- setLogicalWidthForTextRun
- computeExpansionForJustifiedText
- updateLogicalWidthForAlignment
- updateLogicalInlinePositions
- computeInlineDirectionPositionsForLine
- computeInlineDirectionPositionsForSegment
- computeBlockDirectionPositionsForLine
- appendFloatingObjectToLastLine
- statusWithDirection
- setupResolverToResumeInIsolate
- restoreIsolatedMidpointStates
- constructBidiRunsForLine
- createLineBoxesFromBidiRuns
- deleteLineRange
- layoutRunsAndFloats
- m_font
- restartLayoutRunsAndFloatsInRange
- layoutRunsAndFloatsInRange
- linkToEndLineIfNeeded
- repaintDirtyFloats
- next
- getBPMWidth
- getBorderPaddingMargin
- stripTrailingSpace
- updatePreferredWidth
- adjustFloatForSubPixelLayout
- computeInlinePreferredLogicalWidths
- layoutInlineChildren
- checkFloatsInCleanLine
- determineStartPosition
- determineEndPosition
- checkPaginationAndFloatsAtEndLine
- matchedEndLine
- generatesLineBoxesForInlineChild
- addOverflowFromInlineChildren
- deleteEllipsisLineBoxes
- checkLinesForTextOverflow
- positionNewFloatOnLine
- startAlignedOffsetForLine
#include "config.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/rendering/LayoutRectRecorder.h"
#include "core/rendering/RenderCounter.h"
#include "core/rendering/RenderFlowThread.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderListMarker.h"
#include "core/rendering/RenderRegion.h"
#include "core/rendering/RenderRubyRun.h"
#include "core/rendering/RenderView.h"
#include "core/rendering/TrailingFloatsRootInlineBox.h"
#include "core/rendering/VerticalPositionCache.h"
#include "core/rendering/line/BreakingContextInlineHeaders.h"
#include "core/rendering/line/LineLayoutState.h"
#include "core/rendering/line/LineWidth.h"
#include "core/rendering/svg/SVGRootInlineBox.h"
#include "platform/fonts/Character.h"
#include "platform/text/BidiResolver.h"
#include "wtf/RefCountedLeakCounter.h"
#include "wtf/StdLibExtras.h"
#include "wtf/Vector.h"
#include "wtf/unicode/CharacterNames.h"
namespace WebCore {
using namespace std;
using namespace WTF::Unicode;
static RenderObject* firstRenderObjectForDirectionalityDetermination(RenderObject* root, RenderObject* current = 0)
{
RenderObject* next = current;
while (current) {
if (isIsolated(current->style()->unicodeBidi())
&& (current->isRenderInline() || current->isRenderBlock())) {
if (current != root)
current = 0;
else
current = next;
break;
}
current = current->parent();
}
if (!current)
current = root->firstChild();
while (current) {
next = 0;
if (isIteratorTarget(current) && !(current->isText() && toRenderText(current)->isAllCollapsibleWhitespace()))
break;
if (!isIteratorTarget(current) && !isIsolated(current->style()->unicodeBidi()))
next = current->firstChild();
if (!next) {
while (current && current != root) {
next = current->nextSibling();
if (next)
break;
current = current->parent();
}
}
if (!next)
break;
current = next;
}
return current;
}
static TextDirection determinePlaintextDirectionality(RenderObject* root, RenderObject* current = 0, unsigned pos = 0)
{
InlineIterator iter(root, firstRenderObjectForDirectionalityDetermination(root, current), pos);
InlineBidiResolver observer;
observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi())));
observer.setPositionIgnoringNestedIsolates(iter);
return observer.determineParagraphDirectionality();
}
static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
{
if (isRootLineBox)
return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
if (obj->isText()) {
InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
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()) {
RenderText* renderText = toRenderText(o);
renderText->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* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment)
{
unsigned lineDepth = 1;
InlineFlowBox* parentBox = 0;
InlineFlowBox* result = 0;
bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
do {
ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox();
bool constructedNewBox = false;
bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox());
bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot;
if (allowedToConstructNewBox && !canUseExistingParentBox) {
InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
parentBox = toInlineFlowBox(newBox);
parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
parentBox->setIsHorizontal(isHorizontalWritingMode());
if (!hasDefaultLineBoxContain)
parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
constructedNewBox = true;
}
if (constructedNewBox || canUseExistingParentBox) {
if (!result)
result = parentBox;
if (childBox)
parentBox->addToLine(childBox);
if (!constructedNewBox || obj == this)
break;
childBox = parentBox;
}
obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
} while (true);
return result;
}
template <typename CharacterType>
static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
{
while (isASCIISpace(characters[pos])) {
pos++;
if (pos >= end)
return true;
}
return false;
}
static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
{
BidiRun* run = bidiRuns.logicallyLastRun();
if (!run)
return true;
unsigned pos = run->stop();
RenderObject* r = run->m_object;
if (!r->isText() || r->isBR())
return false;
RenderText* renderText = toRenderText(r);
unsigned length = renderText->textLength();
if (pos >= length)
return true;
if (renderText->is8Bit())
return endsWithASCIISpaces(renderText->characters8(), pos, length);
return endsWithASCIISpaces(renderText->characters16(), pos, length);
}
RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
{
ASSERT(bidiRuns.firstRun());
bool rootHasSelectedChildren = false;
InlineFlowBox* parentBox = 0;
int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
bool isOnlyRun = (runCount == 1);
if (runCount == 2 && !r->m_object->isListMarker())
isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
if (lineInfo.isEmpty())
continue;
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;
bool runStartsSegment = r->m_startsSegment;
if (!parentBox || parentBox->renderer() != r->m_object->parent() || runStartsSegment)
parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box, runStartsSegment);
else {
parentBox->addToLine(box);
}
bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder;
box->setBidiLevel(r->level());
if (box->isInlineTextBox()) {
InlineTextBox* text = toInlineTextBox(box);
text->setStart(r->m_start);
text->setLen(r->m_stop - r->m_start);
text->setDirOverride(r->dirOverride(visuallyOrdered));
if (r->m_hasHyphen)
text->setHasHyphen(true);
if (AXObjectCache* cache = document().existingAXObjectCache())
cache->inlineTextBoxesUpdated(r->m_object);
}
}
ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
if (rootHasSelectedChildren)
lastLineBox()->root().setHasSelectedChildren(true);
bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
lastLineBox()->setConstructed();
return lastRootBox();
}
ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
{
ETextAlign alignment = style()->textAlign();
if (endsWithSoftBreak)
return alignment;
if (!RuntimeEnabledFeatures::css3TextEnabled())
return (alignment == JUSTIFY) ? TASTART : alignment;
TextAlignLast alignmentLast = style()->textAlignLast();
switch (alignmentLast) {
case TextAlignLastStart:
return TASTART;
case TextAlignLastEnd:
return TAEND;
case TextAlignLastLeft:
return LEFT;
case TextAlignLastRight:
return RIGHT;
case TextAlignLastCenter:
return CENTER;
case TextAlignLastJustify:
return JUSTIFY;
case TextAlignLastAuto:
if (alignment != JUSTIFY)
return alignment;
if (style()->textJustify() == TextJustifyDistribute)
return JUSTIFY;
return TASTART;
}
return alignment;
}
static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
{
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)
{
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 RenderBlockFlow::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->isOutOfFlowPositioned() && !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, WordMeasurements& wordMeasurements)
{
HashSet<const SimpleFontData*> fallbackFonts;
GlyphOverflow glyphOverflow;
const Font& font = renderer->style(lineInfo.isFirstLine())->font();
if (lineBox->fitsToGlyphs()) {
bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
int boxAscent = font.fontMetrics().ascent() - baselineShift;
int boxDescent = font.fontMetrics().descent() + baselineShift;
if (boxAscent > rootDescent || boxDescent > rootAscent)
glyphOverflow.computeBounds = true;
}
LayoutUnit hyphenWidth = 0;
if (toInlineTextBox(run->m_box)->hasHyphen()) {
const Font& font = renderer->style(lineInfo.isFirstLine())->font();
hyphenWidth = measureHyphenWidth(renderer, font, run->direction());
}
float measuredWidth = 0;
bool kerningIsEnabled = font.fontDescription().typesettingFeatures() & Kerning;
#if OS(MACOSX)
bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath() && !font.fontDescription().featureSettings();
#else
bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
#endif
if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
int lastEndOffset = run->m_start;
for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
const WordMeasurement& wordMeasurement = wordMeasurements[i];
if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
continue;
if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
continue;
lastEndOffset = wordMeasurement.endOffset;
if (kerningIsEnabled && lastEndOffset == run->m_stop) {
int wordLength = lastEndOffset - wordMeasurement.startOffset;
measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, run->direction(), lineInfo.isFirstLine());
if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ')
measuredWidth += renderer->style()->wordSpacing();
} else
measuredWidth += wordMeasurement.width;
if (!wordMeasurement.fallbackFonts.isEmpty()) {
HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
fallbackFonts.add(*it);
}
}
if (measuredWidth && lastEndOffset != run->m_stop) {
measuredWidth = 0;
fallbackFonts.clear();
}
}
if (!measuredWidth)
measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, run->direction(), lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow);
run->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
if (!fallbackFonts.isEmpty()) {
ASSERT(run->m_box->isText());
GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
ASSERT(it->value.first.isEmpty());
copyToVector(fallbackFonts, it->value.first);
run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
}
if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
ASSERT(run->m_box->isText());
GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
it->value.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_startsSegment)
break;
if (!r->m_box || r == trailingSpaceRun)
continue;
if (r->m_object->isText()) {
unsigned opportunitiesInRun = expansionOpportunities[i++];
ASSERT(opportunitiesInRun <= expansionOpportunityCount);
if (r->m_object->style()->collapseWhiteSpace()) {
InlineTextBox* textBox = toInlineTextBox(r->m_box);
int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
textBox->setExpansion(expansion);
totalLogicalWidth += expansion;
}
expansionOpportunityCount -= opportunitiesInRun;
if (!expansionOpportunityCount)
break;
}
}
}
void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount)
{
TextDirection direction;
if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Plaintext)
direction = rootInlineBox->direction();
else
direction = style()->direction();
switch (textAlign) {
case LEFT:
case WEBKIT_LEFT:
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, 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 JUSTIFY:
adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
if (expansionOpportunityCount) {
if (trailingSpaceRun) {
totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
trailingSpaceRun->m_box->setLogicalWidth(0);
}
break;
}
case TASTART:
if (direction == LTR)
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case TAEND:
if (direction == LTR)
updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
}
}
static void updateLogicalInlinePositions(RenderBlockFlow* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
{
LayoutUnit lineLogicalHeight = block->minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
lineLogicalLeft = block->logicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
}
void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
{
ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this);
bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak();
IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style());
float lineLogicalLeft;
float lineLogicalRight;
float availableLogicalWidth;
updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
bool needsWordSpacing;
if (firstRun && firstRun->m_object->isReplaced()) {
RenderBox* renderBox = toRenderBox(firstRun->m_object);
updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight());
}
computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
needsWordSpacing = false;
lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap);
}
BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
WordMeasurements& wordMeasurements)
{
bool needsWordSpacing = false;
float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat();
unsigned expansionOpportunityCount = 0;
bool isAfterExpansion = true;
Vector<unsigned, 16> expansionOpportunities;
RenderObject* previousObject = 0;
TextJustify textJustify = style()->textJustify();
BidiRun* r = firstRun;
for (; r; r = r->next()) {
if (r->m_startsSegment)
break;
if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak())
continue;
if (r->m_object->isText()) {
RenderText* rt = toRenderText(r->m_object);
if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) {
if (!isAfterExpansion)
toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
unsigned opportunitiesInRun;
if (rt->is8Bit())
opportunitiesInRun = Character::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
else
opportunitiesInRun = Character::expansionOpportunityCount(rt->characters16() + 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->characterAt(r->m_start)))
totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().fontDescription().wordSpacing();
needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length;
}
setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
} 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).toFloat());
totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
}
}
totalLogicalWidth += r->m_box->logicalWidth();
previousObject = r->m_object;
}
if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
expansionOpportunities.last()--;
expansionOpportunityCount--;
}
updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
return r;
}
void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
VerticalPositionCache& verticalPositionCache)
{
setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
for (BidiRun* r = firstRun; r; r = r->next()) {
ASSERT(r->m_box);
if (!r->m_box)
continue;
if (r->m_object->isOutOfFlowPositioned())
r->m_box->setLogicalTop(logicalHeight().toFloat());
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);
}
lineBox->markDirty(false);
}
void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
{
ASSERT(!floatingObject->originatingLine());
floatingObject->setOriginatingLine(lastRootBox());
lastRootBox()->appendFloat(floatingObject->renderer());
}
static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride)
{
WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft;
RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
return BidiStatus(direction, direction, direction, context.release());
}
static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
{
if (root != startObject) {
RenderObject* parent = startObject->parent();
setupResolverToResumeInIsolate(resolver, root, parent);
notifyObserverEnteredObject(&resolver, startObject);
}
}
static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver, InlineBidiResolver& isolatedResolver)
{
while (!isolatedResolver.isolatedRuns().isEmpty()) {
BidiRun* run = isolatedResolver.isolatedRuns().last();
isolatedResolver.isolatedRuns().removeLast();
topResolver.setMidpointStateForIsolatedRun(run, isolatedResolver.midpointStateForIsolatedRun(run));
}
}
static inline void constructBidiRunsForLine(const RenderBlockFlow* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly, bool isNewUBAParagraph)
{
ASSERT(&topResolver.runs() == &bidiRuns);
ASSERT(topResolver.position() != endOfLine);
RenderObject* currentRoot = topResolver.position().root();
topResolver.createBidiRunsForLine(endOfLine, override, previousLineBrokeCleanly);
while (!topResolver.isolatedRuns().isEmpty()) {
BidiRun* isolatedRun = topResolver.isolatedRuns().last();
topResolver.isolatedRuns().removeLast();
RenderObject* startObj = isolatedRun->object();
RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObj, currentRoot));
ASSERT(isolatedInline);
InlineBidiResolver isolatedResolver;
LineMidpointState& isolatedLineMidpointState = isolatedResolver.midpointState();
isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun(isolatedRun);
EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
TextDirection direction;
if (unicodeBidi == Plaintext) {
if (isNewUBAParagraph)
direction = determinePlaintextDirectionality(isolatedInline, startObj);
else
direction = determinePlaintextDirectionality(isolatedInline);
} else {
ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
direction = isolatedInline->style()->direction();
}
isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi)));
setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj);
InlineIterator iter = InlineIterator(isolatedInline, startObj, isolatedRun->m_start);
isolatedResolver.setPositionIgnoringNestedIsolates(iter);
isolatedResolver.createBidiRunsForLine(endOfLine, NoVisualOverride, previousLineBrokeCleanly);
ASSERT(isolatedResolver.runs().runCount());
if (isolatedResolver.runs().runCount())
bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
if (!isolatedResolver.isolatedRuns().isEmpty()) {
topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns());
currentRoot = isolatedInline;
restoreIsolatedMidpointStates(topResolver, isolatedResolver);
}
}
}
RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
{
if (!bidiRuns.runCount())
return 0;
lineInfo.setLastLine(!end.object());
RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
if (!lineBox)
return 0;
lineBox->setBidiLevel(bidiLevel);
lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
if (!isSVGRootInlineBox)
computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements);
computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
if (isSVGRootInlineBox) {
ASSERT(isSVGText());
toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
}
lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
return lineBox;
}
static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
{
RootInlineBox* boxToDelete = startLine;
while (boxToDelete && boxToDelete != stopLine) {
layoutState.updateRepaintRangeFromBox(boxToDelete);
RootInlineBox* next = boxToDelete->nextRootBox();
boxToDelete->deleteLine();
boxToDelete = next;
}
}
void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState)
{
InlineBidiResolver resolver;
RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
unsigned consecutiveHyphenatedLines = 0;
if (startLine) {
for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox())
consecutiveHyphenatedLines++;
}
if (containsFloats())
layoutState.setLastFloat(m_floatingObjects->set().last());
InlineIterator cleanLineStart;
BidiStatus cleanLineBidiStatus;
if (!layoutState.isFullLayout() && startLine)
determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus);
if (startLine) {
if (!layoutState.usesRepaintBounds())
layoutState.setRepaintRange(logicalHeight());
deleteLineRange(layoutState, startLine);
}
if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
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)
clearFloats(clear);
}
}
}
layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines);
linkToEndLineIfNeeded(layoutState);
repaintDirtyFloats(layoutState.floats());
}
RenderTextInfo::RenderTextInfo()
: m_text(0)
, m_font(0)
{
}
RenderTextInfo::~RenderTextInfo()
{
}
inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
{
removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
setLogicalHeight(newLogicalHeight);
resolver.setPositionIgnoringNestedIsolates(oldEnd);
return oldEnd;
}
void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
{
RenderStyle* styleToUse = style();
bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
LineMidpointState& lineMidpointState = resolver.midpointState();
InlineIterator endOfLine = resolver.position();
bool checkForEndLineMatch = layoutState.endLine();
RenderTextInfo renderTextInfo;
VerticalPositionCache verticalPositionCache;
LineBreaker lineBreaker(this);
LayoutSize logicalOffsetFromShapeContainer;
while (!endOfLine.atEnd()) {
if (checkForEndLineMatch) {
layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
if (layoutState.endLineMatched()) {
resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
break;
}
}
lineMidpointState.reset();
layoutState.lineInfo().setEmpty(true);
layoutState.lineInfo().resetRunsFromLeadingWhitespace();
const InlineIterator previousEndofLine = endOfLine;
bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0;
WordMeasurements wordMeasurements;
endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
renderTextInfo.m_lineBreakIterator.resetPriorContext();
if (resolver.position().atEnd()) {
resolver.runs().deleteRuns();
resolver.markCurrentRunEmpty();
layoutState.setCheckForFloatsFromLastLine(true);
resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
break;
}
ASSERT(endOfLine != resolver.position());
if (layoutState.lineInfo().isEmpty()) {
if (lastRootBox())
lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
} else {
VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) {
TextDirection direction = determinePlaintextDirectionality(resolver.position().root(), resolver.position().object(), resolver.position().offset());
resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi())));
}
BidiRunList<BidiRun>& bidiRuns = resolver.runs();
constructBidiRunsForLine(this, resolver, bidiRuns, endOfLine, override, layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph);
ASSERT(resolver.position() == endOfLine);
BidiRun* trailingSpaceRun = resolver.trailingSpaceRun();
if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) {
bidiRuns.logicallyLastRun()->m_hasHyphen = true;
consecutiveHyphenatedLines++;
} else
consecutiveHyphenatedLines = 0;
LayoutUnit oldLogicalHeight = logicalHeight();
RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
bidiRuns.deleteRuns();
resolver.markCurrentRunEmpty();
if (lineBox) {
lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
if (layoutState.usesRepaintBounds())
layoutState.updateRepaintRangeFromBox(lineBox);
if (paginated) {
LayoutUnit adjustment = 0;
adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread());
if (adjustment) {
LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine());
lineBox->adjustBlockDirectionPosition(adjustment.toFloat());
if (layoutState.usesRepaintBounds())
layoutState.updateRepaintRangeFromBox(lineBox);
if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) {
lineBox->deleteLine();
endOfLine = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, previousEndofLine);
continue;
}
setLogicalHeight(lineBox->lineBottomWithLeading());
}
}
}
}
for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
setStaticPositions(this, lineBreaker.positionedObjects()[i]);
if (!layoutState.lineInfo().isEmpty()) {
layoutState.lineInfo().setFirstLine(false);
clearFloats(lineBreaker.clear());
}
if (m_floatingObjects && lastRootBox()) {
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
FloatingObjectSetIterator it = floatingObjectSet.begin();
FloatingObjectSetIterator end = floatingObjectSet.end();
if (layoutState.lastFloat()) {
FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it) {
FloatingObject* f = *it;
appendFloatingObjectToLastLine(f);
ASSERT(f->renderer() == layoutState.floats()[layoutState.floatIndex()].object);
if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
checkForEndLineMatch = false;
layoutState.setFloatIndex(layoutState.floatIndex() + 1);
}
layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
}
lineMidpointState.reset();
resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine));
}
if (paginated && !style()->hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
RootInlineBox* lineBox = lastRootBox();
RootInlineBox* firstLineInBlock = firstRootBox();
int numLinesHanging = 1;
while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
++numLinesHanging;
lineBox = lineBox->prevRootBox();
}
if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
return;
if (numLinesHanging < style()->widows()) {
int numLinesNeeded = style()->widows() - numLinesHanging;
RootInlineBox* currentFirstLineOfNewPage = lineBox;
lineBox = lineBox->prevRootBox();
int numLinesInPreviousPage = 1;
while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
++numLinesInPreviousPage;
lineBox = lineBox->prevRootBox();
}
int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans();
int numLinesAvailable = numLinesInPreviousPage - orphans;
if (numLinesAvailable <= 0)
return;
int numLinesToTake = min(numLinesAvailable, numLinesNeeded);
lineBox = currentFirstLineOfNewPage;
for (int i = 0; i < numLinesToTake; ++i)
lineBox = lineBox->prevRootBox();
setBreakAtLineToAvoidWidow(lineCount(lineBox));
markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
}
}
clearDidBreakAtLineToAvoidWidow();
}
void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
{
if (layoutState.endLine()) {
if (layoutState.endLineMatched()) {
bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop();
for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
line->attachLine();
if (paginated) {
delta -= line->paginationStrut();
adjustLinePositionForPagination(line, delta, layoutState.flowThread());
}
if (delta) {
layoutState.updateRepaintRangeFromBox(line, delta);
line->adjustBlockDirectionPosition(delta.toFloat());
}
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->originatingLine());
floatingObject->setOriginatingLine(line);
setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
positionNewFloats();
}
}
}
setLogicalHeight(lastRootBox()->lineBottomWithLeading());
} else {
deleteLineRange(layoutState, layoutState.endLine());
}
}
if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) {
if (layoutState.checkForFloatsFromLastLine()) {
LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
TrailingFloatsRootInlineBox* trailingFloatsLineBox = new TrailingFloatsRootInlineBox(*this);
m_lineBoxes.appendLineBox(trailingFloatsLineBox);
trailingFloatsLineBox->setConstructed();
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
VerticalPositionCache verticalPositionCache;
LayoutUnit blockLogicalHeight = logicalHeight();
trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache);
trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight);
trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent());
LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight);
LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
}
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
FloatingObjectSetIterator it = floatingObjectSet.begin();
FloatingObjectSetIterator end = floatingObjectSet.end();
if (layoutState.lastFloat()) {
FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it)
appendFloatingObjectToLastLine(*it);
layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
}
}
void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats)
{
size_t floatCount = floats.size();
for (size_t i = 0; i < floatCount; ++i) {
if (!floats[i].everHadLayout) {
RenderBox* f = floats[i].object;
if (!f->x() && !f->y() && f->checkForRepaint()) {
if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
f->setShouldDoFullRepaintAfterLayout(true);
else
f->repaint();
}
}
}
}
struct InlineMinMaxIterator {
RenderObject* parent;
RenderObject* current;
bool endOfInline;
InlineMinMaxIterator(RenderObject* p, bool end = false)
: parent(p), current(p), endOfInline(end)
{
}
RenderObject* next();
};
RenderObject* InlineMinMaxIterator::next()
{
RenderObject* result = 0;
bool oldEndOfInline = endOfInline;
endOfInline = false;
while (current || current == parent) {
if (!oldEndOfInline && (current == parent || (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned())))
result = current->firstChild();
if (!result) {
if (!oldEndOfInline && current->isRenderInline()) {
result = current;
endOfInline = true;
break;
}
while (current && current != parent) {
result = current->nextSibling();
if (result)
break;
current = current->parent();
if (current && current != parent && current->isRenderInline()) {
result = current;
endOfInline = true;
break;
}
}
}
if (!result)
break;
if (!result->isOutOfFlowPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline()))
break;
current = result;
result = 0;
}
current = result;
return current;
}
static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit)
{
if (cssUnit.type() != Auto)
return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : childValue);
return 0;
}
static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline)
{
RenderStyle* childStyle = child->style();
if (endOfInline) {
return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) +
getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) +
child->borderEnd();
}
return getBPMWidth(child->marginStart(), childStyle->marginStart()) +
getBPMWidth(child->paddingStart(), childStyle->paddingStart()) +
child->borderStart();
}
static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild)
{
if (trailingSpaceChild && trailingSpaceChild->isText()) {
RenderText* t = toRenderText(trailingSpaceChild);
const UChar space = ' ';
const Font& font = t->style()->font();
float spaceWidth = font.width(RenderBlockFlow::constructTextRun(t, font, &space, 1, t->style(), LTR));
inlineMax -= spaceWidth + font.fontDescription().wordSpacing();
if (inlineMin > inlineMax)
inlineMin = inlineMax;
}
}
static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result)
{
LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result);
preferredWidth = max(snappedResult, preferredWidth);
}
static inline LayoutUnit adjustFloatForSubPixelLayout(float value)
{
return LayoutUnit::fromFloatCeil(value);
}
void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth)
{
float inlineMax = 0;
float inlineMin = 0;
RenderStyle* styleToUse = style();
RenderBlock* containingBlock = this->containingBlock();
LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit();
bool stripFrontSpaces = true;
RenderObject* trailingSpaceChild = 0;
bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse->logicalWidth().isIntrinsicOrAuto();
bool autoWrap, oldAutoWrap;
autoWrap = oldAutoWrap = styleToUse->autoWrap();
InlineMinMaxIterator childIterator(this);
bool addedTextIndent = false;
bool hasRemainingNegativeTextIndent = false;
LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw);
RenderObject* prevFloat = 0;
bool isPrevChildInlineFlow = false;
bool shouldBreakLineAfterText = false;
while (RenderObject* child = childIterator.next()) {
autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
child->style()->autoWrap();
if (!child->isBR()) {
RenderStyle* childStyle = child->style();
float childMin = 0;
float childMax = 0;
if (!child->isText()) {
if (child->isRenderInline()) {
float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline).toFloat();
childMin += bpm;
childMax += bpm;
inlineMin += childMin;
inlineMax += childMax;
child->clearPreferredLogicalWidthsDirty();
} else {
LayoutUnit margins = 0;
Length startMargin = childStyle->marginStart();
Length endMargin = childStyle->marginEnd();
if (startMargin.isFixed())
margins += adjustFloatForSubPixelLayout(startMargin.value());
if (endMargin.isFixed())
margins += adjustFloatForSubPixelLayout(endMargin.value());
childMin += margins.ceilToFloat();
childMax += margins.ceilToFloat();
}
}
if (!child->isRenderInline() && !child->isText()) {
LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
RenderBox* childBox = toRenderBox(child);
LogicalExtentComputedValues computedValues;
childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
} else {
childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
}
childMin += childMinPreferredLogicalWidth.ceilToFloat();
childMax += childMaxPreferredLogicalWidth.ceilToFloat();
bool clearPreviousFloat;
if (child->isFloating()) {
clearPreviousFloat = (prevFloat
&& ((prevFloat->style()->floating() == LeftFloat && (childStyle->clear() & CLEFT))
|| (prevFloat->style()->floating() == RightFloat && (childStyle->clear() & CRIGHT))));
prevFloat = child;
} else {
clearPreviousFloat = false;
}
bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak;
if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = 0;
}
if (clearPreviousFloat) {
updatePreferredWidth(maxLogicalWidth, inlineMax);
inlineMax = 0;
}
if (!addedTextIndent && !child->isFloating()) {
float ceiledTextIndent = textIndent.ceilToFloat();
childMin += ceiledTextIndent;
childMax += ceiledTextIndent;
if (childMin < 0)
textIndent = adjustFloatForSubPixelLayout(childMin);
else
addedTextIndent = true;
}
inlineMax += max<float>(0, childMax);
if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) {
if (child->isFloating())
updatePreferredWidth(minLogicalWidth, childMin);
else
inlineMin += childMin;
} else {
updatePreferredWidth(minLogicalWidth, childMin);
inlineMin = 0;
}
if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = 0;
}
if (!child->isFloating()) {
stripFrontSpaces = false;
trailingSpaceChild = 0;
}
} else if (child->isText()) {
RenderText* t = toRenderText(child);
if (t->isWordBreak()) {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = 0;
continue;
}
if (t->style()->hasTextCombine() && t->isCombineText())
toRenderCombineText(t)->combineText();
bool hasBreakableChar, hasBreak;
float firstLineMinWidth, lastLineMinWidth;
bool hasBreakableStart, hasBreakableEnd;
float firstLineMaxWidth, lastLineMaxWidth;
t->trimmedPrefWidths(inlineMax,
firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd,
hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth,
childMin, childMax, stripFrontSpaces, styleToUse->direction());
if (!hasBreak && !childMax) {
if (autoWrap && (hasBreakableStart || hasBreakableEnd)) {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = 0;
}
continue;
}
if (stripFrontSpaces)
trailingSpaceChild = child;
else
trailingSpaceChild = 0;
float ti = 0;
if (!addedTextIndent || hasRemainingNegativeTextIndent) {
ti = textIndent.ceilToFloat();
childMin += ti;
firstLineMinWidth += ti;
if (!addedTextIndent) {
childMax += ti;
firstLineMaxWidth += ti;
addedTextIndent = true;
}
if (childMin < 0) {
textIndent = childMin;
hasRemainingNegativeTextIndent = true;
}
}
if (!hasBreakableChar) {
inlineMin += childMin;
} else {
if (hasBreakableStart) {
updatePreferredWidth(minLogicalWidth, inlineMin);
} else {
inlineMin += firstLineMinWidth;
updatePreferredWidth(minLogicalWidth, inlineMin);
childMin -= ti;
}
inlineMin = childMin;
if (hasBreakableEnd) {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = 0;
shouldBreakLineAfterText = false;
} else {
updatePreferredWidth(minLogicalWidth, inlineMin);
inlineMin = lastLineMinWidth;
shouldBreakLineAfterText = true;
}
}
if (hasBreak) {
inlineMax += firstLineMaxWidth;
updatePreferredWidth(maxLogicalWidth, inlineMax);
updatePreferredWidth(maxLogicalWidth, childMax);
inlineMax = lastLineMaxWidth;
addedTextIndent = true;
} else {
inlineMax += max<float>(0, childMax);
}
}
if (child->isListMarker())
stripFrontSpaces = true;
} else {
updatePreferredWidth(minLogicalWidth, inlineMin);
updatePreferredWidth(maxLogicalWidth, inlineMax);
inlineMin = inlineMax = 0;
stripFrontSpaces = true;
trailingSpaceChild = 0;
addedTextIndent = true;
}
if (!child->isText() && child->isRenderInline())
isPrevChildInlineFlow = true;
else
isPrevChildInlineFlow = false;
oldAutoWrap = autoWrap;
}
if (styleToUse->collapseWhiteSpace())
stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
updatePreferredWidth(minLogicalWidth, inlineMin);
updatePreferredWidth(maxLogicalWidth, inlineMax);
}
void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, LayoutUnit afterEdge)
{
RenderFlowThread* flowThread = flowThreadContainingBlock();
bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions();
bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
if (isFullLayout)
lineBoxes()->deleteLineBoxes();
bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
|| (isAnonymousBlock() && parent() && parent()->isRenderBlock() && toRenderBlock(parent())->canCollapseAnonymousBlockChild()
&& parent()->style()->textOverflow() && parent()->hasOverflowClip());
if (hasTextOverflow)
deleteEllipsisLineBoxes();
if (firstChild()) {
Vector<RenderBox*> replacedChildren;
for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
RenderObject* o = walker.current();
LayoutRectRecorder recorder(*o, o->isText());
if (!layoutState.hasInlineChild() && o->isInline())
layoutState.setHasInlineChild(true);
if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) {
RenderBox* box = toRenderBox(o);
updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box);
if (o->isOutOfFlowPositioned())
o->containingBlock()->insertPositionedObject(box);
else if (o->isFloating())
layoutState.floats().append(FloatWithRect(box));
else if (isFullLayout || o->needsLayout()) {
box->dirtyLineBoxes(isFullLayout);
if (isFullLayout)
replacedChildren.append(box);
else
o->layoutIfNeeded();
}
} else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
if (!o->isText())
toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
if (layoutState.isFullLayout() || o->selfNeedsLayout())
dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
o->clearNeedsLayout();
}
}
for (size_t i = 0; i < replacedChildren.size(); i++)
replacedChildren[i]->layoutIfNeeded();
layoutRunsAndFloats(layoutState);
}
int lastLineAnnotationsAdjustment = 0;
if (lastRootBox()) {
LayoutUnit lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
if (!style()->isFlippedLinesWritingMode())
lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
else
lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
}
setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge);
if (!firstLineBox() && hasLineIfEmpty())
setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
if (hasTextOverflow)
checkLinesForTextOverflow();
}
void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
{
Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
if (!cleanLineFloats)
return;
Vector<RenderBox*>::iterator end = cleanLineFloats->end();
for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
RenderBox* floatingBox = *it;
floatingBox->layoutIfNeeded();
LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight());
if (floats[floatIndex].object != floatingBox) {
encounteredNewFloat = true;
return;
}
if (floats[floatIndex].rect.size() != newSize) {
LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
LayoutUnit floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height())
: max(floats[floatIndex].rect.width(), newSize.width());
floatHeight = min(floatHeight, LayoutUnit::max() - floatTop);
line->markDirty();
markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
floats[floatIndex].rect.setSize(newSize);
dirtiedByFloat = true;
}
floatIndex++;
}
}
RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
{
RootInlineBox* curr = 0;
RootInlineBox* last = 0;
bool dirtiedByFloat = false;
if (!layoutState.isFullLayout()) {
bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
LayoutUnit paginationDelta = 0;
size_t floatIndex = 0;
for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
if (paginated) {
paginationDelta -= curr->paginationStrut();
adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
if (paginationDelta) {
if (containsFloats() || !layoutState.floats().isEmpty()) {
layoutState.markForFullLayout();
break;
}
layoutState.updateRepaintRangeFromBox(curr, paginationDelta);
curr->adjustBlockDirectionPosition(paginationDelta.toFloat());
}
}
bool encounteredNewFloat = false;
checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
if (encounteredNewFloat)
layoutState.markForFullLayout();
if (dirtiedByFloat || layoutState.isFullLayout())
break;
}
if (!curr && floatIndex < layoutState.floats().size())
layoutState.markForFullLayout();
}
if (layoutState.isFullLayout()) {
if (layoutState.hasInlineChild() && !selfNeedsLayout()) {
setNeedsLayout(MarkOnlyThis);
if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
setShouldDoFullRepaintAfterLayout(true);
}
curr = firstRootBox();
while (curr) {
RootInlineBox* next = curr->nextRootBox();
curr->deleteLine();
curr = next;
}
ASSERT(!firstLineBox() && !lastLineBox());
} else {
if (curr) {
if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
curr = prevRootBox;
}
} else {
if (lastRootBox() && !lastRootBox()->endsWithBreak())
curr = lastRootBox();
}
last = curr ? curr->prevRootBox() : lastRootBox();
}
unsigned numCleanFloats = 0;
if (!layoutState.floats().isEmpty()) {
LayoutUnit savedLogicalHeight = logicalHeight();
RootInlineBox* line = firstRootBox();
while (line != curr) {
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->originatingLine());
floatingObject->setOriginatingLine(line);
setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f));
positionNewFloats();
ASSERT(layoutState.floats()[numCleanFloats].object == *f);
numCleanFloats++;
}
}
line = line->nextRootBox();
}
setLogicalHeight(savedLogicalHeight);
}
layoutState.setFloatIndex(numCleanFloats);
layoutState.lineInfo().setFirstLine(!last);
layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
if (last) {
setLogicalHeight(last->lineBottomWithLeading());
InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
resolver.setStatus(last->lineBreakBidiStatus());
} else {
TextDirection direction = style()->direction();
if (style()->unicodeBidi() == Plaintext)
direction = determinePlaintextDirectionality(this);
resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi())));
InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, &resolver), 0);
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
}
return curr;
}
void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
{
ASSERT(!layoutState.endLine());
size_t floatIndex = layoutState.floatIndex();
RootInlineBox* last = 0;
for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
if (!curr->isDirty()) {
bool encounteredNewFloat = false;
bool dirtiedByFloat = false;
checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
if (encounteredNewFloat)
return;
}
if (curr->isDirty())
last = 0;
else if (!last)
last = curr;
}
if (!last)
return;
RootInlineBox* prev = last->prevRootBox();
cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
cleanLineBidiStatus = prev->lineBreakBidiStatus();
layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
for (RootInlineBox* line = last; line; line = line->nextRootBox())
line->extractLine();
layoutState.setEndLine(last);
}
bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
{
LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
if (paginated && layoutState.flowThread()) {
for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) {
if (paginated) {
LayoutUnit oldPaginationStrut = lineBox->paginationStrut();
lineDelta -= oldPaginationStrut;
adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread());
lineBox->setPaginationStrut(oldPaginationStrut);
}
}
}
if (!lineDelta || !m_floatingObjects)
return true;
LayoutUnit logicalTop = min(logicalHeight(), layoutState.endLineLogicalTop());
RootInlineBox* lastLine = layoutState.endLine();
while (RootInlineBox* nextLine = lastLine->nextRootBox())
lastLine = nextLine;
LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
FloatingObjectSetIterator end = floatingObjectSet.end();
for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
FloatingObject* floatingObject = *it;
if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
return false;
}
return true;
}
bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
{
if (resolver.position() == endLineStart) {
if (resolver.status() != endLineStatus)
return false;
return checkPaginationAndFloatsAtEndLine(layoutState);
}
static int numLines = 8;
RootInlineBox* originalEndLine = layoutState.endLine();
RootInlineBox* line = originalEndLine;
for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
if (line->lineBreakObj() == resolver.position().object() && line->lineBreakPos() == resolver.position().offset()) {
if (line->lineBreakBidiStatus() != resolver.status())
return false;
bool matched = false;
RootInlineBox* result = line->nextRootBox();
layoutState.setEndLine(result);
if (result) {
layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
matched = checkPaginationAndFloatsAtEndLine(layoutState);
}
deleteLineRange(layoutState, originalEndLine, result);
return matched;
}
}
return false;
}
bool RenderBlockFlow::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
{
ASSERT(inlineObj->parent() == this);
InlineIterator it(this, inlineObj, 0);
while (!it.atEnd() && !requiresLineBox(it))
it.increment();
return !it.atEnd();
}
void RenderBlockFlow::addOverflowFromInlineChildren()
{
LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection())
endPadding = 1;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
addContentsVisualOverflow(visualOverflow);
}
}
void RenderBlockFlow::deleteEllipsisLineBoxes()
{
ETextAlign textAlign = style()->textAlign();
bool ltr = style()->isLeftToRightDirection();
bool firstLine = true;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
if (curr->hasEllipsisBox()) {
curr->clearTruncation();
float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine).toFloat();
float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
float totalLogicalWidth = curr->logicalWidth();
updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
if (ltr)
curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
else
curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
}
firstLine = false;
}
}
void RenderBlockFlow::checkLinesForTextOverflow()
{
const Font& font = style()->font();
DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
const Font& firstLineFont = firstLineStyle()->font();
TextDirection ellipsisDirection = LTR;
int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle(), ellipsisDirection));
int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style(), ellipsisDirection));
bool ltr = style()->isLeftToRightDirection();
ETextAlign textAlign = style()->textAlign();
bool firstLine = true;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
float currLogicalLeft = curr->logicalLeft();
int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), currLogicalLeft);
int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
int lineBoxEdge = ltr ? snapSizeToPixel(currLogicalLeft + curr->logicalWidth(), currLogicalLeft) : snapSizeToPixel(currLogicalLeft, 0);
if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth;
LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width.toFloat());
float logicalLeft = 0;
float snappedLogicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine).toFloat();
float availableLogicalWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine) - snappedLogicalLeft;
updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
if (ltr)
curr->adjustLogicalPosition(logicalLeft, 0);
else
curr->adjustLogicalPosition(logicalLeft - (availableLogicalWidth - totalLogicalWidth), 0);
}
}
firstLine = false;
}
}
bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
{
if (!positionNewFloats())
return false;
width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat);
if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
return true;
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
ASSERT(floatingObjectSet.last() == newFloat);
LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
int paginationStrut = newFloat->paginationStrut();
if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
return true;
FloatingObjectSetIterator it = floatingObjectSet.end();
--it;
FloatingObjectSetIterator begin = floatingObjectSet.begin();
while (it != begin) {
--it;
FloatingObject* floatingObject = *it;
if (floatingObject == lastFloatFromPreviousLine)
break;
if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut());
RenderBox* floatBox = floatingObject->renderer();
setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
if (floatBox->isRenderBlock())
floatBox->forceChildLayout();
else
floatBox->layoutIfNeeded();
LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
m_floatingObjects->removePlacedObject(floatingObject);
setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
m_floatingObjects->addPlacedObject(floatingObject);
}
}
lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut);
return true;
}
LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
{
ETextAlign textAlign = style()->textAlign();
if (textAlign == TASTART)
return startOffsetForLine(position, firstLine);
float totalLogicalWidth = 0;
float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false).toFloat();
float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
if (!style()->isLeftToRightDirection())
return logicalWidth() - logicalLeft;
return logicalLeft;
}
}