1 /*
2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
4 * Copyright (C) 2010 Google Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24
25 #include "core/accessibility/AXObjectCache.h"
26 #include "core/rendering/BidiRunForLine.h"
27 #include "core/rendering/RenderCounter.h"
28 #include "core/rendering/RenderFlowThread.h"
29 #include "core/rendering/RenderLayer.h"
30 #include "core/rendering/RenderListMarker.h"
31 #include "core/rendering/RenderObjectInlines.h"
32 #include "core/rendering/RenderRegion.h"
33 #include "core/rendering/RenderRubyRun.h"
34 #include "core/rendering/RenderView.h"
35 #include "core/rendering/TextRunConstructor.h"
36 #include "core/rendering/TrailingFloatsRootInlineBox.h"
37 #include "core/rendering/VerticalPositionCache.h"
38 #include "core/rendering/line/BreakingContextInlineHeaders.h"
39 #include "core/rendering/line/LineLayoutState.h"
40 #include "core/rendering/line/LineWidth.h"
41 #include "core/rendering/line/RenderTextInfo.h"
42 #include "core/rendering/line/WordMeasurement.h"
43 #include "core/rendering/svg/SVGRootInlineBox.h"
44 #include "platform/fonts/Character.h"
45 #include "platform/text/BidiResolver.h"
46 #include "wtf/RefCountedLeakCounter.h"
47 #include "wtf/StdLibExtras.h"
48 #include "wtf/Vector.h"
49 #include "wtf/unicode/CharacterNames.h"
50
51 namespace blink {
52
53 using namespace WTF::Unicode;
54
createInlineBoxForRenderer(RenderObject * obj,bool isRootLineBox,bool isOnlyRun=false)55 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
56 {
57 if (isRootLineBox)
58 return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
59
60 if (obj->isText()) {
61 InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
62 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
63 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.)
64 if (obj->isBR())
65 textBox->setIsText(isOnlyRun || obj->document().inNoQuirksMode());
66 return textBox;
67 }
68
69 if (obj->isBox())
70 return toRenderBox(obj)->createInlineBox();
71
72 return toRenderInline(obj)->createAndAppendInlineFlowBox();
73 }
74
dirtyLineBoxesForRenderer(RenderObject * o,bool fullLayout)75 static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
76 {
77 if (o->isText()) {
78 RenderText* renderText = toRenderText(o);
79 renderText->dirtyLineBoxes(fullLayout);
80 } else
81 toRenderInline(o)->dirtyLineBoxes(fullLayout);
82 }
83
parentIsConstructedOrHaveNext(InlineFlowBox * parentBox)84 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
85 {
86 do {
87 if (parentBox->isConstructed() || parentBox->nextOnLine())
88 return true;
89 parentBox = parentBox->parent();
90 } while (parentBox);
91 return false;
92 }
93
createLineBoxes(RenderObject * obj,const LineInfo & lineInfo,InlineBox * childBox)94 InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox)
95 {
96 // See if we have an unconstructed line box for this object that is also
97 // the last item on the line.
98 unsigned lineDepth = 1;
99 InlineFlowBox* parentBox = 0;
100 InlineFlowBox* result = 0;
101 bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
102 do {
103 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
104
105 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
106
107 // Get the last box we made for this render object.
108 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox();
109
110 // If this box or its ancestor is constructed then it is from a previous line, and we need
111 // to make a new box for our line. If this box or its ancestor is unconstructed but it has
112 // something following it on the line, then we know we have to make a new box
113 // as well. In this situation our inline has actually been split in two on
114 // the same line (this can happen with very fancy language mixtures).
115 bool constructedNewBox = false;
116 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
117 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox);
118 if (allowedToConstructNewBox && !canUseExistingParentBox) {
119 // We need to make a new box for this render object. Once
120 // made, we need to place it at the end of the current line.
121 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
122 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
123 parentBox = toInlineFlowBox(newBox);
124 parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
125 parentBox->setIsHorizontal(isHorizontalWritingMode());
126 if (!hasDefaultLineBoxContain)
127 parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
128 constructedNewBox = true;
129 }
130
131 if (constructedNewBox || canUseExistingParentBox) {
132 if (!result)
133 result = parentBox;
134
135 // If we have hit the block itself, then |box| represents the root
136 // inline box for the line, and it doesn't have to be appended to any parent
137 // inline.
138 if (childBox)
139 parentBox->addToLine(childBox);
140
141 if (!constructedNewBox || obj == this)
142 break;
143
144 childBox = parentBox;
145 }
146
147 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
148 // intermediate inline flows.
149 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
150
151 } while (true);
152
153 return result;
154 }
155
156 template <typename CharacterType>
endsWithASCIISpaces(const CharacterType * characters,unsigned pos,unsigned end)157 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
158 {
159 while (isASCIISpace(characters[pos])) {
160 pos++;
161 if (pos >= end)
162 return true;
163 }
164 return false;
165 }
166
reachedEndOfTextRenderer(const BidiRunList<BidiRun> & bidiRuns)167 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
168 {
169 BidiRun* run = bidiRuns.logicallyLastRun();
170 if (!run)
171 return true;
172 unsigned pos = run->stop();
173 RenderObject* r = run->m_object;
174 if (!r->isText() || r->isBR())
175 return false;
176 RenderText* renderText = toRenderText(r);
177 unsigned length = renderText->textLength();
178 if (pos >= length)
179 return true;
180
181 if (renderText->is8Bit())
182 return endsWithASCIISpaces(renderText->characters8(), pos, length);
183 return endsWithASCIISpaces(renderText->characters16(), pos, length);
184 }
185
constructLine(BidiRunList<BidiRun> & bidiRuns,const LineInfo & lineInfo)186 RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
187 {
188 ASSERT(bidiRuns.firstRun());
189
190 bool rootHasSelectedChildren = false;
191 InlineFlowBox* parentBox = 0;
192 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
193 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
194 // Create a box for our object.
195 bool isOnlyRun = (runCount == 1);
196 if (runCount == 2 && !r->m_object->isListMarker())
197 isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
198
199 if (lineInfo.isEmpty())
200 continue;
201
202 InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun);
203 r->m_box = box;
204
205 ASSERT(box);
206 if (!box)
207 continue;
208
209 if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone)
210 rootHasSelectedChildren = true;
211
212 // If we have no parent box yet, or if the run is not simply a sibling,
213 // then we need to construct inline boxes as necessary to properly enclose the
214 // run's inline box. Segments can only be siblings at the root level, as
215 // they are positioned separately.
216 if (!parentBox || parentBox->renderer() != r->m_object->parent()) {
217 // Create new inline boxes all the way back to the appropriate insertion point.
218 parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box);
219 } else {
220 // Append the inline box to this line.
221 parentBox->addToLine(box);
222 }
223
224 bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder;
225 box->setBidiLevel(r->level());
226
227 if (box->isInlineTextBox()) {
228 InlineTextBox* text = toInlineTextBox(box);
229 text->setStart(r->m_start);
230 text->setLen(r->m_stop - r->m_start);
231 text->setDirOverride(r->dirOverride(visuallyOrdered));
232 if (r->m_hasHyphen)
233 text->setHasHyphen(true);
234
235 if (AXObjectCache* cache = document().existingAXObjectCache())
236 cache->inlineTextBoxesUpdated(r->m_object);
237 }
238 }
239
240 // We should have a root inline box. It should be unconstructed and
241 // be the last continuation of our line list.
242 ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
243
244 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
245 // from the bidi runs walk above has a selection state.
246 if (rootHasSelectedChildren)
247 lastLineBox()->root().setHasSelectedChildren(true);
248
249 // Set bits on our inline flow boxes that indicate which sides should
250 // paint borders/margins/padding. This knowledge will ultimately be used when
251 // we determine the horizontal positions and widths of all the inline boxes on
252 // the line.
253 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
254 lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
255
256 // Now mark the line boxes as being constructed.
257 lastLineBox()->setConstructed();
258
259 // Return the last line.
260 return lastRootBox();
261 }
262
textAlignmentForLine(bool endsWithSoftBreak) const263 ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
264 {
265 ETextAlign alignment = style()->textAlign();
266 if (endsWithSoftBreak)
267 return alignment;
268
269 if (!RuntimeEnabledFeatures::css3TextEnabled())
270 return (alignment == JUSTIFY) ? TASTART : alignment;
271
272 if (alignment != JUSTIFY)
273 return alignment;
274
275 TextAlignLast alignmentLast = style()->textAlignLast();
276 switch (alignmentLast) {
277 case TextAlignLastStart:
278 return TASTART;
279 case TextAlignLastEnd:
280 return TAEND;
281 case TextAlignLastLeft:
282 return LEFT;
283 case TextAlignLastRight:
284 return RIGHT;
285 case TextAlignLastCenter:
286 return CENTER;
287 case TextAlignLastJustify:
288 return JUSTIFY;
289 case TextAlignLastAuto:
290 if (style()->textJustify() == TextJustifyDistribute)
291 return JUSTIFY;
292 return TASTART;
293 }
294
295 return alignment;
296 }
297
updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)298 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
299 {
300 // The direction of the block should determine what happens with wide lines.
301 // In particular with RTL blocks, wide lines should still spill out to the left.
302 if (isLeftToRightDirection) {
303 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
304 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
305 return;
306 }
307
308 if (trailingSpaceRun)
309 trailingSpaceRun->m_box->setLogicalWidth(0);
310 else if (totalLogicalWidth > availableLogicalWidth)
311 logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
312 }
313
updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)314 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
315 {
316 // Wide lines spill out of the block based off direction.
317 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
318 // side of the block.
319 if (isLeftToRightDirection) {
320 if (trailingSpaceRun) {
321 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
322 trailingSpaceRun->m_box->setLogicalWidth(0);
323 }
324 if (totalLogicalWidth < availableLogicalWidth)
325 logicalLeft += availableLogicalWidth - totalLogicalWidth;
326 return;
327 }
328
329 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
330 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
331 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
332 } else
333 logicalLeft += availableLogicalWidth - totalLogicalWidth;
334 }
335
updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)336 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
337 {
338 float trailingSpaceWidth = 0;
339 if (trailingSpaceRun) {
340 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
341 trailingSpaceWidth = std::min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
342 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpaceWidth));
343 }
344 if (isLeftToRightDirection)
345 logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
346 else
347 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
348 }
349
setMarginsForRubyRun(BidiRun * run,RenderRubyRun * renderer,RenderObject * previousObject,const LineInfo & lineInfo)350 void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo)
351 {
352 int startOverhang;
353 int endOverhang;
354 RenderObject* nextObject = 0;
355 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
356 if (!runWithNextObject->m_object->isOutOfFlowPositioned() && !runWithNextObject->m_box->isLineBreak()) {
357 nextObject = runWithNextObject->m_object;
358 break;
359 }
360 }
361 renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
362 setMarginStartForChild(renderer, -startOverhang);
363 setMarginEndForChild(renderer, -endOverhang);
364 }
365
setLogicalWidthForTextRun(RootInlineBox * lineBox,BidiRun * run,RenderText * renderer,float xPos,const LineInfo & lineInfo,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)366 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo,
367 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
368 {
369 HashSet<const SimpleFontData*> fallbackFonts;
370 GlyphOverflow glyphOverflow;
371
372 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
373 // Always compute glyph overflow if the block's line-box-contain value is "glyphs".
374 if (lineBox->fitsToGlyphs()) {
375 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
376 // will keep us from computing glyph bounds in nearly all cases.
377 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
378 int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
379 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
380 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
381 int boxAscent = font.fontMetrics().ascent() - baselineShift;
382 int boxDescent = font.fontMetrics().descent() + baselineShift;
383 if (boxAscent > rootDescent || boxDescent > rootAscent)
384 glyphOverflow.computeBounds = true;
385 }
386
387 LayoutUnit hyphenWidth = 0;
388 if (toInlineTextBox(run->m_box)->hasHyphen()) {
389 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
390 hyphenWidth = measureHyphenWidth(renderer, font, run->direction());
391 }
392 float measuredWidth = 0;
393
394 bool kerningIsEnabled = font.fontDescription().typesettingFeatures() & Kerning;
395
396 #if OS(MACOSX)
397 // FIXME: Having any font feature settings enabled can lead to selection gaps on
398 // Chromium-mac. https://bugs.webkit.org/show_bug.cgi?id=113418
399 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath() && !font.fontDescription().featureSettings();
400 #else
401 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
402 #endif
403
404 // Since we don't cache glyph overflows, we need to re-measure the run if
405 // the style is linebox-contain: glyph.
406
407 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
408 int lastEndOffset = run->m_start;
409 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
410 const WordMeasurement& wordMeasurement = wordMeasurements[i];
411 if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
412 continue;
413 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
414 continue;
415
416 lastEndOffset = wordMeasurement.endOffset;
417 if (kerningIsEnabled && lastEndOffset == run->m_stop) {
418 int wordLength = lastEndOffset - wordMeasurement.startOffset;
419 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, run->direction(), lineInfo.isFirstLine());
420 if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ')
421 measuredWidth += renderer->style()->wordSpacing();
422 } else
423 measuredWidth += wordMeasurement.width;
424 if (!wordMeasurement.fallbackFonts.isEmpty()) {
425 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
426 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
427 fallbackFonts.add(*it);
428 }
429 }
430 if (measuredWidth && lastEndOffset != run->m_stop) {
431 // If we don't have enough cached data, we'll measure the run again.
432 measuredWidth = 0;
433 fallbackFonts.clear();
434 }
435 }
436
437 if (!measuredWidth)
438 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, run->direction(), lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow);
439
440 run->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
441 if (!fallbackFonts.isEmpty()) {
442 ASSERT(run->m_box->isText());
443 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
444 ASSERT(it->value.first.isEmpty());
445 copyToVector(fallbackFonts, it->value.first);
446 run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
447 }
448 if (!glyphOverflow.isZero()) {
449 ASSERT(run->m_box->isText());
450 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toInlineTextBox(run->m_box), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).storedValue;
451 it->value.second = glyphOverflow;
452 run->m_box->clearKnownToHaveNoOverflow();
453 }
454 }
455
computeExpansionForJustifiedText(BidiRun * firstRun,BidiRun * trailingSpaceRun,Vector<unsigned,16> & expansionOpportunities,unsigned expansionOpportunityCount,float & totalLogicalWidth,float availableLogicalWidth)456 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
457 {
458 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
459 return;
460
461 size_t i = 0;
462 for (BidiRun* r = firstRun; r; r = r->next()) {
463 if (!r->m_box || r == trailingSpaceRun)
464 continue;
465
466 if (r->m_object->isText()) {
467 unsigned opportunitiesInRun = expansionOpportunities[i++];
468
469 ASSERT(opportunitiesInRun <= expansionOpportunityCount);
470
471 // Don't justify for white-space: pre.
472 if (r->m_object->style()->whiteSpace() != PRE) {
473 InlineTextBox* textBox = toInlineTextBox(r->m_box);
474 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
475 textBox->setExpansion(expansion);
476 totalLogicalWidth += expansion;
477 }
478 expansionOpportunityCount -= opportunitiesInRun;
479 if (!expansionOpportunityCount)
480 break;
481 }
482 }
483 }
484
updateLogicalWidthForAlignment(const ETextAlign & textAlign,const RootInlineBox * rootInlineBox,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float & availableLogicalWidth,unsigned expansionOpportunityCount)485 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionOpportunityCount)
486 {
487 TextDirection direction;
488 if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Plaintext)
489 direction = rootInlineBox->direction();
490 else
491 direction = style()->direction();
492
493 // Armed with the total width of the line (without justification),
494 // we now examine our text-align property in order to determine where to position the
495 // objects horizontally. The total width of the line can be increased if we end up
496 // justifying text.
497 switch (textAlign) {
498 case LEFT:
499 case WEBKIT_LEFT:
500 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
501 break;
502 case RIGHT:
503 case WEBKIT_RIGHT:
504 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
505 break;
506 case CENTER:
507 case WEBKIT_CENTER:
508 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
509 break;
510 case JUSTIFY:
511 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
512 if (expansionOpportunityCount) {
513 if (trailingSpaceRun) {
514 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
515 trailingSpaceRun->m_box->setLogicalWidth(0);
516 }
517 break;
518 }
519 // Fall through
520 case TASTART:
521 if (direction == LTR)
522 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
523 else
524 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
525 break;
526 case TAEND:
527 if (direction == LTR)
528 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
529 else
530 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
531 break;
532 }
533 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
534 logicalLeft += verticalScrollbarWidth();
535 }
536
updateLogicalInlinePositions(RenderBlockFlow * block,float & lineLogicalLeft,float & lineLogicalRight,float & availableLogicalWidth,bool firstLine,IndentTextOrNot shouldIndentText,LayoutUnit boxLogicalHeight)537 static void updateLogicalInlinePositions(RenderBlockFlow* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
538 {
539 LayoutUnit lineLogicalHeight = block->minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
540 lineLogicalLeft = block->logicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
541 lineLogicalRight = block->logicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight).toFloat();
542 availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
543 }
544
computeInlineDirectionPositionsForLine(RootInlineBox * lineBox,const LineInfo & lineInfo,BidiRun * firstRun,BidiRun * trailingSpaceRun,bool reachedEnd,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)545 void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
546 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
547 {
548 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
549
550 // CSS 2.1: "'Text-indent' only affects a line if it is the first formatted line of an element. For example, the first line of an anonymous block
551 // box is only affected if it is the first child of its parent element."
552 // CSS3 "text-indent", "each-line" affects the first line of the block container as well as each line after a forced line break,
553 // but does not affect lines after a soft wrap break.
554 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->slowFirstChild() != this);
555 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak();
556 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style());
557 float lineLogicalLeft;
558 float lineLogicalRight;
559 float availableLogicalWidth;
560 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
561 bool needsWordSpacing;
562
563 if (firstRun && firstRun->m_object->isReplaced()) {
564 RenderBox* renderBox = toRenderBox(firstRun->m_object);
565 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight());
566 }
567
568 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
569 // The widths of all runs are now known. We can now place every inline box (and
570 // compute accurate widths for the inline flow boxes).
571 needsWordSpacing = lineBox->isLeftToRightDirection() ? false: true;
572 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing);
573 }
574
computeInlineDirectionPositionsForSegment(RootInlineBox * lineBox,const LineInfo & lineInfo,ETextAlign textAlign,float & logicalLeft,float & availableLogicalWidth,BidiRun * firstRun,BidiRun * trailingSpaceRun,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)575 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
576 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
577 WordMeasurements& wordMeasurements)
578 {
579 bool needsWordSpacing = true;
580 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat();
581 unsigned expansionOpportunityCount = 0;
582 bool isAfterExpansion = true;
583 Vector<unsigned, 16> expansionOpportunities;
584 RenderObject* previousObject = 0;
585 TextJustify textJustify = style()->textJustify();
586
587 BidiRun* r = firstRun;
588 for (; r; r = r->next()) {
589 if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak())
590 continue; // Positioned objects are only participating to figure out their
591 // correct static x position. They have no effect on the width.
592 // Similarly, line break boxes have no effect on the width.
593 if (r->m_object->isText()) {
594 RenderText* rt = toRenderText(r->m_object);
595 if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) {
596 if (!isAfterExpansion)
597 toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
598 unsigned opportunitiesInRun;
599 if (rt->is8Bit())
600 opportunitiesInRun = Character::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
601 else
602 opportunitiesInRun = Character::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
603 expansionOpportunities.append(opportunitiesInRun);
604 expansionOpportunityCount += opportunitiesInRun;
605 }
606
607 if (rt->textLength()) {
608 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start)))
609 totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().fontDescription().wordSpacing();
610 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1));
611 }
612
613 setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
614 } else {
615 isAfterExpansion = false;
616 if (!r->m_object->isRenderInline()) {
617 RenderBox* renderBox = toRenderBox(r->m_object);
618 if (renderBox->isRubyRun())
619 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
620 r->m_box->setLogicalWidth(logicalWidthForChild(renderBox).toFloat());
621 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
622 }
623 }
624
625 totalLogicalWidth += r->m_box->logicalWidth();
626 previousObject = r->m_object;
627 }
628
629 if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
630 expansionOpportunities.last()--;
631 expansionOpportunityCount--;
632 }
633
634 updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
635
636 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
637
638 return r;
639 }
640
computeBlockDirectionPositionsForLine(RootInlineBox * lineBox,BidiRun * firstRun,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache)641 void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
642 VerticalPositionCache& verticalPositionCache)
643 {
644 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
645
646 // Now make sure we place replaced render objects correctly.
647 for (BidiRun* r = firstRun; r; r = r->next()) {
648 ASSERT(r->m_box);
649 if (!r->m_box)
650 continue; // Skip runs with no line boxes.
651
652 // Align positioned boxes with the top of the line box. This is
653 // a reasonable approximation of an appropriate y position.
654 if (r->m_object->isOutOfFlowPositioned())
655 r->m_box->setLogicalTop(logicalHeight().toFloat());
656
657 // Position is used to properly position both replaced elements and
658 // to update the static normal flow x/y of positioned elements.
659 if (r->m_object->isText())
660 toRenderText(r->m_object)->positionLineBox(r->m_box);
661 else if (r->m_object->isBox())
662 toRenderBox(r->m_object)->positionLineBox(r->m_box);
663 }
664 }
665
appendFloatingObjectToLastLine(FloatingObject * floatingObject)666 void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
667 {
668 ASSERT(!floatingObject->originatingLine());
669 floatingObject->setOriginatingLine(lastRootBox());
670 lastRootBox()->appendFloat(floatingObject->renderer());
671 }
672
673 // This function constructs line boxes for all of the text runs in the resolver and computes their position.
createLineBoxesFromBidiRuns(unsigned bidiLevel,BidiRunList<BidiRun> & bidiRuns,const InlineIterator & end,LineInfo & lineInfo,VerticalPositionCache & verticalPositionCache,BidiRun * trailingSpaceRun,WordMeasurements & wordMeasurements)674 RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
675 {
676 if (!bidiRuns.runCount())
677 return 0;
678
679 // FIXME: Why is this only done when we had runs?
680 lineInfo.setLastLine(!end.object());
681
682 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
683 if (!lineBox)
684 return 0;
685
686 lineBox->setBidiLevel(bidiLevel);
687 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
688
689 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
690
691 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
692
693 // Now we position all of our text runs horizontally.
694 if (!isSVGRootInlineBox)
695 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements);
696
697 // Now position our text runs vertically.
698 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
699
700 // SVG text layout code computes vertical & horizontal positions on its own.
701 // Note that we still need to execute computeVerticalPositionsForLine() as
702 // it calls InlineTextBox::positionLineBox(), which tracks whether the box
703 // contains reversed text or not. If we wouldn't do that editing and thus
704 // text selection in RTL boxes would not work as expected.
705 if (isSVGRootInlineBox) {
706 ASSERT(isSVGText());
707 toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
708 }
709
710 // Compute our overflow now.
711 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
712
713 return lineBox;
714 }
715
deleteLineRange(LineLayoutState & layoutState,RootInlineBox * startLine,RootInlineBox * stopLine=0)716 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
717 {
718 RootInlineBox* boxToDelete = startLine;
719 while (boxToDelete && boxToDelete != stopLine) {
720 layoutState.updatePaintInvalidationRangeFromBox(boxToDelete);
721 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree().
722 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing.
723 RootInlineBox* next = boxToDelete->nextRootBox();
724 boxToDelete->deleteLine();
725 boxToDelete = next;
726 }
727 }
728
layoutRunsAndFloats(LineLayoutState & layoutState)729 void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState)
730 {
731 // We want to skip ahead to the first dirty line
732 InlineBidiResolver resolver;
733 RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
734
735 if (containsFloats())
736 layoutState.setLastFloat(m_floatingObjects->set().last().get());
737
738 // We also find the first clean line and extract these lines. We will add them back
739 // if we determine that we're able to synchronize after handling all our dirty lines.
740 InlineIterator cleanLineStart;
741 BidiStatus cleanLineBidiStatus;
742 if (!layoutState.isFullLayout() && startLine)
743 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus);
744
745 if (startLine) {
746 if (!layoutState.usesPaintInvalidationBounds())
747 layoutState.setPaintInvalidationRange(logicalHeight());
748 deleteLineRange(layoutState, startLine);
749 }
750
751 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
752 // If the last line before the start line ends with a line break that clear floats,
753 // adjust the height accordingly.
754 // A line break can be either the first or the last object on a line, depending on its direction.
755 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
756 RenderObject* lastObject = &lastLeafChild->renderer();
757 if (!lastObject->isBR())
758 lastObject = &lastRootBox()->firstLeafChild()->renderer();
759 if (lastObject->isBR()) {
760 EClear clear = lastObject->style()->clear();
761 if (clear != CNONE)
762 clearFloats(clear);
763 }
764 }
765 }
766
767 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus);
768 linkToEndLineIfNeeded(layoutState);
769 markDirtyFloatsForPaintInvalidation(layoutState.floats());
770 }
771
772 // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight,LayoutUnit newLogicalHeight,FloatingObject * lastFloatFromPreviousLine,InlineBidiResolver & resolver,const InlineIterator & oldEnd)773 inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
774 {
775 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
776 setLogicalHeight(newLogicalHeight);
777 resolver.setPositionIgnoringNestedIsolates(oldEnd);
778 return oldEnd;
779 }
780
layoutRunsAndFloatsInRange(LineLayoutState & layoutState,InlineBidiResolver & resolver,const InlineIterator & cleanLineStart,const BidiStatus & cleanLineBidiStatus)781 void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState,
782 InlineBidiResolver& resolver, const InlineIterator& cleanLineStart,
783 const BidiStatus& cleanLineBidiStatus)
784 {
785 RenderStyle* styleToUse = style();
786 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
787 LineMidpointState& lineMidpointState = resolver.midpointState();
788 InlineIterator endOfLine = resolver.position();
789 bool checkForEndLineMatch = layoutState.endLine();
790 RenderTextInfo renderTextInfo;
791 VerticalPositionCache verticalPositionCache;
792
793 LineBreaker lineBreaker(this);
794
795 while (!endOfLine.atEnd()) {
796 // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
797 if (checkForEndLineMatch) {
798 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
799 if (layoutState.endLineMatched()) {
800 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
801 break;
802 }
803 }
804
805 lineMidpointState.reset();
806
807 layoutState.lineInfo().setEmpty(true);
808 layoutState.lineInfo().resetRunsFromLeadingWhitespace();
809
810 const InlineIterator previousEndofLine = endOfLine;
811 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
812 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : 0;
813
814 WordMeasurements wordMeasurements;
815 endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo,
816 lastFloatFromPreviousLine, wordMeasurements);
817 renderTextInfo.m_lineBreakIterator.resetPriorContext();
818 if (resolver.position().atEnd()) {
819 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with!
820 // Once BidiRunList is separated from BidiResolver this will not be needed.
821 resolver.runs().deleteRuns();
822 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
823 layoutState.setCheckForFloatsFromLastLine(true);
824 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
825 break;
826 }
827
828 ASSERT(endOfLine != resolver.position());
829
830 // This is a short-cut for empty lines.
831 if (layoutState.lineInfo().isEmpty()) {
832 if (lastRootBox())
833 lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
834 } else {
835 VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
836 if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) {
837 TextDirection direction = determinePlaintextDirectionality(resolver.position().root(), resolver.position().object(), resolver.position().offset());
838 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi())));
839 }
840 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
841 BidiRunList<BidiRun>& bidiRuns = resolver.runs();
842 constructBidiRunsForLine(resolver, bidiRuns, endOfLine, override, layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph);
843 ASSERT(resolver.position() == endOfLine);
844
845 BidiRun* trailingSpaceRun = resolver.trailingSpaceRun();
846
847 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated())
848 bidiRuns.logicallyLastRun()->m_hasHyphen = true;
849
850 // Now that the runs have been ordered, we create the line boxes.
851 // At the same time we figure out where border/padding/margin should be applied for
852 // inline flow boxes.
853
854 LayoutUnit oldLogicalHeight = logicalHeight();
855 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
856
857 bidiRuns.deleteRuns();
858 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
859
860 if (lineBox) {
861 lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset(), resolver.status());
862 if (layoutState.usesPaintInvalidationBounds())
863 layoutState.updatePaintInvalidationRangeFromBox(lineBox);
864
865 if (paginated) {
866 LayoutUnit adjustment = 0;
867 adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread());
868 if (adjustment) {
869 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine());
870 lineBox->adjustBlockDirectionPosition(adjustment.toFloat());
871 if (layoutState.usesPaintInvalidationBounds())
872 layoutState.updatePaintInvalidationRangeFromBox(lineBox);
873
874 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) {
875 // We have to delete this line, remove all floats that got added, and let line layout re-run.
876 lineBox->deleteLine();
877 endOfLine = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, previousEndofLine);
878 continue;
879 }
880
881 setLogicalHeight(lineBox->lineBottomWithLeading());
882 }
883 }
884 }
885 }
886
887 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
888 setStaticPositions(this, lineBreaker.positionedObjects()[i]);
889
890 if (!layoutState.lineInfo().isEmpty()) {
891 layoutState.lineInfo().setFirstLine(false);
892 clearFloats(lineBreaker.clear());
893 }
894
895 if (m_floatingObjects && lastRootBox()) {
896 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
897 FloatingObjectSetIterator it = floatingObjectSet.begin();
898 FloatingObjectSetIterator end = floatingObjectSet.end();
899 if (layoutState.lastFloat()) {
900 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
901 ASSERT(lastFloatIterator != end);
902 ++lastFloatIterator;
903 it = lastFloatIterator;
904 }
905 for (; it != end; ++it) {
906 FloatingObject* f = it->get();
907 appendFloatingObjectToLastLine(f);
908 ASSERT(f->renderer() == layoutState.floats()[layoutState.floatIndex()].object);
909 // If a float's geometry has changed, give up on syncing with clean lines.
910 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
911 checkForEndLineMatch = false;
912 layoutState.setFloatIndex(layoutState.floatIndex() + 1);
913 }
914 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : 0);
915 }
916
917 lineMidpointState.reset();
918 resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine));
919 }
920
921 // In case we already adjusted the line positions during this layout to avoid widows
922 // then we need to ignore the possibility of having a new widows situation.
923 // Otherwise, we risk leaving empty containers which is against the block fragmentation principles.
924 if (paginated && !style()->hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
925 // Check the line boxes to make sure we didn't create unacceptable widows.
926 // However, we'll prioritize orphans - so nothing we do here should create
927 // a new orphan.
928
929 RootInlineBox* lineBox = lastRootBox();
930
931 // Count from the end of the block backwards, to see how many hanging
932 // lines we have.
933 RootInlineBox* firstLineInBlock = firstRootBox();
934 int numLinesHanging = 1;
935 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
936 ++numLinesHanging;
937 lineBox = lineBox->prevRootBox();
938 }
939
940 // If there were no breaks in the block, we didn't create any widows.
941 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
942 return;
943
944 if (numLinesHanging < style()->widows()) {
945 // We have detected a widow. Now we need to work out how many
946 // lines there are on the previous page, and how many we need
947 // to steal.
948 int numLinesNeeded = style()->widows() - numLinesHanging;
949 RootInlineBox* currentFirstLineOfNewPage = lineBox;
950
951 // Count the number of lines in the previous page.
952 lineBox = lineBox->prevRootBox();
953 int numLinesInPreviousPage = 1;
954 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
955 ++numLinesInPreviousPage;
956 lineBox = lineBox->prevRootBox();
957 }
958
959 // If there was an explicit value for orphans, respect that. If not, we still
960 // shouldn't create a situation where we make an orphan bigger than the initial value.
961 // This means that setting widows implies we also care about orphans, but given
962 // the specification says the initial orphan value is non-zero, this is ok. The
963 // author is always free to set orphans explicitly as well.
964 int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans();
965 int numLinesAvailable = numLinesInPreviousPage - orphans;
966 if (numLinesAvailable <= 0)
967 return;
968
969 int numLinesToTake = std::min(numLinesAvailable, numLinesNeeded);
970 // Wind back from our first widowed line.
971 lineBox = currentFirstLineOfNewPage;
972 for (int i = 0; i < numLinesToTake; ++i)
973 lineBox = lineBox->prevRootBox();
974
975 // We now want to break at this line. Remember for next layout and trigger relayout.
976 setBreakAtLineToAvoidWidow(lineCount(lineBox));
977 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
978 }
979 }
980
981 clearDidBreakAtLineToAvoidWidow();
982 }
983
linkToEndLineIfNeeded(LineLayoutState & layoutState)984 void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
985 {
986 if (layoutState.endLine()) {
987 if (layoutState.endLineMatched()) {
988 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
989 // Attach all the remaining lines, and then adjust their y-positions as needed.
990 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop();
991 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
992 line->attachLine();
993 if (paginated) {
994 delta -= line->paginationStrut();
995 adjustLinePositionForPagination(line, delta, layoutState.flowThread());
996 }
997 if (delta) {
998 layoutState.updatePaintInvalidationRangeFromBox(line, delta);
999 line->adjustBlockDirectionPosition(delta.toFloat());
1000 }
1001 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1002 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1003 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1004 FloatingObject* floatingObject = insertFloatingObject(*f);
1005 ASSERT(!floatingObject->originatingLine());
1006 floatingObject->setOriginatingLine(line);
1007 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
1008 positionNewFloats();
1009 }
1010 }
1011 }
1012 setLogicalHeight(lastRootBox()->lineBottomWithLeading());
1013 } else {
1014 // Delete all the remaining lines.
1015 deleteLineRange(layoutState, layoutState.endLine());
1016 }
1017 }
1018
1019 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) {
1020 // In case we have a float on the last line, it might not be positioned up to now.
1021 // This has to be done before adding in the bottom border/padding, or the float will
1022 // include the padding incorrectly. -dwh
1023 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1024 FloatingObjectSetIterator it = floatingObjectSet.begin();
1025 FloatingObjectSetIterator end = floatingObjectSet.end();
1026 if (layoutState.lastFloat()) {
1027 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
1028 ASSERT(lastFloatIterator != end);
1029 ++lastFloatIterator;
1030 it = lastFloatIterator;
1031 }
1032 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : 0);
1033
1034 if (it == end)
1035 return;
1036
1037 if (layoutState.checkForFloatsFromLastLine()) {
1038 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
1039 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
1040 TrailingFloatsRootInlineBox* trailingFloatsLineBox = new TrailingFloatsRootInlineBox(*this);
1041 m_lineBoxes.appendLineBox(trailingFloatsLineBox);
1042 trailingFloatsLineBox->setConstructed();
1043 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1044 VerticalPositionCache verticalPositionCache;
1045 LayoutUnit blockLogicalHeight = logicalHeight();
1046 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache);
1047 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight);
1048 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent());
1049 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight);
1050 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
1051 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
1052 }
1053
1054 for (; it != end; ++it)
1055 appendFloatingObjectToLastLine(it->get());
1056 }
1057 }
1058
markDirtyFloatsForPaintInvalidation(Vector<FloatWithRect> & floats)1059 void RenderBlockFlow::markDirtyFloatsForPaintInvalidation(Vector<FloatWithRect>& floats)
1060 {
1061 size_t floatCount = floats.size();
1062 // Floats that did not have layout did not paint invalidations when we laid them out. They would have
1063 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
1064 // painted.
1065 for (size_t i = 0; i < floatCount; ++i) {
1066 if (!floats[i].everHadLayout) {
1067 RenderBox* f = floats[i].object;
1068 if (!f->x() && !f->y() && f->checkForPaintInvalidation()) {
1069 f->setShouldDoFullPaintInvalidation(true);
1070 }
1071 }
1072 }
1073 }
1074
1075 struct InlineMinMaxIterator {
1076 /* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to
1077 inline min/max width calculations. Note the following about the way it walks:
1078 (1) Positioned content is skipped (since it does not contribute to min/max width of a block)
1079 (2) We do not drill into the children of floats or replaced elements, since you can't break
1080 in the middle of such an element.
1081 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have
1082 distinct borders/margin/padding that contribute to the min/max width.
1083 */
1084 RenderObject* parent;
1085 RenderObject* current;
1086 bool endOfInline;
1087
InlineMinMaxIteratorblink::InlineMinMaxIterator1088 InlineMinMaxIterator(RenderObject* p, bool end = false)
1089 : parent(p), current(p), endOfInline(end)
1090 {
1091
1092 }
1093
1094 RenderObject* next();
1095 };
1096
next()1097 RenderObject* InlineMinMaxIterator::next()
1098 {
1099 RenderObject* result = 0;
1100 bool oldEndOfInline = endOfInline;
1101 endOfInline = false;
1102 while (current || current == parent) {
1103 if (!oldEndOfInline && (current == parent || (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned())))
1104 result = current->slowFirstChild();
1105
1106 if (!result) {
1107 // We hit the end of our inline. (It was empty, e.g., <span></span>.)
1108 if (!oldEndOfInline && current->isRenderInline()) {
1109 result = current;
1110 endOfInline = true;
1111 break;
1112 }
1113
1114 while (current && current != parent) {
1115 result = current->nextSibling();
1116 if (result)
1117 break;
1118 current = current->parent();
1119 if (current && current != parent && current->isRenderInline()) {
1120 result = current;
1121 endOfInline = true;
1122 break;
1123 }
1124 }
1125 }
1126
1127 if (!result)
1128 break;
1129
1130 if (!result->isOutOfFlowPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline()))
1131 break;
1132
1133 current = result;
1134 result = 0;
1135 }
1136
1137 // Update our position.
1138 current = result;
1139 return current;
1140 }
1141
getBPMWidth(LayoutUnit childValue,Length cssUnit)1142 static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit)
1143 {
1144 if (cssUnit.type() != Auto)
1145 return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : childValue);
1146 return 0;
1147 }
1148
getBorderPaddingMargin(RenderBoxModelObject * child,bool endOfInline)1149 static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline)
1150 {
1151 RenderStyle* childStyle = child->style();
1152 if (endOfInline) {
1153 return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) +
1154 getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) +
1155 child->borderEnd();
1156 }
1157 return getBPMWidth(child->marginStart(), childStyle->marginStart()) +
1158 getBPMWidth(child->paddingStart(), childStyle->paddingStart()) +
1159 child->borderStart();
1160 }
1161
stripTrailingSpace(float & inlineMax,float & inlineMin,RenderObject * trailingSpaceChild)1162 static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, RenderObject* trailingSpaceChild)
1163 {
1164 if (trailingSpaceChild && trailingSpaceChild->isText()) {
1165 // Collapse away the trailing space at the end of a block.
1166 RenderText* t = toRenderText(trailingSpaceChild);
1167 const UChar space = ' ';
1168 const Font& font = t->style()->font(); // FIXME: This ignores first-line.
1169 float spaceWidth = font.width(constructTextRun(t, font, &space, 1, t->style(), LTR));
1170 inlineMax -= spaceWidth + font.fontDescription().wordSpacing();
1171 if (inlineMin > inlineMax)
1172 inlineMin = inlineMax;
1173 }
1174 }
1175
updatePreferredWidth(LayoutUnit & preferredWidth,float & result)1176 static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result)
1177 {
1178 LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result);
1179 preferredWidth = std::max(snappedResult, preferredWidth);
1180 }
1181
1182 // When converting between floating point and LayoutUnits we risk losing precision
1183 // with each conversion. When this occurs while accumulating our preferred widths,
1184 // we can wind up with a line width that's larger than our maxPreferredWidth due to
1185 // pure float accumulation.
adjustFloatForSubPixelLayout(float value)1186 static inline LayoutUnit adjustFloatForSubPixelLayout(float value)
1187 {
1188 return LayoutUnit::fromFloatCeil(value);
1189 }
1190
1191 // FIXME: This function should be broken into something less monolithic.
1192 // FIXME: The main loop here is very similar to LineBreaker::nextSegmentBreak. They can probably reuse code.
computeInlinePreferredLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth)1193 void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth)
1194 {
1195 float inlineMax = 0;
1196 float inlineMin = 0;
1197
1198 RenderStyle* styleToUse = style();
1199 RenderBlock* containingBlock = this->containingBlock();
1200 LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit();
1201
1202 // If we are at the start of a line, we want to ignore all white-space.
1203 // Also strip spaces if we previously had text that ended in a trailing space.
1204 bool stripFrontSpaces = true;
1205 RenderObject* trailingSpaceChild = 0;
1206
1207 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
1208 // very specific cirucumstances (in order to match common WinIE renderings).
1209 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
1210 bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse->logicalWidth().isIntrinsicOrAuto();
1211
1212 bool autoWrap, oldAutoWrap;
1213 autoWrap = oldAutoWrap = styleToUse->autoWrap();
1214
1215 InlineMinMaxIterator childIterator(this);
1216
1217 // Only gets added to the max preffered width once.
1218 bool addedTextIndent = false;
1219 // Signals the text indent was more negative than the min preferred width
1220 bool hasRemainingNegativeTextIndent = false;
1221
1222 LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw);
1223 RenderObject* prevFloat = 0;
1224 bool isPrevChildInlineFlow = false;
1225 bool shouldBreakLineAfterText = false;
1226 while (RenderObject* child = childIterator.next()) {
1227 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
1228 child->style()->autoWrap();
1229
1230 if (!child->isBR()) {
1231 // Step One: determine whether or not we need to go ahead and
1232 // terminate our current line. Each discrete chunk can become
1233 // the new min-width, if it is the widest chunk seen so far, and
1234 // it can also become the max-width.
1235
1236 // Children fall into three categories:
1237 // (1) An inline flow object. These objects always have a min/max of 0,
1238 // and are included in the iteration solely so that their margins can
1239 // be added in.
1240 //
1241 // (2) An inline non-text non-flow object, e.g., an inline replaced element.
1242 // These objects can always be on a line by themselves, so in this situation
1243 // we need to go ahead and break the current line, and then add in our own
1244 // margins and min/max width on its own line, and then terminate the line.
1245 //
1246 // (3) A text object. Text runs can have breakable characters at the start,
1247 // the middle or the end. They may also lose whitespace off the front if
1248 // we're already ignoring whitespace. In order to compute accurate min-width
1249 // information, we need three pieces of information.
1250 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
1251 // starts with whitespace.
1252 // (b) the min-width of the last non-breakable run. Should be 0 if the text string
1253 // ends with whitespace.
1254 // (c) the min/max width of the string (trimmed for whitespace).
1255 //
1256 // If the text string starts with whitespace, then we need to go ahead and
1257 // terminate our current line (unless we're already in a whitespace stripping
1258 // mode.
1259 //
1260 // If the text string has a breakable character in the middle, but didn't start
1261 // with whitespace, then we add the width of the first non-breakable run and
1262 // then end the current line. We then need to use the intermediate min/max width
1263 // values (if any of them are larger than our current min/max). We then look at
1264 // the width of the last non-breakable run and use that to start a new line
1265 // (unless we end in whitespace).
1266 RenderStyle* childStyle = child->style();
1267 float childMin = 0;
1268 float childMax = 0;
1269
1270 if (!child->isText()) {
1271 // Case (1) and (2). Inline replaced and inline flow elements.
1272 if (child->isRenderInline()) {
1273 // Add in padding/border/margin from the appropriate side of
1274 // the element.
1275 float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline).toFloat();
1276 childMin += bpm;
1277 childMax += bpm;
1278
1279 inlineMin += childMin;
1280 inlineMax += childMax;
1281
1282 child->clearPreferredLogicalWidthsDirty();
1283 } else {
1284 // Inline replaced elts add in their margins to their min/max values.
1285 LayoutUnit margins = 0;
1286 Length startMargin = childStyle->marginStart();
1287 Length endMargin = childStyle->marginEnd();
1288 if (startMargin.isFixed())
1289 margins += adjustFloatForSubPixelLayout(startMargin.value());
1290 if (endMargin.isFixed())
1291 margins += adjustFloatForSubPixelLayout(endMargin.value());
1292 childMin += margins.ceilToFloat();
1293 childMax += margins.ceilToFloat();
1294 }
1295 }
1296
1297 if (!child->isRenderInline() && !child->isText()) {
1298 // Case (2). Inline replaced elements and floats.
1299 // Go ahead and terminate the current line as far as
1300 // minwidth is concerned.
1301 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth;
1302 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) {
1303 RenderBox* childBox = toRenderBox(child);
1304 LogicalExtentComputedValues computedValues;
1305 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues);
1306 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent;
1307 } else {
1308 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth();
1309 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth();
1310 }
1311 childMin += childMinPreferredLogicalWidth.ceilToFloat();
1312 childMax += childMaxPreferredLogicalWidth.ceilToFloat();
1313
1314 bool clearPreviousFloat;
1315 if (child->isFloating()) {
1316 clearPreviousFloat = (prevFloat
1317 && ((prevFloat->style()->floating() == LeftFloat && (childStyle->clear() & CLEFT))
1318 || (prevFloat->style()->floating() == RightFloat && (childStyle->clear() & CRIGHT))));
1319 prevFloat = child;
1320 } else {
1321 clearPreviousFloat = false;
1322 }
1323
1324 bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak;
1325 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) {
1326 updatePreferredWidth(minLogicalWidth, inlineMin);
1327 inlineMin = 0;
1328 }
1329
1330 // If we're supposed to clear the previous float, then terminate maxwidth as well.
1331 if (clearPreviousFloat) {
1332 updatePreferredWidth(maxLogicalWidth, inlineMax);
1333 inlineMax = 0;
1334 }
1335
1336 // Add in text-indent. This is added in only once.
1337 if (!addedTextIndent && !child->isFloating()) {
1338 float ceiledTextIndent = textIndent.ceilToFloat();
1339 childMin += ceiledTextIndent;
1340 childMax += ceiledTextIndent;
1341
1342 if (childMin < 0)
1343 textIndent = adjustFloatForSubPixelLayout(childMin);
1344 else
1345 addedTextIndent = true;
1346 }
1347
1348 // Add our width to the max.
1349 inlineMax += std::max<float>(0, childMax);
1350
1351 if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) {
1352 if (child->isFloating())
1353 updatePreferredWidth(minLogicalWidth, childMin);
1354 else
1355 inlineMin += childMin;
1356 } else {
1357 // Now check our line.
1358 updatePreferredWidth(minLogicalWidth, childMin);
1359
1360 // Now start a new line.
1361 inlineMin = 0;
1362 }
1363
1364 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
1365 updatePreferredWidth(minLogicalWidth, inlineMin);
1366 inlineMin = 0;
1367 }
1368
1369 // We are no longer stripping whitespace at the start of
1370 // a line.
1371 if (!child->isFloating()) {
1372 stripFrontSpaces = false;
1373 trailingSpaceChild = 0;
1374 }
1375 } else if (child->isText()) {
1376 // Case (3). Text.
1377 RenderText* t = toRenderText(child);
1378
1379 if (t->isWordBreak()) {
1380 updatePreferredWidth(minLogicalWidth, inlineMin);
1381 inlineMin = 0;
1382 continue;
1383 }
1384
1385 if (t->style()->hasTextCombine() && t->isCombineText())
1386 toRenderCombineText(t)->combineText();
1387
1388 // Determine if we have a breakable character. Pass in
1389 // whether or not we should ignore any spaces at the front
1390 // of the string. If those are going to be stripped out,
1391 // then they shouldn't be considered in the breakable char
1392 // check.
1393 bool hasBreakableChar, hasBreak;
1394 float firstLineMinWidth, lastLineMinWidth;
1395 bool hasBreakableStart, hasBreakableEnd;
1396 float firstLineMaxWidth, lastLineMaxWidth;
1397 t->trimmedPrefWidths(inlineMax,
1398 firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreakableEnd,
1399 hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth,
1400 childMin, childMax, stripFrontSpaces, styleToUse->direction());
1401
1402 // This text object will not be rendered, but it may still provide a breaking opportunity.
1403 if (!hasBreak && !childMax) {
1404 if (autoWrap && (hasBreakableStart || hasBreakableEnd)) {
1405 updatePreferredWidth(minLogicalWidth, inlineMin);
1406 inlineMin = 0;
1407 }
1408 continue;
1409 }
1410
1411 if (stripFrontSpaces)
1412 trailingSpaceChild = child;
1413 else
1414 trailingSpaceChild = 0;
1415
1416 // Add in text-indent. This is added in only once.
1417 float ti = 0;
1418 if (!addedTextIndent || hasRemainingNegativeTextIndent) {
1419 ti = textIndent.ceilToFloat();
1420 childMin += ti;
1421 firstLineMinWidth += ti;
1422
1423 // It the text indent negative and larger than the child minimum, we re-use the remainder
1424 // in future minimum calculations, but using the negative value again on the maximum
1425 // will lead to under-counting the max pref width.
1426 if (!addedTextIndent) {
1427 childMax += ti;
1428 firstLineMaxWidth += ti;
1429 addedTextIndent = true;
1430 }
1431
1432 if (childMin < 0) {
1433 textIndent = childMin;
1434 hasRemainingNegativeTextIndent = true;
1435 }
1436 }
1437
1438 // If we have no breakable characters at all,
1439 // then this is the easy case. We add ourselves to the current
1440 // min and max and continue.
1441 if (!hasBreakableChar) {
1442 inlineMin += childMin;
1443 } else {
1444 if (hasBreakableStart) {
1445 updatePreferredWidth(minLogicalWidth, inlineMin);
1446 } else {
1447 inlineMin += firstLineMinWidth;
1448 updatePreferredWidth(minLogicalWidth, inlineMin);
1449 childMin -= ti;
1450 }
1451
1452 inlineMin = childMin;
1453
1454 if (hasBreakableEnd) {
1455 updatePreferredWidth(minLogicalWidth, inlineMin);
1456 inlineMin = 0;
1457 shouldBreakLineAfterText = false;
1458 } else {
1459 updatePreferredWidth(minLogicalWidth, inlineMin);
1460 inlineMin = lastLineMinWidth;
1461 shouldBreakLineAfterText = true;
1462 }
1463 }
1464
1465 if (hasBreak) {
1466 inlineMax += firstLineMaxWidth;
1467 updatePreferredWidth(maxLogicalWidth, inlineMax);
1468 updatePreferredWidth(maxLogicalWidth, childMax);
1469 inlineMax = lastLineMaxWidth;
1470 addedTextIndent = true;
1471 } else {
1472 inlineMax += std::max<float>(0, childMax);
1473 }
1474 }
1475
1476 // Ignore spaces after a list marker.
1477 if (child->isListMarker())
1478 stripFrontSpaces = true;
1479 } else {
1480 updatePreferredWidth(minLogicalWidth, inlineMin);
1481 updatePreferredWidth(maxLogicalWidth, inlineMax);
1482 inlineMin = inlineMax = 0;
1483 stripFrontSpaces = true;
1484 trailingSpaceChild = 0;
1485 addedTextIndent = true;
1486 }
1487
1488 if (!child->isText() && child->isRenderInline())
1489 isPrevChildInlineFlow = true;
1490 else
1491 isPrevChildInlineFlow = false;
1492
1493 oldAutoWrap = autoWrap;
1494 }
1495
1496 if (styleToUse->collapseWhiteSpace())
1497 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
1498
1499 updatePreferredWidth(minLogicalWidth, inlineMin);
1500 updatePreferredWidth(maxLogicalWidth, inlineMax);
1501 }
1502
layoutInlineChildren(bool relayoutChildren,LayoutUnit & paintInvalidationLogicalTop,LayoutUnit & paintInvalidationLogicalBottom,LayoutUnit afterEdge)1503 void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& paintInvalidationLogicalTop, LayoutUnit& paintInvalidationLogicalBottom, LayoutUnit afterEdge)
1504 {
1505 RenderFlowThread* flowThread = flowThreadContainingBlock();
1506 bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions();
1507
1508 // Figure out if we should clear out our line boxes.
1509 // FIXME: Handle resize eventually!
1510 bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
1511 LineLayoutState layoutState(isFullLayout, paintInvalidationLogicalTop, paintInvalidationLogicalBottom, flowThread);
1512
1513 if (isFullLayout) {
1514 // Ensure the old line boxes will be erased.
1515 if (firstLineBox())
1516 setShouldDoFullPaintInvalidation(true);
1517 lineBoxes()->deleteLineBoxes();
1518 }
1519
1520 // Text truncation kicks in in two cases:
1521 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip.
1522 // 2) If you're an anonymous block with a block parent that satisfies #1 that was created
1523 // to accomodate a block that has inline and block children. This excludes parents where
1524 // canCollapseAnonymousBlockChild is false, notabley flex items and grid items.
1525 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1526 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the
1527 // simple case of an anonymous block truncating when it's parent is clipped.
1528 bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
1529 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && toRenderBlock(parent())->canCollapseAnonymousBlockChild()
1530 && parent()->style()->textOverflow() && parent()->hasOverflowClip());
1531
1532 // Walk all the lines and delete our ellipsis line boxes if they exist.
1533 if (hasTextOverflow)
1534 deleteEllipsisLineBoxes();
1535
1536 if (firstChild()) {
1537 // In full layout mode, clear the line boxes of children upfront. Otherwise,
1538 // siblings can run into stale root lineboxes during layout. Then layout
1539 // the replaced elements later. In partial layout mode, line boxes are not
1540 // deleted and only dirtied. In that case, we can layout the replaced
1541 // elements at the same time.
1542 Vector<RenderBox*> replacedChildren;
1543 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
1544 RenderObject* o = walker.current();
1545
1546 if (!layoutState.hasInlineChild() && o->isInline())
1547 layoutState.setHasInlineChild(true);
1548
1549 if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) {
1550 RenderBox* box = toRenderBox(o);
1551
1552 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box);
1553
1554 if (o->isOutOfFlowPositioned())
1555 o->containingBlock()->insertPositionedObject(box);
1556 else if (o->isFloating())
1557 layoutState.floats().append(FloatWithRect(box));
1558 else if (isFullLayout || o->needsLayout()) {
1559 // Replaced element.
1560 box->dirtyLineBoxes(isFullLayout);
1561 if (isFullLayout)
1562 replacedChildren.append(box);
1563 else
1564 o->layoutIfNeeded();
1565 }
1566 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
1567 if (!o->isText())
1568 toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
1569 if (layoutState.isFullLayout() || o->selfNeedsLayout())
1570 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
1571 o->clearNeedsLayout();
1572 }
1573 }
1574
1575 for (size_t i = 0; i < replacedChildren.size(); i++)
1576 replacedChildren[i]->layoutIfNeeded();
1577
1578 layoutRunsAndFloats(layoutState);
1579 }
1580
1581 // Expand the last line to accommodate Ruby and emphasis marks.
1582 int lastLineAnnotationsAdjustment = 0;
1583 if (lastRootBox()) {
1584 LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
1585 if (!style()->isFlippedLinesWritingMode())
1586 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
1587 else
1588 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
1589 }
1590
1591 // Now add in the bottom border/padding.
1592 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge);
1593
1594 if (!firstLineBox() && hasLineIfEmpty())
1595 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
1596
1597 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1598 // truncate text.
1599 if (hasTextOverflow)
1600 checkLinesForTextOverflow();
1601
1602 // Ensure the new line boxes will be painted.
1603 if (isFullLayout && firstLineBox())
1604 setShouldDoFullPaintInvalidation(true);
1605 }
1606
checkFloatsInCleanLine(RootInlineBox * line,Vector<FloatWithRect> & floats,size_t & floatIndex,bool & encounteredNewFloat,bool & dirtiedByFloat)1607 void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
1608 {
1609 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
1610 if (!cleanLineFloats)
1611 return;
1612
1613 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1614 for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
1615 RenderBox* floatingBox = *it;
1616 floatingBox->layoutIfNeeded();
1617 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight());
1618 if (floats[floatIndex].object != floatingBox) {
1619 encounteredNewFloat = true;
1620 return;
1621 }
1622
1623 if (floats[floatIndex].rect.size() != newSize) {
1624 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
1625 LayoutUnit floatHeight = isHorizontalWritingMode() ? std::max(floats[floatIndex].rect.height(), newSize.height())
1626 : std::max(floats[floatIndex].rect.width(), newSize.width());
1627 floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop);
1628 line->markDirty();
1629 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
1630 floats[floatIndex].rect.setSize(newSize);
1631 dirtiedByFloat = true;
1632 }
1633 floatIndex++;
1634 }
1635 }
1636
determineStartPosition(LineLayoutState & layoutState,InlineBidiResolver & resolver)1637 RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
1638 {
1639 RootInlineBox* curr = 0;
1640 RootInlineBox* last = 0;
1641
1642 // FIXME: This entire float-checking block needs to be broken into a new function.
1643 bool dirtiedByFloat = false;
1644 if (!layoutState.isFullLayout()) {
1645 // Paginate all of the clean lines.
1646 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1647 LayoutUnit paginationDelta = 0;
1648 size_t floatIndex = 0;
1649 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1650 if (paginated) {
1651 paginationDelta -= curr->paginationStrut();
1652 adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
1653 if (paginationDelta) {
1654 if (containsFloats() || !layoutState.floats().isEmpty()) {
1655 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout.
1656 layoutState.markForFullLayout();
1657 break;
1658 }
1659
1660 layoutState.updatePaintInvalidationRangeFromBox(curr, paginationDelta);
1661 curr->adjustBlockDirectionPosition(paginationDelta.toFloat());
1662 }
1663 }
1664
1665 // If a new float has been inserted before this line or before its last known float, just do a full layout.
1666 bool encounteredNewFloat = false;
1667 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1668 if (encounteredNewFloat)
1669 layoutState.markForFullLayout();
1670
1671 if (dirtiedByFloat || layoutState.isFullLayout())
1672 break;
1673 }
1674 // Check if a new float has been inserted after the last known float.
1675 if (!curr && floatIndex < layoutState.floats().size())
1676 layoutState.markForFullLayout();
1677 }
1678
1679 if (layoutState.isFullLayout()) {
1680 // If we encountered a new float and have inline children, mark ourself to force us to issue paint invalidations.
1681 if (layoutState.hasInlineChild() && !selfNeedsLayout()) {
1682 setNeedsLayoutAndFullPaintInvalidation(MarkOnlyThis);
1683 setShouldDoFullPaintInvalidation(true);
1684 }
1685
1686 // FIXME: This should just call deleteLineBoxTree, but that causes
1687 // crashes for fast/repaint tests.
1688 curr = firstRootBox();
1689 while (curr) {
1690 // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does.
1691 RootInlineBox* next = curr->nextRootBox();
1692 curr->deleteLine();
1693 curr = next;
1694 }
1695 ASSERT(!firstLineBox() && !lastLineBox());
1696 } else {
1697 if (curr) {
1698 // We have a dirty line.
1699 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1700 // We have a previous line.
1701 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
1702 // The previous line didn't break cleanly or broke at a newline
1703 // that has been deleted, so treat it as dirty too.
1704 curr = prevRootBox;
1705 }
1706 } else {
1707 // No dirty lines were found.
1708 // If the last line didn't break cleanly, treat it as dirty.
1709 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1710 curr = lastRootBox();
1711 }
1712
1713 // If we have no dirty lines, then last is just the last root box.
1714 last = curr ? curr->prevRootBox() : lastRootBox();
1715 }
1716
1717 unsigned numCleanFloats = 0;
1718 if (!layoutState.floats().isEmpty()) {
1719 LayoutUnit savedLogicalHeight = logicalHeight();
1720 // Restore floats from clean lines.
1721 RootInlineBox* line = firstRootBox();
1722 while (line != curr) {
1723 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1724 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1725 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1726 FloatingObject* floatingObject = insertFloatingObject(*f);
1727 ASSERT(!floatingObject->originatingLine());
1728 floatingObject->setOriginatingLine(line);
1729 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f));
1730 positionNewFloats();
1731 ASSERT(layoutState.floats()[numCleanFloats].object == *f);
1732 numCleanFloats++;
1733 }
1734 }
1735 line = line->nextRootBox();
1736 }
1737 setLogicalHeight(savedLogicalHeight);
1738 }
1739 layoutState.setFloatIndex(numCleanFloats);
1740
1741 layoutState.lineInfo().setFirstLine(!last);
1742 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
1743
1744 if (last) {
1745 setLogicalHeight(last->lineBottomWithLeading());
1746 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
1747 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1748 resolver.setStatus(last->lineBreakBidiStatus());
1749 } else {
1750 TextDirection direction = style()->direction();
1751 if (style()->unicodeBidi() == Plaintext)
1752 direction = determinePlaintextDirectionality(this);
1753 resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi())));
1754 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, resolver.runs(), &resolver), 0);
1755 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1756 }
1757 return curr;
1758 }
1759
determineEndPosition(LineLayoutState & layoutState,RootInlineBox * startLine,InlineIterator & cleanLineStart,BidiStatus & cleanLineBidiStatus)1760 void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
1761 {
1762 ASSERT(!layoutState.endLine());
1763 size_t floatIndex = layoutState.floatIndex();
1764 RootInlineBox* last = 0;
1765 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1766 if (!curr->isDirty()) {
1767 bool encounteredNewFloat = false;
1768 bool dirtiedByFloat = false;
1769 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1770 if (encounteredNewFloat)
1771 return;
1772 }
1773 if (curr->isDirty())
1774 last = 0;
1775 else if (!last)
1776 last = curr;
1777 }
1778
1779 if (!last)
1780 return;
1781
1782 // At this point, |last| is the first line in a run of clean lines that ends with the last line
1783 // in the block.
1784
1785 RootInlineBox* prev = last->prevRootBox();
1786 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1787 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1788 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
1789
1790 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1791 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1792 // their connections to one another.
1793
1794 layoutState.setEndLine(last);
1795 }
1796
checkPaginationAndFloatsAtEndLine(LineLayoutState & layoutState)1797 bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
1798 {
1799 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
1800
1801 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1802 if (paginated && layoutState.flowThread()) {
1803 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result
1804 // in a different available line width.
1805 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) {
1806 if (paginated) {
1807 // This isn't the real move we're going to do, so don't update the line box's pagination
1808 // strut yet.
1809 LayoutUnit oldPaginationStrut = lineBox->paginationStrut();
1810 lineDelta -= oldPaginationStrut;
1811 adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread());
1812 lineBox->setPaginationStrut(oldPaginationStrut);
1813 }
1814 }
1815 }
1816
1817 if (!lineDelta || !m_floatingObjects)
1818 return true;
1819
1820 // See if any floats end in the range along which we want to shift the lines vertically.
1821 LayoutUnit logicalTop = std::min(logicalHeight(), layoutState.endLineLogicalTop());
1822
1823 RootInlineBox* lastLine = layoutState.endLine();
1824 while (RootInlineBox* nextLine = lastLine->nextRootBox())
1825 lastLine = nextLine;
1826
1827 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
1828
1829 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1830 FloatingObjectSetIterator end = floatingObjectSet.end();
1831 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
1832 FloatingObject* floatingObject = it->get();
1833 if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
1834 return false;
1835 }
1836
1837 return true;
1838 }
1839
matchedEndLine(LineLayoutState & layoutState,const InlineBidiResolver & resolver,const InlineIterator & endLineStart,const BidiStatus & endLineStatus)1840 bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
1841 {
1842 if (resolver.position() == endLineStart) {
1843 if (resolver.status() != endLineStatus)
1844 return false;
1845 return checkPaginationAndFloatsAtEndLine(layoutState);
1846 }
1847
1848 // The first clean line doesn't match, but we can check a handful of following lines to try
1849 // to match back up.
1850 static int numLines = 8; // The # of lines we're willing to match against.
1851 RootInlineBox* originalEndLine = layoutState.endLine();
1852 RootInlineBox* line = originalEndLine;
1853 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1854 if (line->lineBreakObj() == resolver.position().object() && line->lineBreakPos() == resolver.position().offset()) {
1855 // We have a match.
1856 if (line->lineBreakBidiStatus() != resolver.status())
1857 return false; // ...but the bidi state doesn't match.
1858
1859 bool matched = false;
1860 RootInlineBox* result = line->nextRootBox();
1861 layoutState.setEndLine(result);
1862 if (result) {
1863 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
1864 matched = checkPaginationAndFloatsAtEndLine(layoutState);
1865 }
1866
1867 // Now delete the lines that we failed to sync.
1868 deleteLineRange(layoutState, originalEndLine, result);
1869 return matched;
1870 }
1871 }
1872
1873 return false;
1874 }
1875
generatesLineBoxesForInlineChild(RenderObject * inlineObj)1876 bool RenderBlockFlow::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
1877
1878 {
1879 ASSERT(inlineObj->parent() == this);
1880
1881 InlineIterator it(this, inlineObj, 0);
1882 // FIXME: We should pass correct value for WhitespacePosition.
1883 while (!it.atEnd() && !requiresLineBox(it))
1884 it.increment();
1885
1886 return !it.atEnd();
1887 }
1888
1889
addOverflowFromInlineChildren()1890 void RenderBlockFlow::addOverflowFromInlineChildren()
1891 {
1892 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
1893 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
1894 if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection())
1895 endPadding = 1;
1896 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
1897 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
1898 LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
1899 addContentsVisualOverflow(visualOverflow);
1900 }
1901 }
1902
deleteEllipsisLineBoxes()1903 void RenderBlockFlow::deleteEllipsisLineBoxes()
1904 {
1905 ETextAlign textAlign = style()->textAlign();
1906 bool ltr = style()->isLeftToRightDirection();
1907 bool firstLine = true;
1908 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
1909 if (curr->hasEllipsisBox()) {
1910 curr->clearTruncation();
1911
1912 // Shift the line back where it belongs if we cannot accomodate an ellipsis.
1913 float logicalLeft = logicalLeftOffsetForLine(curr->lineTop(), firstLine).toFloat();
1914 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
1915 float totalLogicalWidth = curr->logicalWidth();
1916 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
1917
1918 if (ltr)
1919 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
1920 else
1921 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
1922 }
1923 firstLine = false;
1924 }
1925 }
1926
checkLinesForTextOverflow()1927 void RenderBlockFlow::checkLinesForTextOverflow()
1928 {
1929 // Determine the width of the ellipsis using the current font.
1930 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
1931 const Font& font = style()->font();
1932 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
1933 const Font& firstLineFont = firstLineStyle()->font();
1934 // FIXME: We should probably not hard-code the direction here. https://crbug.com/333004
1935 TextDirection ellipsisDirection = LTR;
1936 float firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle(), ellipsisDirection));
1937 float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style(), ellipsisDirection));
1938
1939 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
1940 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
1941 // check the left edge of the line box to see if it is less
1942 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
1943 bool ltr = style()->isLeftToRightDirection();
1944 ETextAlign textAlign = style()->textAlign();
1945 bool firstLine = true;
1946 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
1947 float currLogicalLeft = curr->logicalLeft();
1948 LayoutUnit blockRightEdge = logicalRightOffsetForLine(curr->lineTop(), firstLine);
1949 LayoutUnit blockLeftEdge = logicalLeftOffsetForLine(curr->lineTop(), firstLine);
1950 LayoutUnit lineBoxEdge = ltr ? currLogicalLeft + curr->logicalWidth() : currLogicalLeft;
1951 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
1952 // This line spills out of our box in the appropriate direction. Now we need to see if the line
1953 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
1954 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
1955 // space.
1956
1957 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth;
1958 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
1959 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
1960 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge.toFloat(), blockRightEdge.toFloat(), width.toFloat());
1961
1962 float logicalLeft = 0; // We are only intersted in the delta from the base position.
1963 float availableLogicalWidth = (blockRightEdge - blockLeftEdge).toFloat();
1964 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
1965 if (ltr)
1966 curr->adjustLogicalPosition(logicalLeft, 0);
1967 else
1968 curr->adjustLogicalPosition(logicalLeft - (availableLogicalWidth - totalLogicalWidth), 0);
1969 }
1970 }
1971 firstLine = false;
1972 }
1973 }
1974
positionNewFloatOnLine(FloatingObject * newFloat,FloatingObject * lastFloatFromPreviousLine,LineInfo & lineInfo,LineWidth & width)1975 bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
1976 {
1977 if (!positionNewFloats())
1978 return false;
1979
1980 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat);
1981
1982 // We only connect floats to lines for pagination purposes if the floats occur at the start of
1983 // the line and the previous line had a hard break (so this line is either the first in the block
1984 // or follows a <br>).
1985 if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
1986 return true;
1987
1988 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1989 ASSERT(floatingObjectSet.last() == newFloat);
1990
1991 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
1992 int paginationStrut = newFloat->paginationStrut();
1993
1994 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
1995 return true;
1996
1997 FloatingObjectSetIterator it = floatingObjectSet.end();
1998 --it; // Last float is newFloat, skip that one.
1999 FloatingObjectSetIterator begin = floatingObjectSet.begin();
2000 while (it != begin) {
2001 --it;
2002 FloatingObject* floatingObject = it->get();
2003 if (floatingObject == lastFloatFromPreviousLine)
2004 break;
2005 if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
2006 floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut());
2007 RenderBox* floatBox = floatingObject->renderer();
2008 setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
2009 if (floatBox->isRenderBlock())
2010 floatBox->forceChildLayout();
2011 else
2012 floatBox->layoutIfNeeded();
2013 // Save the old logical top before calling removePlacedObject which will set
2014 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat.
2015 LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
2016 m_floatingObjects->removePlacedObject(floatingObject);
2017 setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
2018 m_floatingObjects->addPlacedObject(floatingObject);
2019 }
2020 }
2021
2022 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing
2023 // no content, then we don't want to improperly grow the height of the block.
2024 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut);
2025 return true;
2026 }
2027
startAlignedOffsetForLine(LayoutUnit position,bool firstLine)2028 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
2029 {
2030 ETextAlign textAlign = style()->textAlign();
2031
2032 if (textAlign == TASTART) // FIXME: Handle TAEND here
2033 return startOffsetForLine(position, firstLine);
2034
2035 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
2036 float totalLogicalWidth = 0;
2037 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false).toFloat();
2038 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
2039 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2040
2041 if (!style()->isLeftToRightDirection())
2042 return logicalWidth() - logicalLeft;
2043 return logicalLeft;
2044 }
2045
2046 }
2047