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/rendering/FastTextAutosizer.h"
26 #include "core/rendering/LayoutRectRecorder.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/RenderRegion.h"
32 #include "core/rendering/RenderRubyRun.h"
33 #include "core/rendering/RenderView.h"
34 #include "core/rendering/TrailingFloatsRootInlineBox.h"
35 #include "core/rendering/VerticalPositionCache.h"
36 #include "core/rendering/line/BreakingContextInlineHeaders.h"
37 #include "core/rendering/svg/SVGRootInlineBox.h"
38 #include "platform/text/BidiResolver.h"
39 #include "wtf/RefCountedLeakCounter.h"
40 #include "wtf/StdLibExtras.h"
41 #include "wtf/Vector.h"
42 #include "wtf/unicode/CharacterNames.h"
43
44 namespace WebCore {
45
requiresIndent(bool isFirstLine,bool isAfterHardLineBreak,RenderStyle * style)46 static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
47 {
48 if (isFirstLine)
49 return IndentText;
50 if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine)
51 return IndentText;
52
53 return DoNotIndentText;
54 }
55
56 class LineBreaker {
57 public:
58 friend class BreakingContext;
LineBreaker(RenderBlockFlow * block)59 LineBreaker(RenderBlockFlow* block)
60 : m_block(block)
61 {
62 reset();
63 }
64
65 InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
66
lineWasHyphenated()67 bool lineWasHyphenated() { return m_hyphenated; }
positionedObjects()68 const Vector<RenderBox*>& positionedObjects() { return m_positionedObjects; }
clear()69 EClear clear() { return m_clear; }
70 private:
71 void reset();
72
73 InlineIterator nextSegmentBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
74 void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&);
75
76 RenderBlockFlow* m_block;
77 bool m_hyphenated;
78 EClear m_clear;
79 Vector<RenderBox*> m_positionedObjects;
80 };
81
firstRenderObjectForDirectionalityDetermination(RenderObject * root,RenderObject * current=0)82 static RenderObject* firstRenderObjectForDirectionalityDetermination(RenderObject* root, RenderObject* current = 0)
83 {
84 RenderObject* next = current;
85 while (current) {
86 if (isIsolated(current->style()->unicodeBidi())
87 && (current->isRenderInline() || current->isRenderBlock())) {
88 if (current != root)
89 current = 0;
90 else
91 current = next;
92 break;
93 }
94 current = current->parent();
95 }
96
97 if (!current)
98 current = root->firstChild();
99
100 while (current) {
101 next = 0;
102 if (isIteratorTarget(current) && !(current->isText() && toRenderText(current)->isAllCollapsibleWhitespace()))
103 break;
104
105 if (!isIteratorTarget(current) && !isIsolated(current->style()->unicodeBidi()))
106 next = current->firstChild();
107
108 if (!next) {
109 while (current && current != root) {
110 next = current->nextSibling();
111 if (next)
112 break;
113 current = current->parent();
114 }
115 }
116
117 if (!next)
118 break;
119
120 current = next;
121 }
122
123 return current;
124 }
125
determinePlaintextDirectionality(RenderObject * root,RenderObject * current=0,unsigned pos=0)126 static TextDirection determinePlaintextDirectionality(RenderObject* root, RenderObject* current = 0, unsigned pos = 0)
127 {
128 InlineIterator iter(root, firstRenderObjectForDirectionalityDetermination(root, current), pos);
129 InlineBidiResolver observer;
130 observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi())));
131 observer.setPositionIgnoringNestedIsolates(iter);
132 return observer.determineParagraphDirectionality();
133 }
134
createInlineBoxForRenderer(RenderObject * obj,bool isRootLineBox,bool isOnlyRun=false)135 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
136 {
137 if (isRootLineBox)
138 return toRenderBlock(obj)->createAndAppendRootInlineBox();
139
140 if (obj->isText()) {
141 InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
142 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
143 // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.)
144 if (obj->isBR())
145 textBox->setIsText(isOnlyRun || obj->document().inNoQuirksMode());
146 return textBox;
147 }
148
149 if (obj->isBox())
150 return toRenderBox(obj)->createInlineBox();
151
152 return toRenderInline(obj)->createAndAppendInlineFlowBox();
153 }
154
dirtyLineBoxesForRenderer(RenderObject * o,bool fullLayout)155 static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
156 {
157 if (o->isText()) {
158 RenderText* renderText = toRenderText(o);
159 renderText->dirtyLineBoxes(fullLayout);
160 } else
161 toRenderInline(o)->dirtyLineBoxes(fullLayout);
162 }
163
parentIsConstructedOrHaveNext(InlineFlowBox * parentBox)164 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
165 {
166 do {
167 if (parentBox->isConstructed() || parentBox->nextOnLine())
168 return true;
169 parentBox = parentBox->parent();
170 } while (parentBox);
171 return false;
172 }
173
createLineBoxes(RenderObject * obj,const LineInfo & lineInfo,InlineBox * childBox,bool startNewSegment)174 InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment)
175 {
176 // See if we have an unconstructed line box for this object that is also
177 // the last item on the line.
178 unsigned lineDepth = 1;
179 InlineFlowBox* parentBox = 0;
180 InlineFlowBox* result = 0;
181 bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
182 do {
183 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
184
185 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
186
187 // Get the last box we made for this render object.
188 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)->lastLineBox();
189
190 // If this box or its ancestor is constructed then it is from a previous line, and we need
191 // to make a new box for our line. If this box or its ancestor is unconstructed but it has
192 // something following it on the line, then we know we have to make a new box
193 // as well. In this situation our inline has actually been split in two on
194 // the same line (this can happen with very fancy language mixtures).
195 bool constructedNewBox = false;
196 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
197 bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox());
198 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot;
199 if (allowedToConstructNewBox && !canUseExistingParentBox) {
200 // We need to make a new box for this render object. Once
201 // made, we need to place it at the end of the current line.
202 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
203 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
204 parentBox = toInlineFlowBox(newBox);
205 parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
206 parentBox->setIsHorizontal(isHorizontalWritingMode());
207 if (!hasDefaultLineBoxContain)
208 parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
209 constructedNewBox = true;
210 }
211
212 if (constructedNewBox || canUseExistingParentBox) {
213 if (!result)
214 result = parentBox;
215
216 // If we have hit the block itself, then |box| represents the root
217 // inline box for the line, and it doesn't have to be appended to any parent
218 // inline.
219 if (childBox)
220 parentBox->addToLine(childBox);
221
222 if (!constructedNewBox || obj == this)
223 break;
224
225 childBox = parentBox;
226 }
227
228 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
229 // intermediate inline flows.
230 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
231
232 } while (true);
233
234 return result;
235 }
236
237 template <typename CharacterType>
endsWithASCIISpaces(const CharacterType * characters,unsigned pos,unsigned end)238 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
239 {
240 while (isASCIISpace(characters[pos])) {
241 pos++;
242 if (pos >= end)
243 return true;
244 }
245 return false;
246 }
247
reachedEndOfTextRenderer(const BidiRunList<BidiRun> & bidiRuns)248 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
249 {
250 BidiRun* run = bidiRuns.logicallyLastRun();
251 if (!run)
252 return true;
253 unsigned pos = run->stop();
254 RenderObject* r = run->m_object;
255 if (!r->isText() || r->isBR())
256 return false;
257 RenderText* renderText = toRenderText(r);
258 unsigned length = renderText->textLength();
259 if (pos >= length)
260 return true;
261
262 if (renderText->is8Bit())
263 return endsWithASCIISpaces(renderText->characters8(), pos, length);
264 return endsWithASCIISpaces(renderText->characters16(), pos, length);
265 }
266
constructLine(BidiRunList<BidiRun> & bidiRuns,const LineInfo & lineInfo)267 RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
268 {
269 ASSERT(bidiRuns.firstRun());
270
271 bool rootHasSelectedChildren = false;
272 InlineFlowBox* parentBox = 0;
273 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
274 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
275 // Create a box for our object.
276 bool isOnlyRun = (runCount == 1);
277 if (runCount == 2 && !r->m_object->isListMarker())
278 isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
279
280 if (lineInfo.isEmpty())
281 continue;
282
283 InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun);
284 r->m_box = box;
285
286 ASSERT(box);
287 if (!box)
288 continue;
289
290 if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone)
291 rootHasSelectedChildren = true;
292
293 // If we have no parent box yet, or if the run is not simply a sibling,
294 // then we need to construct inline boxes as necessary to properly enclose the
295 // run's inline box. Segments can only be siblings at the root level, as
296 // they are positioned separately.
297 bool runStartsSegment = r->m_startsSegment;
298
299 if (!parentBox || parentBox->renderer() != r->m_object->parent() || runStartsSegment)
300 // Create new inline boxes all the way back to the appropriate insertion point.
301 parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box, runStartsSegment);
302 else {
303 // Append the inline box to this line.
304 parentBox->addToLine(box);
305 }
306
307 bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder;
308 box->setBidiLevel(r->level());
309
310 if (box->isInlineTextBox()) {
311 InlineTextBox* text = toInlineTextBox(box);
312 text->setStart(r->m_start);
313 text->setLen(r->m_stop - r->m_start);
314 text->setDirOverride(r->dirOverride(visuallyOrdered));
315 if (r->m_hasHyphen)
316 text->setHasHyphen(true);
317 }
318 }
319
320 // We should have a root inline box. It should be unconstructed and
321 // be the last continuation of our line list.
322 ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
323
324 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
325 // from the bidi runs walk above has a selection state.
326 if (rootHasSelectedChildren)
327 lastLineBox()->root()->setHasSelectedChildren(true);
328
329 // Set bits on our inline flow boxes that indicate which sides should
330 // paint borders/margins/padding. This knowledge will ultimately be used when
331 // we determine the horizontal positions and widths of all the inline boxes on
332 // the line.
333 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
334 lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
335
336 // Now mark the line boxes as being constructed.
337 lastLineBox()->setConstructed();
338
339 // Return the last line.
340 return lastRootBox();
341 }
342
textAlignmentForLine(bool endsWithSoftBreak) const343 ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
344 {
345 ETextAlign alignment = style()->textAlign();
346 if (endsWithSoftBreak)
347 return alignment;
348
349 if (!RuntimeEnabledFeatures::css3TextEnabled())
350 return (alignment == JUSTIFY) ? TASTART : alignment;
351
352 TextAlignLast alignmentLast = style()->textAlignLast();
353 switch (alignmentLast) {
354 case TextAlignLastStart:
355 return TASTART;
356 case TextAlignLastEnd:
357 return TAEND;
358 case TextAlignLastLeft:
359 return LEFT;
360 case TextAlignLastRight:
361 return RIGHT;
362 case TextAlignLastCenter:
363 return CENTER;
364 case TextAlignLastJustify:
365 return JUSTIFY;
366 case TextAlignLastAuto:
367 if (alignment != JUSTIFY)
368 return alignment;
369 if (style()->textJustify() == TextJustifyDistribute)
370 return JUSTIFY;
371 return TASTART;
372 }
373
374 return alignment;
375 }
376
updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)377 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
378 {
379 // The direction of the block should determine what happens with wide lines.
380 // In particular with RTL blocks, wide lines should still spill out to the left.
381 if (isLeftToRightDirection) {
382 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
383 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
384 return;
385 }
386
387 if (trailingSpaceRun)
388 trailingSpaceRun->m_box->setLogicalWidth(0);
389 else if (totalLogicalWidth > availableLogicalWidth)
390 logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
391 }
392
updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)393 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
394 {
395 // Wide lines spill out of the block based off direction.
396 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
397 // side of the block.
398 if (isLeftToRightDirection) {
399 if (trailingSpaceRun) {
400 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
401 trailingSpaceRun->m_box->setLogicalWidth(0);
402 }
403 if (totalLogicalWidth < availableLogicalWidth)
404 logicalLeft += availableLogicalWidth - totalLogicalWidth;
405 return;
406 }
407
408 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
409 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
410 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
411 } else
412 logicalLeft += availableLogicalWidth - totalLogicalWidth;
413 }
414
updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float availableLogicalWidth)415 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
416 {
417 float trailingSpaceWidth = 0;
418 if (trailingSpaceRun) {
419 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
420 trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
421 trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth));
422 }
423 if (isLeftToRightDirection)
424 logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
425 else
426 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
427 }
428
setMarginsForRubyRun(BidiRun * run,RenderRubyRun * renderer,RenderObject * previousObject,const LineInfo & lineInfo)429 void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo)
430 {
431 int startOverhang;
432 int endOverhang;
433 RenderObject* nextObject = 0;
434 for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
435 if (!runWithNextObject->m_object->isOutOfFlowPositioned() && !runWithNextObject->m_box->isLineBreak()) {
436 nextObject = runWithNextObject->m_object;
437 break;
438 }
439 }
440 renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
441 setMarginStartForChild(renderer, -startOverhang);
442 setMarginEndForChild(renderer, -endOverhang);
443 }
444
setLogicalWidthForTextRun(RootInlineBox * lineBox,BidiRun * run,RenderText * renderer,float xPos,const LineInfo & lineInfo,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)445 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo,
446 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
447 {
448 HashSet<const SimpleFontData*> fallbackFonts;
449 GlyphOverflow glyphOverflow;
450
451 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
452 // Always compute glyph overflow if the block's line-box-contain value is "glyphs".
453 if (lineBox->fitsToGlyphs()) {
454 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
455 // will keep us from computing glyph bounds in nearly all cases.
456 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
457 int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
458 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
459 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
460 int boxAscent = font.fontMetrics().ascent() - baselineShift;
461 int boxDescent = font.fontMetrics().descent() + baselineShift;
462 if (boxAscent > rootDescent || boxDescent > rootAscent)
463 glyphOverflow.computeBounds = true;
464 }
465
466 LayoutUnit hyphenWidth = 0;
467 if (toInlineTextBox(run->m_box)->hasHyphen()) {
468 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
469 hyphenWidth = measureHyphenWidth(renderer, font);
470 }
471 float measuredWidth = 0;
472
473 bool kerningIsEnabled = font.typesettingFeatures() & Kerning;
474
475 #if OS(MACOSX)
476 // FIXME: Having any font feature settings enabled can lead to selection gaps on
477 // Chromium-mac. https://bugs.webkit.org/show_bug.cgi?id=113418
478 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath() && !font.fontDescription().featureSettings();
479 #else
480 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
481 #endif
482
483 // Since we don't cache glyph overflows, we need to re-measure the run if
484 // the style is linebox-contain: glyph.
485
486 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
487 int lastEndOffset = run->m_start;
488 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
489 const WordMeasurement& wordMeasurement = wordMeasurements[i];
490 if (wordMeasurement.width <=0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
491 continue;
492 if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
493 continue;
494
495 lastEndOffset = wordMeasurement.endOffset;
496 if (kerningIsEnabled && lastEndOffset == run->m_stop) {
497 int wordLength = lastEndOffset - wordMeasurement.startOffset;
498 measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos, lineInfo.isFirstLine());
499 if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasurement.startOffset) == ' ')
500 measuredWidth += renderer->style()->wordSpacing();
501 } else
502 measuredWidth += wordMeasurement.width;
503 if (!wordMeasurement.fallbackFonts.isEmpty()) {
504 HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
505 for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
506 fallbackFonts.add(*it);
507 }
508 }
509 if (measuredWidth && lastEndOffset != run->m_stop) {
510 // If we don't have enough cached data, we'll measure the run again.
511 measuredWidth = 0;
512 fallbackFonts.clear();
513 }
514 }
515
516 if (!measuredWidth)
517 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow);
518
519 run->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
520 if (!fallbackFonts.isEmpty()) {
521 ASSERT(run->m_box->isText());
522 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
523 ASSERT(it->value.first.isEmpty());
524 copyToVector(fallbackFonts, it->value.first);
525 run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
526 }
527 if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
528 ASSERT(run->m_box->isText());
529 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
530 it->value.second = glyphOverflow;
531 run->m_box->clearKnownToHaveNoOverflow();
532 }
533 }
534
computeExpansionForJustifiedText(BidiRun * firstRun,BidiRun * trailingSpaceRun,Vector<unsigned,16> & expansionOpportunities,unsigned expansionOpportunityCount,float & totalLogicalWidth,float availableLogicalWidth)535 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
536 {
537 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
538 return;
539
540 size_t i = 0;
541 for (BidiRun* r = firstRun; r; r = r->next()) {
542 // This method is called once per segment, do not move past the current segment.
543 if (r->m_startsSegment)
544 break;
545 if (!r->m_box || r == trailingSpaceRun)
546 continue;
547
548 if (r->m_object->isText()) {
549 unsigned opportunitiesInRun = expansionOpportunities[i++];
550
551 ASSERT(opportunitiesInRun <= expansionOpportunityCount);
552
553 // Only justify text if whitespace is collapsed.
554 if (r->m_object->style()->collapseWhiteSpace()) {
555 InlineTextBox* textBox = toInlineTextBox(r->m_box);
556 int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
557 textBox->setExpansion(expansion);
558 totalLogicalWidth += expansion;
559 }
560 expansionOpportunityCount -= opportunitiesInRun;
561 if (!expansionOpportunityCount)
562 break;
563 }
564 }
565 }
566
updateLogicalWidthForAlignment(const ETextAlign & textAlign,const RootInlineBox * rootInlineBox,BidiRun * trailingSpaceRun,float & logicalLeft,float & totalLogicalWidth,float & availableLogicalWidth,int expansionOpportunityCount)567 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount)
568 {
569 TextDirection direction;
570 if (rootInlineBox && rootInlineBox->renderer()->style()->unicodeBidi() == Plaintext)
571 direction = rootInlineBox->direction();
572 else
573 direction = style()->direction();
574
575 // Armed with the total width of the line (without justification),
576 // we now examine our text-align property in order to determine where to position the
577 // objects horizontally. The total width of the line can be increased if we end up
578 // justifying text.
579 switch (textAlign) {
580 case LEFT:
581 case WEBKIT_LEFT:
582 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
583 break;
584 case RIGHT:
585 case WEBKIT_RIGHT:
586 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
587 break;
588 case CENTER:
589 case WEBKIT_CENTER:
590 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
591 break;
592 case JUSTIFY:
593 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
594 if (expansionOpportunityCount) {
595 if (trailingSpaceRun) {
596 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
597 trailingSpaceRun->m_box->setLogicalWidth(0);
598 }
599 break;
600 }
601 // Fall through
602 case TASTART:
603 if (direction == LTR)
604 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
605 else
606 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
607 break;
608 case TAEND:
609 if (direction == LTR)
610 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
611 else
612 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
613 break;
614 }
615 }
616
updateLogicalInlinePositions(RenderBlockFlow * block,float & lineLogicalLeft,float & lineLogicalRight,float & availableLogicalWidth,bool firstLine,IndentTextOrNot shouldIndentText,LayoutUnit boxLogicalHeight)617 static void updateLogicalInlinePositions(RenderBlockFlow* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
618 {
619 LayoutUnit lineLogicalHeight = block->minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
620 lineLogicalLeft = block->logicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
621 // FIXME: This shouldn't be pixel snapped once multicolumn layout has been updated to correctly carry over subpixel values.
622 // https://bugs.webkit.org/show_bug.cgi?id=105461
623 lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
624 availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
625 }
626
computeInlineDirectionPositionsForLine(RootInlineBox * lineBox,const LineInfo & lineInfo,BidiRun * firstRun,BidiRun * trailingSpaceRun,bool reachedEnd,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)627 void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
628 GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
629 {
630 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
631
632 // 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
633 // box is only affected if it is the first child of its parent element."
634 // CSS3 "text-indent", "each-line" affects the first line of the block container as well as each line after a forced line break,
635 // but does not affect lines after a soft wrap break.
636 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent()->firstChild() != this);
637 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox()->endsWithBreak();
638 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLineBreak, style());
639 float lineLogicalLeft;
640 float lineLogicalRight;
641 float availableLogicalWidth;
642 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
643 bool needsWordSpacing;
644 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo();
645 if (shapeInsideInfo && shapeInsideInfo->hasSegments()) {
646 BidiRun* segmentStart = firstRun;
647 const SegmentList& segments = shapeInsideInfo->segments();
648 float logicalLeft = max<float>(segments[0].logicalLeft, lineLogicalLeft);
649 float logicalRight = min<float>(segments[0].logicalRight, lineLogicalRight);
650 float startLogicalLeft = logicalLeft;
651 float endLogicalRight = logicalLeft;
652 float minLogicalLeft = logicalLeft;
653 float maxLogicalRight = logicalLeft;
654 lineBox->beginPlacingBoxRangesInInlineDirection(logicalLeft);
655 for (size_t i = 0; i < segments.size(); i++) {
656 if (i) {
657 logicalLeft = max<float>(segments[i].logicalLeft, lineLogicalLeft);
658 logicalRight = min<float>(segments[i].logicalRight, lineLogicalRight);
659 }
660 availableLogicalWidth = logicalRight - logicalLeft;
661 BidiRun* newSegmentStart = computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, logicalLeft, availableLogicalWidth, segmentStart, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
662 needsWordSpacing = false;
663 endLogicalRight = lineBox->placeBoxRangeInInlineDirection(segmentStart->m_box, newSegmentStart ? newSegmentStart->m_box : 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap);
664 if (!newSegmentStart || !newSegmentStart->next())
665 break;
666 ASSERT(newSegmentStart->m_startsSegment);
667 // Discard the empty segment start marker bidi runs
668 segmentStart = newSegmentStart->next();
669 }
670 lineBox->endPlacingBoxRangesInInlineDirection(startLogicalLeft, endLogicalRight, minLogicalLeft, maxLogicalRight);
671 return;
672 }
673
674 if (firstRun && firstRun->m_object->isReplaced()) {
675 RenderBox* renderBox = toRenderBox(firstRun->m_object);
676 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight());
677 }
678
679 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
680 // The widths of all runs are now known. We can now place every inline box (and
681 // compute accurate widths for the inline flow boxes).
682 needsWordSpacing = false;
683 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap);
684 }
685
computeInlineDirectionPositionsForSegment(RootInlineBox * lineBox,const LineInfo & lineInfo,ETextAlign textAlign,float & logicalLeft,float & availableLogicalWidth,BidiRun * firstRun,BidiRun * trailingSpaceRun,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache,WordMeasurements & wordMeasurements)686 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
687 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
688 WordMeasurements& wordMeasurements)
689 {
690 bool needsWordSpacing = false;
691 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
692 unsigned expansionOpportunityCount = 0;
693 bool isAfterExpansion = true;
694 Vector<unsigned, 16> expansionOpportunities;
695 RenderObject* previousObject = 0;
696 TextJustify textJustify = style()->textJustify();
697
698 BidiRun* r = firstRun;
699 for (; r; r = r->next()) {
700 // Once we have reached the start of the next segment, we have finished
701 // computing the positions for this segment's contents.
702 if (r->m_startsSegment)
703 break;
704 if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak())
705 continue; // Positioned objects are only participating to figure out their
706 // correct static x position. They have no effect on the width.
707 // Similarly, line break boxes have no effect on the width.
708 if (r->m_object->isText()) {
709 RenderText* rt = toRenderText(r->m_object);
710 if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) {
711 if (!isAfterExpansion)
712 toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
713 unsigned opportunitiesInRun;
714 if (rt->is8Bit())
715 opportunitiesInRun = Font::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
716 else
717 opportunitiesInRun = Font::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
718 expansionOpportunities.append(opportunitiesInRun);
719 expansionOpportunityCount += opportunitiesInRun;
720 }
721
722 if (int length = rt->textLength()) {
723 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start)))
724 totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().wordSpacing();
725 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length;
726 }
727
728 setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
729 } else {
730 isAfterExpansion = false;
731 if (!r->m_object->isRenderInline()) {
732 RenderBox* renderBox = toRenderBox(r->m_object);
733 if (renderBox->isRubyRun())
734 setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
735 r->m_box->setLogicalWidth(logicalWidthForChild(renderBox));
736 totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
737 }
738 }
739
740 totalLogicalWidth += r->m_box->logicalWidth();
741 previousObject = r->m_object;
742 }
743
744 if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
745 expansionOpportunities.last()--;
746 expansionOpportunityCount--;
747 }
748
749 updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
750
751 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
752
753 return r;
754 }
755
computeBlockDirectionPositionsForLine(RootInlineBox * lineBox,BidiRun * firstRun,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache)756 void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
757 VerticalPositionCache& verticalPositionCache)
758 {
759 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
760
761 // Now make sure we place replaced render objects correctly.
762 for (BidiRun* r = firstRun; r; r = r->next()) {
763 ASSERT(r->m_box);
764 if (!r->m_box)
765 continue; // Skip runs with no line boxes.
766
767 // Align positioned boxes with the top of the line box. This is
768 // a reasonable approximation of an appropriate y position.
769 if (r->m_object->isOutOfFlowPositioned())
770 r->m_box->setLogicalTop(logicalHeight());
771
772 // Position is used to properly position both replaced elements and
773 // to update the static normal flow x/y of positioned elements.
774 if (r->m_object->isText())
775 toRenderText(r->m_object)->positionLineBox(r->m_box);
776 else if (r->m_object->isBox())
777 toRenderBox(r->m_object)->positionLineBox(r->m_box);
778 }
779 // Positioned objects and zero-length text nodes destroy their boxes in
780 // position(), which unnecessarily dirties the line.
781 lineBox->markDirty(false);
782 }
783
isCollapsibleSpace(UChar character,RenderText * renderer)784 static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
785 {
786 if (character == ' ' || character == '\t' || character == softHyphen)
787 return true;
788 if (character == '\n')
789 return !renderer->style()->preserveNewline();
790 return false;
791 }
792
793 template <typename CharacterType>
findFirstTrailingSpace(RenderText * lastText,const CharacterType * characters,int start,int stop)794 static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterType* characters, int start, int stop)
795 {
796 int firstSpace = stop;
797 while (firstSpace > start) {
798 UChar current = characters[firstSpace - 1];
799 if (!isCollapsibleSpace(current, lastText))
800 break;
801 firstSpace--;
802 }
803
804 return firstSpace;
805 }
806
handleTrailingSpaces(BidiRunList<BidiRun> & bidiRuns,BidiContext * currentContext)807 inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext)
808 {
809 if (!bidiRuns.runCount()
810 || !bidiRuns.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()
811 || !bidiRuns.logicallyLastRun()->m_object->style()->autoWrap())
812 return 0;
813
814 BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun();
815 RenderObject* lastObject = trailingSpaceRun->m_object;
816 if (!lastObject->isText())
817 return 0;
818
819 RenderText* lastText = toRenderText(lastObject);
820 int firstSpace;
821 if (lastText->is8Bit())
822 firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop());
823 else
824 firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop());
825
826 if (firstSpace == trailingSpaceRun->stop())
827 return 0;
828
829 TextDirection direction = style()->direction();
830 bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun());
831 if (firstSpace != trailingSpaceRun->start()) {
832 BidiContext* baseContext = currentContext;
833 while (BidiContext* parent = baseContext->parent())
834 baseContext = parent;
835
836 BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
837 trailingSpaceRun->m_stop = firstSpace;
838 if (direction == LTR)
839 bidiRuns.addRun(newTrailingRun);
840 else
841 bidiRuns.prependRun(newTrailingRun);
842 trailingSpaceRun = newTrailingRun;
843 return trailingSpaceRun;
844 }
845 if (!shouldReorder)
846 return trailingSpaceRun;
847
848 if (direction == LTR) {
849 bidiRuns.moveRunToEnd(trailingSpaceRun);
850 trailingSpaceRun->m_level = 0;
851 } else {
852 bidiRuns.moveRunToBeginning(trailingSpaceRun);
853 trailingSpaceRun->m_level = 1;
854 }
855 return trailingSpaceRun;
856 }
857
appendFloatingObjectToLastLine(FloatingObject * floatingObject)858 void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
859 {
860 ASSERT(!floatingObject->originatingLine());
861 floatingObject->setOriginatingLine(lastRootBox());
862 lastRootBox()->appendFloat(floatingObject->renderer());
863 }
864
865 // FIXME: This should be a BidiStatus constructor or create method.
statusWithDirection(TextDirection textDirection,bool isOverride)866 static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride)
867 {
868 WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft;
869 RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
870
871 // This copies BidiStatus and may churn the ref on BidiContext I doubt it matters.
872 return BidiStatus(direction, direction, direction, context.release());
873 }
874
setupResolverToResumeInIsolate(InlineBidiResolver & resolver,RenderObject * root,RenderObject * startObject)875 static inline void setupResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
876 {
877 if (root != startObject) {
878 RenderObject* parent = startObject->parent();
879 setupResolverToResumeInIsolate(resolver, root, parent);
880 notifyObserverEnteredObject(&resolver, startObject);
881 }
882 }
883
restoreIsolatedMidpointStates(InlineBidiResolver & topResolver,InlineBidiResolver & isolatedResolver)884 static void restoreIsolatedMidpointStates(InlineBidiResolver& topResolver, InlineBidiResolver& isolatedResolver)
885 {
886 while (!isolatedResolver.isolatedRuns().isEmpty()) {
887 BidiRun* run = isolatedResolver.isolatedRuns().last();
888 isolatedResolver.isolatedRuns().removeLast();
889 topResolver.setMidpointStateForIsolatedRun(run, isolatedResolver.midpointStateForIsolatedRun(run));
890 }
891 }
892
893 // FIXME: BidiResolver should have this logic.
constructBidiRunsForSegment(InlineBidiResolver & topResolver,BidiRunList<BidiRun> & bidiRuns,const InlineIterator & endOfRuns,VisualDirectionOverride override,bool previousLineBrokeCleanly,bool isNewUBAParagraph)894 static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly, bool isNewUBAParagraph)
895 {
896 // FIXME: We should pass a BidiRunList into createBidiRunsForLine instead
897 // of the resolver owning the runs.
898 ASSERT(&topResolver.runs() == &bidiRuns);
899 ASSERT(topResolver.position() != endOfRuns);
900 RenderObject* currentRoot = topResolver.position().root();
901 topResolver.createBidiRunsForLine(endOfRuns, override, previousLineBrokeCleanly);
902
903 while (!topResolver.isolatedRuns().isEmpty()) {
904 // It does not matter which order we resolve the runs as long as we resolve them all.
905 BidiRun* isolatedRun = topResolver.isolatedRuns().last();
906 topResolver.isolatedRuns().removeLast();
907
908 RenderObject* startObj = isolatedRun->object();
909
910 // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated).
911 // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the
912 // tree to see which parent inline is the isolate. We could change enterIsolate
913 // to take a RenderObject and do this logic there, but that would be a layering
914 // violation for BidiResolver (which knows nothing about RenderObject).
915 RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObj, currentRoot));
916 ASSERT(isolatedInline);
917
918 InlineBidiResolver isolatedResolver;
919 LineMidpointState& isolatedLineMidpointState = isolatedResolver.midpointState();
920 isolatedLineMidpointState = topResolver.midpointStateForIsolatedRun(isolatedRun);
921 EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
922 TextDirection direction = isolatedInline->style()->direction();
923 if (unicodeBidi == Plaintext) {
924 if (isNewUBAParagraph)
925 direction = determinePlaintextDirectionality(isolatedInline, startObj);
926 else
927 direction = determinePlaintextDirectionality(isolatedInline);
928 } else {
929 ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
930 direction = isolatedInline->style()->direction();
931 }
932 isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi)));
933
934 setupResolverToResumeInIsolate(isolatedResolver, isolatedInline, startObj);
935
936 // The starting position is the beginning of the first run within the isolate that was identified
937 // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the
938 // first run within the isolate.
939 InlineIterator iter = InlineIterator(isolatedInline, startObj, isolatedRun->m_start);
940 isolatedResolver.setPositionIgnoringNestedIsolates(iter);
941 // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns().
942 // FIXME: What should end and previousLineBrokeCleanly be?
943 // rniwa says previousLineBrokeCleanly is just a WinIE hack and could always be false here?
944 isolatedResolver.createBidiRunsForLine(endOfRuns, NoVisualOverride, previousLineBrokeCleanly);
945
946 ASSERT(isolatedResolver.runs().runCount());
947 if (isolatedResolver.runs().runCount())
948 bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs());
949
950 // If we encountered any nested isolate runs, just move them
951 // to the top resolver's list for later processing.
952 if (!isolatedResolver.isolatedRuns().isEmpty()) {
953 topResolver.isolatedRuns().append(isolatedResolver.isolatedRuns());
954 currentRoot = isolatedInline;
955 restoreIsolatedMidpointStates(topResolver, isolatedResolver);
956 }
957 }
958 }
959
segmentIsEmpty(const InlineIterator & segmentStart,const InlineIterator & segmentEnd)960 static inline bool segmentIsEmpty(const InlineIterator& segmentStart, const InlineIterator& segmentEnd)
961 {
962 return segmentStart == segmentEnd;
963 }
964
constructBidiRunsForLine(const RenderBlockFlow * block,InlineBidiResolver & topResolver,BidiRunList<BidiRun> & bidiRuns,const InlineIterator & endOfLine,VisualDirectionOverride override,bool previousLineBrokeCleanly,bool isNewUBAParagraph)965 static inline void constructBidiRunsForLine(const RenderBlockFlow* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly, bool isNewUBAParagraph)
966 {
967 ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo();
968 if (!shapeInsideInfo || !shapeInsideInfo->hasSegments()) {
969 constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly, isNewUBAParagraph);
970 return;
971 }
972
973 const SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges();
974 ASSERT(segmentRanges.size());
975
976 for (size_t i = 0; i < segmentRanges.size(); i++) {
977 LineSegmentIterator iterator = segmentRanges[i].start;
978 InlineIterator segmentStart(iterator.root, iterator.object, iterator.offset);
979 iterator = segmentRanges[i].end;
980 InlineIterator segmentEnd(iterator.root, iterator.object, iterator.offset);
981 if (i) {
982 ASSERT(segmentStart.object());
983 BidiRun* segmentMarker = createRun(segmentStart.m_pos, segmentStart.m_pos, segmentStart.object(), topResolver);
984 segmentMarker->m_startsSegment = true;
985 bidiRuns.addRun(segmentMarker);
986 // Do not collapse midpoints between segments
987 topResolver.midpointState().betweenMidpoints = false;
988 }
989 if (!segmentIsEmpty(segmentStart, segmentEnd)) {
990 topResolver.setPosition(segmentStart, numberOfIsolateAncestors(segmentStart));
991 constructBidiRunsForSegment(topResolver, bidiRuns, segmentEnd, override, previousLineBrokeCleanly, isNewUBAParagraph);
992 }
993 }
994 }
995
996 // 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)997 RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
998 {
999 if (!bidiRuns.runCount())
1000 return 0;
1001
1002 // FIXME: Why is this only done when we had runs?
1003 lineInfo.setLastLine(!end.object());
1004
1005 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
1006 if (!lineBox)
1007 return 0;
1008
1009 lineBox->setBidiLevel(bidiLevel);
1010 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
1011
1012 bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
1013
1014 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1015
1016 // Now we position all of our text runs horizontally.
1017 if (!isSVGRootInlineBox)
1018 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMeasurements);
1019
1020 // Now position our text runs vertically.
1021 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache);
1022
1023 // SVG text layout code computes vertical & horizontal positions on its own.
1024 // Note that we still need to execute computeVerticalPositionsForLine() as
1025 // it calls InlineTextBox::positionLineBox(), which tracks whether the box
1026 // contains reversed text or not. If we wouldn't do that editing and thus
1027 // text selection in RTL boxes would not work as expected.
1028 if (isSVGRootInlineBox) {
1029 ASSERT(isSVGText());
1030 toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
1031 }
1032
1033 // Compute our overflow now.
1034 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap);
1035
1036 return lineBox;
1037 }
1038
1039 // Like LayoutState for layout(), LineLayoutState keeps track of global information
1040 // during an entire linebox tree layout pass (aka layoutInlineChildren).
1041 class LineLayoutState {
1042 public:
LineLayoutState(bool fullLayout,LayoutUnit & repaintLogicalTop,LayoutUnit & repaintLogicalBottom,RenderFlowThread * flowThread)1043 LineLayoutState(bool fullLayout, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, RenderFlowThread* flowThread)
1044 : m_lastFloat(0)
1045 , m_endLine(0)
1046 , m_floatIndex(0)
1047 , m_endLineLogicalTop(0)
1048 , m_endLineMatched(false)
1049 , m_checkForFloatsFromLastLine(false)
1050 , m_isFullLayout(fullLayout)
1051 , m_repaintLogicalTop(repaintLogicalTop)
1052 , m_repaintLogicalBottom(repaintLogicalBottom)
1053 , m_adjustedLogicalLineTop(0)
1054 , m_usesRepaintBounds(false)
1055 , m_flowThread(flowThread)
1056 { }
1057
markForFullLayout()1058 void markForFullLayout() { m_isFullLayout = true; }
isFullLayout() const1059 bool isFullLayout() const { return m_isFullLayout; }
1060
usesRepaintBounds() const1061 bool usesRepaintBounds() const { return m_usesRepaintBounds; }
1062
setRepaintRange(LayoutUnit logicalHeight)1063 void setRepaintRange(LayoutUnit logicalHeight)
1064 {
1065 m_usesRepaintBounds = true;
1066 m_repaintLogicalTop = m_repaintLogicalBottom = logicalHeight;
1067 }
1068
updateRepaintRangeFromBox(RootInlineBox * box,LayoutUnit paginationDelta=0)1069 void updateRepaintRangeFromBox(RootInlineBox* box, LayoutUnit paginationDelta = 0)
1070 {
1071 m_usesRepaintBounds = true;
1072 m_repaintLogicalTop = min(m_repaintLogicalTop, box->logicalTopVisualOverflow() + min<LayoutUnit>(paginationDelta, 0));
1073 m_repaintLogicalBottom = max(m_repaintLogicalBottom, box->logicalBottomVisualOverflow() + max<LayoutUnit>(paginationDelta, 0));
1074 }
1075
endLineMatched() const1076 bool endLineMatched() const { return m_endLineMatched; }
setEndLineMatched(bool endLineMatched)1077 void setEndLineMatched(bool endLineMatched) { m_endLineMatched = endLineMatched; }
1078
checkForFloatsFromLastLine() const1079 bool checkForFloatsFromLastLine() const { return m_checkForFloatsFromLastLine; }
setCheckForFloatsFromLastLine(bool check)1080 void setCheckForFloatsFromLastLine(bool check) { m_checkForFloatsFromLastLine = check; }
1081
lineInfo()1082 LineInfo& lineInfo() { return m_lineInfo; }
lineInfo() const1083 const LineInfo& lineInfo() const { return m_lineInfo; }
1084
endLineLogicalTop() const1085 LayoutUnit endLineLogicalTop() const { return m_endLineLogicalTop; }
setEndLineLogicalTop(LayoutUnit logicalTop)1086 void setEndLineLogicalTop(LayoutUnit logicalTop) { m_endLineLogicalTop = logicalTop; }
1087
endLine() const1088 RootInlineBox* endLine() const { return m_endLine; }
setEndLine(RootInlineBox * line)1089 void setEndLine(RootInlineBox* line) { m_endLine = line; }
1090
lastFloat() const1091 FloatingObject* lastFloat() const { return m_lastFloat; }
setLastFloat(FloatingObject * lastFloat)1092 void setLastFloat(FloatingObject* lastFloat) { m_lastFloat = lastFloat; }
1093
floats()1094 Vector<RenderBlockFlow::FloatWithRect>& floats() { return m_floats; }
1095
floatIndex() const1096 unsigned floatIndex() const { return m_floatIndex; }
setFloatIndex(unsigned floatIndex)1097 void setFloatIndex(unsigned floatIndex) { m_floatIndex = floatIndex; }
1098
adjustedLogicalLineTop() const1099 LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; }
setAdjustedLogicalLineTop(LayoutUnit value)1100 void setAdjustedLogicalLineTop(LayoutUnit value) { m_adjustedLogicalLineTop = value; }
1101
flowThread() const1102 RenderFlowThread* flowThread() const { return m_flowThread; }
setFlowThread(RenderFlowThread * thread)1103 void setFlowThread(RenderFlowThread* thread) { m_flowThread = thread; }
1104
1105 private:
1106 Vector<RenderBlockFlow::FloatWithRect> m_floats;
1107 FloatingObject* m_lastFloat;
1108 RootInlineBox* m_endLine;
1109 LineInfo m_lineInfo;
1110 unsigned m_floatIndex;
1111 LayoutUnit m_endLineLogicalTop;
1112 bool m_endLineMatched;
1113 bool m_checkForFloatsFromLastLine;
1114
1115 bool m_isFullLayout;
1116
1117 // FIXME: Should this be a range object instead of two ints?
1118 LayoutUnit& m_repaintLogicalTop;
1119 LayoutUnit& m_repaintLogicalBottom;
1120
1121 LayoutUnit m_adjustedLogicalLineTop;
1122
1123 bool m_usesRepaintBounds;
1124
1125 RenderFlowThread* m_flowThread;
1126 };
1127
deleteLineRange(LineLayoutState & layoutState,RootInlineBox * startLine,RootInlineBox * stopLine=0)1128 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
1129 {
1130 RootInlineBox* boxToDelete = startLine;
1131 while (boxToDelete && boxToDelete != stopLine) {
1132 layoutState.updateRepaintRangeFromBox(boxToDelete);
1133 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree().
1134 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing.
1135 RootInlineBox* next = boxToDelete->nextRootBox();
1136 boxToDelete->deleteLine();
1137 boxToDelete = next;
1138 }
1139 }
1140
layoutRunsAndFloats(LineLayoutState & layoutState,bool hasInlineChild)1141 void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild)
1142 {
1143 // We want to skip ahead to the first dirty line
1144 InlineBidiResolver resolver;
1145 RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
1146
1147 unsigned consecutiveHyphenatedLines = 0;
1148 if (startLine) {
1149 for (RootInlineBox* line = startLine->prevRootBox(); line && line->isHyphenated(); line = line->prevRootBox())
1150 consecutiveHyphenatedLines++;
1151 }
1152
1153 // FIXME: This would make more sense outside of this function, but since
1154 // determineStartPosition can change the fullLayout flag we have to do this here. Failure to call
1155 // determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
1156 if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) {
1157 // Mark as needing a full layout to force us to repaint. Allow regions
1158 // to reflow as needed.
1159 setNeedsLayout(MarkOnlyThis);
1160
1161 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) {
1162 setShouldDoFullRepaintAfterLayout(true);
1163 } else {
1164 RenderView* v = view();
1165 if (v && !v->doingFullRepaint() && hasLayer()) {
1166 // Because we waited until we were already inside layout to discover
1167 // that the block really needed a full layout, we missed our chance to repaint the layer
1168 // before layout started. Luckily the layer has cached the repaint rect for its original
1169 // position and size, and so we can use that to make a repaint happen now.
1170 repaintUsingContainer(containerForRepaint(), pixelSnappedIntRect(layer()->repainter().repaintRect()));
1171 }
1172 }
1173 }
1174
1175 if (containsFloats())
1176 layoutState.setLastFloat(m_floatingObjects->set().last());
1177
1178 // We also find the first clean line and extract these lines. We will add them back
1179 // if we determine that we're able to synchronize after handling all our dirty lines.
1180 InlineIterator cleanLineStart;
1181 BidiStatus cleanLineBidiStatus;
1182 if (!layoutState.isFullLayout() && startLine)
1183 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBidiStatus);
1184
1185 if (startLine) {
1186 if (!layoutState.usesRepaintBounds())
1187 layoutState.setRepaintRange(logicalHeight());
1188 deleteLineRange(layoutState, startLine);
1189 }
1190
1191 if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
1192 // If the last line before the start line ends with a line break that clear floats,
1193 // adjust the height accordingly.
1194 // A line break can be either the first or the last object on a line, depending on its direction.
1195 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
1196 RenderObject* lastObject = lastLeafChild->renderer();
1197 if (!lastObject->isBR())
1198 lastObject = lastRootBox()->firstLeafChild()->renderer();
1199 if (lastObject->isBR()) {
1200 EClear clear = lastObject->style()->clear();
1201 if (clear != CNONE)
1202 clearFloats(clear);
1203 }
1204 }
1205 }
1206
1207 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines);
1208 linkToEndLineIfNeeded(layoutState);
1209 repaintDirtyFloats(layoutState.floats());
1210 }
1211
RenderTextInfo()1212 RenderTextInfo::RenderTextInfo()
1213 : m_text(0)
1214 , m_font(0)
1215 {
1216 }
1217
~RenderTextInfo()1218 RenderTextInfo::~RenderTextInfo()
1219 {
1220 }
1221
1222 // 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)1223 inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
1224 {
1225 removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
1226 setLogicalHeight(newLogicalHeight);
1227 resolver.setPositionIgnoringNestedIsolates(oldEnd);
1228 return oldEnd;
1229 }
1230
adjustLogicalLineTop(ShapeInsideInfo * shapeInsideInfo,InlineIterator start,InlineIterator end,const WordMeasurements & wordMeasurements)1231 static inline LayoutUnit adjustLogicalLineTop(ShapeInsideInfo* shapeInsideInfo, InlineIterator start, InlineIterator end, const WordMeasurements& wordMeasurements)
1232 {
1233 if (!shapeInsideInfo || end != start)
1234 return 0;
1235
1236 float minWidth = firstPositiveWidth(wordMeasurements);
1237 ASSERT(minWidth || wordMeasurements.isEmpty());
1238 if (minWidth > 0 && shapeInsideInfo->adjustLogicalLineTop(minWidth))
1239 return shapeInsideInfo->logicalLineTop();
1240
1241 return shapeInsideInfo->shapeLogicalBottom();
1242 }
1243
pushShapeContentOverflowBelowTheContentBox(RenderBlockFlow * block,ShapeInsideInfo * shapeInsideInfo,LayoutUnit lineTop,LayoutUnit lineHeight)1244 static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlockFlow* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight)
1245 {
1246 ASSERT(shapeInsideInfo);
1247
1248 LayoutUnit logicalLineBottom = lineTop + lineHeight;
1249 LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom();
1250 LayoutUnit shapeContainingBlockHeight = shapeInsideInfo->shapeContainingBlockHeight();
1251
1252 bool isOverflowPositionedAlready = (shapeContainingBlockHeight - shapeInsideInfo->owner()->borderAndPaddingAfter() + lineHeight) <= lineTop;
1253
1254 // If the last line overlaps with the shape, we don't need the segments anymore
1255 if (lineTop < shapeLogicalBottom && shapeLogicalBottom < logicalLineBottom)
1256 shapeInsideInfo->clearSegments();
1257 if (logicalLineBottom <= shapeLogicalBottom || !shapeContainingBlockHeight || isOverflowPositionedAlready)
1258 return;
1259
1260 LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockHeight - (lineTop + shapeInsideInfo->owner()->borderAndPaddingAfter()));
1261 block->setLogicalHeight(newLogicalHeight);
1262 }
1263
updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo * & shapeInsideInfo,const LayoutSize & logicalOffsetFromShapeContainer,LineLayoutState & layoutState)1264 void RenderBlockFlow::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, const LayoutSize& logicalOffsetFromShapeContainer, LineLayoutState& layoutState)
1265 {
1266 if (layoutState.flowThread())
1267 return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState);
1268
1269 if (!shapeInsideInfo)
1270 return;
1271
1272 LayoutUnit lineTop = logicalHeight() + logicalOffsetFromShapeContainer.height();
1273 LayoutUnit lineLeft = logicalOffsetFromShapeContainer.width();
1274 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
1275
1276 // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which case these segments may be incorrect.
1277 shapeInsideInfo->updateSegmentsForLine(LayoutSize(lineLeft, lineTop), lineHeight);
1278
1279 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
1280 }
1281
updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo * & shapeInsideInfo,LineLayoutState & layoutState)1282 void RenderBlockFlow::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState)
1283 {
1284 ASSERT(layoutState.flowThread());
1285
1286 RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight());
1287 if (!currentRegion || !currentRegion->logicalHeight())
1288 return;
1289
1290 shapeInsideInfo = currentRegion->shapeInsideInfo();
1291
1292 RenderRegion* nextRegion = 0;
1293 if (!currentRegion->isLastRegion()) {
1294 RenderRegionList regionList = layoutState.flowThread()->renderRegionList();
1295 RenderRegionList::const_iterator it = regionList.find(currentRegion);
1296 nextRegion = *(++it);
1297 }
1298
1299 // We only want to deal regions with shapes, so we check if the next region has a shape
1300 if (!shapeInsideInfo && nextRegion && !nextRegion->shapeInsideInfo())
1301 return;
1302
1303 LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
1304 LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
1305 LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
1306 LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
1307 LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
1308
1309 LayoutUnit shapeBottomInFlowThread = LayoutUnit::max();
1310 if (shapeInsideInfo)
1311 shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent();
1312
1313 bool lineOverLapsWithShapeBottom = shapeBottomInFlowThread < logicalLineBottomInFlowThread;
1314 bool lineOverLapsWithRegionBottom = logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread;
1315 bool overFlowsToNextRegion = nextRegion && (lineOverLapsWithShapeBottom || lineOverLapsWithRegionBottom);
1316
1317 // If the line is between two shapes/regions we position the line to the top of the next shape/region
1318 if (overFlowsToNextRegion) {
1319 ASSERT(currentRegion != nextRegion);
1320 LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread;
1321 setLogicalHeight(logicalHeight() + deltaToNextRegion);
1322
1323 currentRegion = nextRegion;
1324 shapeInsideInfo = currentRegion->shapeInsideInfo();
1325
1326 logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
1327 logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
1328 logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
1329 logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
1330 }
1331
1332 if (!shapeInsideInfo)
1333 return;
1334
1335 bool isFirstLineInRegion = logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight);
1336 bool isFirstLineAdjusted = (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore());
1337 // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape
1338 if (isFirstLineInRegion || isFirstLineAdjusted) {
1339 LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop();
1340 if (!shapeTopOffset && (shapeInsideInfo->shapeLogicalTop() > 0))
1341 shapeTopOffset = shapeInsideInfo->shapeLogicalTop();
1342
1343 LayoutUnit shapePositionInFlowThread = currentRegion->logicalTopForFlowThreadContent() + shapeTopOffset;
1344 LayoutUnit shapeTopLineTopDelta = shapePositionInFlowThread - logicalLineTopInFlowThread - currentRegion->borderAndPaddingBefore();
1345
1346 setLogicalHeight(logicalHeight() + shapeTopLineTopDelta);
1347 logicalLineTopInFlowThread += shapeTopLineTopDelta;
1348 layoutState.setAdjustedLogicalLineTop(0);
1349 }
1350
1351 LayoutUnit lineTop = logicalLineTopInFlowThread - currentRegion->logicalTopForFlowThreadContent() + currentRegion->borderAndPaddingBefore();
1352 // FIXME: Shape inside on a region does not yet take into account its padding for nested flow blocks
1353 shapeInsideInfo->updateSegmentsForLine(LayoutSize(0, lineTop), lineHeight);
1354
1355 if (currentRegion->isLastRegion())
1356 pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
1357 }
1358
adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo * shapeInsideInfo,LayoutUnit absoluteLogicalTop,LineLayoutState & layoutState,InlineBidiResolver & resolver,FloatingObject * lastFloatFromPreviousLine,InlineIterator & end,WordMeasurements & wordMeasurements)1359 bool RenderBlockFlow::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements)
1360 {
1361 LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements);
1362
1363 if (shapeInsideInfo && containsFloats()) {
1364 lastFloatFromPreviousLine = m_floatingObjects->set().last();
1365 if (!wordMeasurements.size()) {
1366 LayoutUnit floatLogicalTopOffset = shapeInsideInfo->computeFirstFitPositionForFloat(logicalSizeForFloat(lastFloatFromPreviousLine));
1367 if (logicalHeight() < floatLogicalTopOffset)
1368 adjustedLogicalLineTop = floatLogicalTopOffset;
1369 }
1370 }
1371
1372 if (!adjustedLogicalLineTop)
1373 return false;
1374
1375 LayoutUnit newLogicalHeight = adjustedLogicalLineTop - absoluteLogicalTop;
1376
1377 if (layoutState.flowThread()) {
1378 layoutState.setAdjustedLogicalLineTop(adjustedLogicalLineTop);
1379 newLogicalHeight = logicalHeight();
1380 }
1381
1382 end = restartLayoutRunsAndFloatsInRange(logicalHeight(), newLogicalHeight, lastFloatFromPreviousLine, resolver, end);
1383 return true;
1384 }
1385
layoutRunsAndFloatsInRange(LineLayoutState & layoutState,InlineBidiResolver & resolver,const InlineIterator & cleanLineStart,const BidiStatus & cleanLineBidiStatus,unsigned consecutiveHyphenatedLines)1386 void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
1387 {
1388 RenderStyle* styleToUse = style();
1389 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1390 LineMidpointState& lineMidpointState = resolver.midpointState();
1391 InlineIterator endOfLine = resolver.position();
1392 bool checkForEndLineMatch = layoutState.endLine();
1393 RenderTextInfo renderTextInfo;
1394 VerticalPositionCache verticalPositionCache;
1395
1396 LineBreaker lineBreaker(this);
1397
1398 LayoutSize logicalOffsetFromShapeContainer;
1399 ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo();
1400 if (shapeInsideInfo) {
1401 ASSERT(shapeInsideInfo->owner() == this || allowsShapeInsideInfoSharing(shapeInsideInfo->owner()));
1402 if (shapeInsideInfo != this->shapeInsideInfo()) {
1403 // FIXME Bug 100284: If subsequent LayoutStates are pushed, we will have to add
1404 // their offsets from the original shape-inside container.
1405 logicalOffsetFromShapeContainer = logicalOffsetFromShapeAncestorContainer(shapeInsideInfo->owner());
1406 }
1407 // Begin layout at the logical top of our shape inside.
1408 if (logicalHeight() + logicalOffsetFromShapeContainer.height() < shapeInsideInfo->shapeLogicalTop()) {
1409 LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - logicalOffsetFromShapeContainer.height();
1410 if (layoutState.flowThread())
1411 logicalHeight -= shapeInsideInfo->owner()->borderAndPaddingBefore();
1412 setLogicalHeight(logicalHeight);
1413 }
1414 }
1415
1416 while (!endOfLine.atEnd()) {
1417 // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
1418 if (checkForEndLineMatch) {
1419 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
1420 if (layoutState.endLineMatched()) {
1421 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
1422 break;
1423 }
1424 }
1425
1426 lineMidpointState.reset();
1427
1428 layoutState.lineInfo().setEmpty(true);
1429 layoutState.lineInfo().resetRunsFromLeadingWhitespace();
1430
1431 const InlineIterator previousEndofLine = endOfLine;
1432 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
1433 FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0;
1434
1435 updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, logicalOffsetFromShapeContainer, layoutState);
1436
1437 WordMeasurements wordMeasurements;
1438 endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
1439 renderTextInfo.m_lineBreakIterator.resetPriorContext();
1440 if (resolver.position().atEnd()) {
1441 // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with!
1442 // Once BidiRunList is separated from BidiResolver this will not be needed.
1443 resolver.runs().deleteRuns();
1444 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
1445 layoutState.setCheckForFloatsFromLastLine(true);
1446 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
1447 break;
1448 }
1449
1450 if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, logicalOffsetFromShapeContainer.height(), layoutState, resolver, lastFloatFromPreviousLine, endOfLine, wordMeasurements))
1451 continue;
1452
1453 ASSERT(endOfLine != resolver.position());
1454
1455 // This is a short-cut for empty lines.
1456 if (layoutState.lineInfo().isEmpty()) {
1457 if (lastRootBox())
1458 lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.m_pos, resolver.status());
1459 } else {
1460 VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
1461
1462 if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) {
1463 TextDirection direction = determinePlaintextDirectionality(resolver.position().root(), resolver.position().object(), resolver.position().offset());
1464 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi())));
1465 }
1466 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
1467 BidiRunList<BidiRun>& bidiRuns = resolver.runs();
1468 constructBidiRunsForLine(this, resolver, bidiRuns, endOfLine, override, layoutState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph);
1469 ASSERT(resolver.position() == endOfLine);
1470
1471 BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0;
1472
1473 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) {
1474 bidiRuns.logicallyLastRun()->m_hasHyphen = true;
1475 consecutiveHyphenatedLines++;
1476 } else
1477 consecutiveHyphenatedLines = 0;
1478
1479 // Now that the runs have been ordered, we create the line boxes.
1480 // At the same time we figure out where border/padding/margin should be applied for
1481 // inline flow boxes.
1482
1483 LayoutUnit oldLogicalHeight = logicalHeight();
1484 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
1485
1486 bidiRuns.deleteRuns();
1487 resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
1488
1489 if (lineBox) {
1490 lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.m_pos, resolver.status());
1491 if (layoutState.usesRepaintBounds())
1492 layoutState.updateRepaintRangeFromBox(lineBox);
1493
1494 if (paginated) {
1495 LayoutUnit adjustment = 0;
1496 adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread());
1497 if (adjustment) {
1498 LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine());
1499 lineBox->adjustBlockDirectionPosition(adjustment);
1500 if (layoutState.usesRepaintBounds())
1501 layoutState.updateRepaintRangeFromBox(lineBox);
1502
1503 if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) {
1504 // We have to delete this line, remove all floats that got added, and let line layout re-run.
1505 lineBox->deleteLine();
1506 endOfLine = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, previousEndofLine);
1507 continue;
1508 }
1509
1510 setLogicalHeight(lineBox->lineBottomWithLeading());
1511 }
1512
1513 if (layoutState.flowThread())
1514 updateRegionForLine(lineBox);
1515 }
1516 }
1517 }
1518
1519 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
1520 setStaticPositions(this, lineBreaker.positionedObjects()[i]);
1521
1522 if (!layoutState.lineInfo().isEmpty()) {
1523 layoutState.lineInfo().setFirstLine(false);
1524 clearFloats(lineBreaker.clear());
1525 }
1526
1527 if (m_floatingObjects && lastRootBox()) {
1528 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1529 FloatingObjectSetIterator it = floatingObjectSet.begin();
1530 FloatingObjectSetIterator end = floatingObjectSet.end();
1531 if (layoutState.lastFloat()) {
1532 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
1533 ASSERT(lastFloatIterator != end);
1534 ++lastFloatIterator;
1535 it = lastFloatIterator;
1536 }
1537 for (; it != end; ++it) {
1538 FloatingObject* f = *it;
1539 appendFloatingObjectToLastLine(f);
1540 ASSERT(f->renderer() == layoutState.floats()[layoutState.floatIndex()].object);
1541 // If a float's geometry has changed, give up on syncing with clean lines.
1542 if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
1543 checkForEndLineMatch = false;
1544 layoutState.setFloatIndex(layoutState.floatIndex() + 1);
1545 }
1546 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
1547 }
1548
1549 lineMidpointState.reset();
1550 resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine));
1551 }
1552
1553 // In case we already adjusted the line positions during this layout to avoid widows
1554 // then we need to ignore the possibility of having a new widows situation.
1555 // Otherwise, we risk leaving empty containers which is against the block fragmentation principles.
1556 if (paginated && !style()->hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
1557 // Check the line boxes to make sure we didn't create unacceptable widows.
1558 // However, we'll prioritize orphans - so nothing we do here should create
1559 // a new orphan.
1560
1561 RootInlineBox* lineBox = lastRootBox();
1562
1563 // Count from the end of the block backwards, to see how many hanging
1564 // lines we have.
1565 RootInlineBox* firstLineInBlock = firstRootBox();
1566 int numLinesHanging = 1;
1567 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1568 ++numLinesHanging;
1569 lineBox = lineBox->prevRootBox();
1570 }
1571
1572 // If there were no breaks in the block, we didn't create any widows.
1573 if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
1574 return;
1575
1576 if (numLinesHanging < style()->widows()) {
1577 // We have detected a widow. Now we need to work out how many
1578 // lines there are on the previous page, and how many we need
1579 // to steal.
1580 int numLinesNeeded = style()->widows() - numLinesHanging;
1581 RootInlineBox* currentFirstLineOfNewPage = lineBox;
1582
1583 // Count the number of lines in the previous page.
1584 lineBox = lineBox->prevRootBox();
1585 int numLinesInPreviousPage = 1;
1586 while (lineBox && lineBox != firstLineInBlock && !lineBox->isFirstAfterPageBreak()) {
1587 ++numLinesInPreviousPage;
1588 lineBox = lineBox->prevRootBox();
1589 }
1590
1591 // If there was an explicit value for orphans, respect that. If not, we still
1592 // shouldn't create a situation where we make an orphan bigger than the initial value.
1593 // This means that setting widows implies we also care about orphans, but given
1594 // the specification says the initial orphan value is non-zero, this is ok. The
1595 // author is always free to set orphans explicitly as well.
1596 int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans();
1597 int numLinesAvailable = numLinesInPreviousPage - orphans;
1598 if (numLinesAvailable <= 0)
1599 return;
1600
1601 int numLinesToTake = min(numLinesAvailable, numLinesNeeded);
1602 // Wind back from our first widowed line.
1603 lineBox = currentFirstLineOfNewPage;
1604 for (int i = 0; i < numLinesToTake; ++i)
1605 lineBox = lineBox->prevRootBox();
1606
1607 // We now want to break at this line. Remember for next layout and trigger relayout.
1608 setBreakAtLineToAvoidWidow(lineCount(lineBox));
1609 markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
1610 }
1611 }
1612
1613 clearDidBreakAtLineToAvoidWidow();
1614 }
1615
linkToEndLineIfNeeded(LineLayoutState & layoutState)1616 void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
1617 {
1618 if (layoutState.endLine()) {
1619 if (layoutState.endLineMatched()) {
1620 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1621 // Attach all the remaining lines, and then adjust their y-positions as needed.
1622 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop();
1623 for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
1624 line->attachLine();
1625 if (paginated) {
1626 delta -= line->paginationStrut();
1627 adjustLinePositionForPagination(line, delta, layoutState.flowThread());
1628 }
1629 if (delta) {
1630 layoutState.updateRepaintRangeFromBox(line, delta);
1631 line->adjustBlockDirectionPosition(delta);
1632 }
1633 if (layoutState.flowThread())
1634 updateRegionForLine(line);
1635 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1636 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1637 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1638 FloatingObject* floatingObject = insertFloatingObject(*f);
1639 ASSERT(!floatingObject->originatingLine());
1640 floatingObject->setOriginatingLine(line);
1641 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
1642 positionNewFloats();
1643 }
1644 }
1645 }
1646 setLogicalHeight(lastRootBox()->lineBottomWithLeading());
1647 } else {
1648 // Delete all the remaining lines.
1649 deleteLineRange(layoutState, layoutState.endLine());
1650 }
1651 }
1652
1653 if (m_floatingObjects && (layoutState.checkForFloatsFromLastLine() || positionNewFloats()) && lastRootBox()) {
1654 // In case we have a float on the last line, it might not be positioned up to now.
1655 // This has to be done before adding in the bottom border/padding, or the float will
1656 // include the padding incorrectly. -dwh
1657 if (layoutState.checkForFloatsFromLastLine()) {
1658 LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
1659 LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
1660 TrailingFloatsRootInlineBox* trailingFloatsLineBox = new TrailingFloatsRootInlineBox(this);
1661 m_lineBoxes.appendLineBox(trailingFloatsLineBox);
1662 trailingFloatsLineBox->setConstructed();
1663 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
1664 VerticalPositionCache verticalPositionCache;
1665 LayoutUnit blockLogicalHeight = logicalHeight();
1666 trailingFloatsLineBox->alignBoxesInBlockDirection(blockLogicalHeight, textBoxDataMap, verticalPositionCache);
1667 trailingFloatsLineBox->setLineTopBottomPositions(blockLogicalHeight, blockLogicalHeight, blockLogicalHeight, blockLogicalHeight);
1668 trailingFloatsLineBox->setPaginatedLineWidth(availableLogicalWidthForContent(blockLogicalHeight));
1669 LayoutRect logicalLayoutOverflow(0, blockLogicalHeight, 1, bottomLayoutOverflow - blockLogicalHeight);
1670 LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
1671 trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
1672 if (layoutState.flowThread())
1673 updateRegionForLine(trailingFloatsLineBox);
1674 }
1675
1676 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
1677 FloatingObjectSetIterator it = floatingObjectSet.begin();
1678 FloatingObjectSetIterator end = floatingObjectSet.end();
1679 if (layoutState.lastFloat()) {
1680 FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
1681 ASSERT(lastFloatIterator != end);
1682 ++lastFloatIterator;
1683 it = lastFloatIterator;
1684 }
1685 for (; it != end; ++it)
1686 appendFloatingObjectToLastLine(*it);
1687 layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
1688 }
1689 }
1690
repaintDirtyFloats(Vector<FloatWithRect> & floats)1691 void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats)
1692 {
1693 size_t floatCount = floats.size();
1694 // Floats that did not have layout did not repaint when we laid them out. They would have
1695 // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
1696 // painted.
1697 for (size_t i = 0; i < floatCount; ++i) {
1698 if (!floats[i].everHadLayout) {
1699 RenderBox* f = floats[i].object;
1700 if (!f->x() && !f->y() && f->checkForRepaintDuringLayout()) {
1701 if (RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
1702 f->setShouldDoFullRepaintAfterLayout(true);
1703 else
1704 f->repaint();
1705 }
1706 }
1707 }
1708 }
1709
layoutInlineChildren(bool relayoutChildren,LayoutUnit & repaintLogicalTop,LayoutUnit & repaintLogicalBottom)1710 void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
1711 {
1712 FastTextAutosizer* textAutosizer = document().fastTextAutosizer();
1713 if (textAutosizer)
1714 textAutosizer->inflate(this);
1715
1716 setLogicalHeight(borderBefore() + paddingBefore());
1717
1718 // Lay out our hypothetical grid line as though it occurs at the top of the block.
1719 if (view()->layoutState() && view()->layoutState()->lineGrid() == this)
1720 layoutLineGridBox();
1721
1722 RenderFlowThread* flowThread = flowThreadContainingBlock();
1723 bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions();
1724
1725 // Figure out if we should clear out our line boxes.
1726 // FIXME: Handle resize eventually!
1727 bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
1728 LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
1729
1730 if (isFullLayout)
1731 lineBoxes()->deleteLineBoxes();
1732
1733 // Text truncation kicks in in two cases:
1734 // 1) If your overflow isn't visible and your text-overflow-mode isn't clip.
1735 // 2) If you're an anonymous block with a block parent that satisfies #1 that was created
1736 // to accomodate a block that has inline and block children. This excludes parents where
1737 // canCollapseAnonymousBlockChild is false, notabley flex items and grid items.
1738 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1739 // difficult to figure out in general (especially in the middle of doing layout), so we only handle the
1740 // simple case of an anonymous block truncating when it's parent is clipped.
1741 bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
1742 || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && toRenderBlock(parent())->canCollapseAnonymousBlockChild()
1743 && parent()->style()->textOverflow() && parent()->hasOverflowClip());
1744
1745 // Walk all the lines and delete our ellipsis line boxes if they exist.
1746 if (hasTextOverflow)
1747 deleteEllipsisLineBoxes();
1748
1749 if (firstChild()) {
1750 // In full layout mode, clear the line boxes of children upfront. Otherwise,
1751 // siblings can run into stale root lineboxes during layout. Then layout
1752 // the replaced elements later. In partial layout mode, line boxes are not
1753 // deleted and only dirtied. In that case, we can layout the replaced
1754 // elements at the same time.
1755 bool hasInlineChild = false;
1756 Vector<RenderBox*> replacedChildren;
1757 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
1758 RenderObject* o = walker.current();
1759
1760 LayoutRectRecorder recorder(*o, !o->isText());
1761
1762 if (!hasInlineChild && o->isInline())
1763 hasInlineChild = true;
1764
1765 if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) {
1766 RenderBox* box = toRenderBox(o);
1767
1768 if (relayoutChildren || box->hasRelativeDimensions())
1769 o->setChildNeedsLayout(MarkOnlyThis);
1770
1771 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
1772 if (relayoutChildren && box->needsPreferredWidthsRecalculation())
1773 o->setPreferredLogicalWidthsDirty(MarkOnlyThis);
1774
1775 if (o->isOutOfFlowPositioned())
1776 o->containingBlock()->insertPositionedObject(box);
1777 else if (o->isFloating())
1778 layoutState.floats().append(FloatWithRect(box));
1779 else if (isFullLayout || o->needsLayout()) {
1780 // Replaced element.
1781 box->dirtyLineBoxes(isFullLayout);
1782 if (isFullLayout)
1783 replacedChildren.append(box);
1784 else
1785 o->layoutIfNeeded();
1786 }
1787 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
1788 if (!o->isText())
1789 toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
1790 if (layoutState.isFullLayout() || o->selfNeedsLayout())
1791 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
1792 o->clearNeedsLayout();
1793 }
1794 }
1795
1796 for (size_t i = 0; i < replacedChildren.size(); i++)
1797 replacedChildren[i]->layoutIfNeeded();
1798
1799 layoutRunsAndFloats(layoutState, hasInlineChild);
1800 }
1801
1802 // Expand the last line to accommodate Ruby and emphasis marks.
1803 int lastLineAnnotationsAdjustment = 0;
1804 if (lastRootBox()) {
1805 LayoutUnit lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
1806 if (!style()->isFlippedLinesWritingMode())
1807 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
1808 else
1809 lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
1810 }
1811
1812 // Now add in the bottom border/padding.
1813 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAfter() + paddingAfter() + scrollbarLogicalHeight());
1814
1815 if (!firstLineBox() && hasLineIfEmpty())
1816 setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
1817
1818 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1819 // truncate text.
1820 if (hasTextOverflow)
1821 checkLinesForTextOverflow();
1822 }
1823
checkFloatsInCleanLine(RootInlineBox * line,Vector<FloatWithRect> & floats,size_t & floatIndex,bool & encounteredNewFloat,bool & dirtiedByFloat)1824 void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
1825 {
1826 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
1827 if (!cleanLineFloats)
1828 return;
1829
1830 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1831 for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
1832 RenderBox* floatingBox = *it;
1833 floatingBox->layoutIfNeeded();
1834 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight());
1835 if (floats[floatIndex].object != floatingBox) {
1836 encounteredNewFloat = true;
1837 return;
1838 }
1839
1840 if (floats[floatIndex].rect.size() != newSize) {
1841 LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
1842 LayoutUnit floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height())
1843 : max(floats[floatIndex].rect.width(), newSize.width());
1844 floatHeight = min(floatHeight, LayoutUnit::max() - floatTop);
1845 line->markDirty();
1846 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
1847 floats[floatIndex].rect.setSize(newSize);
1848 dirtiedByFloat = true;
1849 }
1850 floatIndex++;
1851 }
1852 }
1853
determineStartPosition(LineLayoutState & layoutState,InlineBidiResolver & resolver)1854 RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
1855 {
1856 RootInlineBox* curr = 0;
1857 RootInlineBox* last = 0;
1858
1859 // FIXME: This entire float-checking block needs to be broken into a new function.
1860 bool dirtiedByFloat = false;
1861 if (!layoutState.isFullLayout()) {
1862 // Paginate all of the clean lines.
1863 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
1864 LayoutUnit paginationDelta = 0;
1865 size_t floatIndex = 0;
1866 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1867 if (paginated) {
1868 if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) {
1869 curr->markDirty();
1870 break;
1871 }
1872 paginationDelta -= curr->paginationStrut();
1873 adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
1874 if (paginationDelta) {
1875 if (containsFloats() || !layoutState.floats().isEmpty()) {
1876 // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout.
1877 layoutState.markForFullLayout();
1878 break;
1879 }
1880
1881 layoutState.updateRepaintRangeFromBox(curr, paginationDelta);
1882 curr->adjustBlockDirectionPosition(paginationDelta);
1883 }
1884 if (layoutState.flowThread())
1885 updateRegionForLine(curr);
1886 }
1887
1888 // If a new float has been inserted before this line or before its last known float, just do a full layout.
1889 bool encounteredNewFloat = false;
1890 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1891 if (encounteredNewFloat)
1892 layoutState.markForFullLayout();
1893
1894 if (dirtiedByFloat || layoutState.isFullLayout())
1895 break;
1896 }
1897 // Check if a new float has been inserted after the last known float.
1898 if (!curr && floatIndex < layoutState.floats().size())
1899 layoutState.markForFullLayout();
1900 }
1901
1902 if (layoutState.isFullLayout()) {
1903 // FIXME: This should just call deleteLineBoxTree, but that causes
1904 // crashes for fast/repaint tests.
1905 curr = firstRootBox();
1906 while (curr) {
1907 // Note: This uses nextRootBox() insted of nextLineBox() like deleteLineBoxTree does.
1908 RootInlineBox* next = curr->nextRootBox();
1909 curr->deleteLine();
1910 curr = next;
1911 }
1912 ASSERT(!firstLineBox() && !lastLineBox());
1913 } else {
1914 if (curr) {
1915 // We have a dirty line.
1916 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1917 // We have a previous line.
1918 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
1919 // The previous line didn't break cleanly or broke at a newline
1920 // that has been deleted, so treat it as dirty too.
1921 curr = prevRootBox;
1922 }
1923 } else {
1924 // No dirty lines were found.
1925 // If the last line didn't break cleanly, treat it as dirty.
1926 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1927 curr = lastRootBox();
1928 }
1929
1930 // If we have no dirty lines, then last is just the last root box.
1931 last = curr ? curr->prevRootBox() : lastRootBox();
1932 }
1933
1934 unsigned numCleanFloats = 0;
1935 if (!layoutState.floats().isEmpty()) {
1936 LayoutUnit savedLogicalHeight = logicalHeight();
1937 // Restore floats from clean lines.
1938 RootInlineBox* line = firstRootBox();
1939 while (line != curr) {
1940 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1941 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1942 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1943 FloatingObject* floatingObject = insertFloatingObject(*f);
1944 ASSERT(!floatingObject->originatingLine());
1945 floatingObject->setOriginatingLine(line);
1946 setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f));
1947 positionNewFloats();
1948 ASSERT(layoutState.floats()[numCleanFloats].object == *f);
1949 numCleanFloats++;
1950 }
1951 }
1952 line = line->nextRootBox();
1953 }
1954 setLogicalHeight(savedLogicalHeight);
1955 }
1956 layoutState.setFloatIndex(numCleanFloats);
1957
1958 layoutState.lineInfo().setFirstLine(!last);
1959 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
1960
1961 if (last) {
1962 setLogicalHeight(last->lineBottomWithLeading());
1963 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
1964 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1965 resolver.setStatus(last->lineBreakBidiStatus());
1966 } else {
1967 TextDirection direction = style()->direction();
1968 if (style()->unicodeBidi() == Plaintext)
1969 direction = determinePlaintextDirectionality(this);
1970 resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi())));
1971 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, &resolver), 0);
1972 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1973 }
1974 return curr;
1975 }
1976
determineEndPosition(LineLayoutState & layoutState,RootInlineBox * startLine,InlineIterator & cleanLineStart,BidiStatus & cleanLineBidiStatus)1977 void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
1978 {
1979 ASSERT(!layoutState.endLine());
1980 size_t floatIndex = layoutState.floatIndex();
1981 RootInlineBox* last = 0;
1982 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1983 if (!curr->isDirty()) {
1984 bool encounteredNewFloat = false;
1985 bool dirtiedByFloat = false;
1986 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
1987 if (encounteredNewFloat)
1988 return;
1989 }
1990 if (curr->isDirty())
1991 last = 0;
1992 else if (!last)
1993 last = curr;
1994 }
1995
1996 if (!last)
1997 return;
1998
1999 // At this point, |last| is the first line in a run of clean lines that ends with the last line
2000 // in the block.
2001
2002 RootInlineBox* prev = last->prevRootBox();
2003 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
2004 cleanLineBidiStatus = prev->lineBreakBidiStatus();
2005 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
2006
2007 for (RootInlineBox* line = last; line; line = line->nextRootBox())
2008 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
2009 // their connections to one another.
2010
2011 layoutState.setEndLine(last);
2012 }
2013
checkPaginationAndFloatsAtEndLine(LineLayoutState & layoutState)2014 bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
2015 {
2016 LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
2017
2018 bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
2019 if (paginated && layoutState.flowThread()) {
2020 // Check all lines from here to the end, and see if the hypothetical new position for the lines will result
2021 // in a different available line width.
2022 for (RootInlineBox* lineBox = layoutState.endLine(); lineBox; lineBox = lineBox->nextRootBox()) {
2023 if (paginated) {
2024 // This isn't the real move we're going to do, so don't update the line box's pagination
2025 // strut yet.
2026 LayoutUnit oldPaginationStrut = lineBox->paginationStrut();
2027 lineDelta -= oldPaginationStrut;
2028 adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread());
2029 lineBox->setPaginationStrut(oldPaginationStrut);
2030 }
2031 if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread()))
2032 return false;
2033 }
2034 }
2035
2036 if (!lineDelta || !m_floatingObjects)
2037 return true;
2038
2039 // See if any floats end in the range along which we want to shift the lines vertically.
2040 LayoutUnit logicalTop = min(logicalHeight(), layoutState.endLineLogicalTop());
2041
2042 RootInlineBox* lastLine = layoutState.endLine();
2043 while (RootInlineBox* nextLine = lastLine->nextRootBox())
2044 lastLine = nextLine;
2045
2046 LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
2047
2048 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2049 FloatingObjectSetIterator end = floatingObjectSet.end();
2050 for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
2051 FloatingObject* floatingObject = *it;
2052 if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
2053 return false;
2054 }
2055
2056 return true;
2057 }
2058
matchedEndLine(LineLayoutState & layoutState,const InlineBidiResolver & resolver,const InlineIterator & endLineStart,const BidiStatus & endLineStatus)2059 bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
2060 {
2061 if (resolver.position() == endLineStart) {
2062 if (resolver.status() != endLineStatus)
2063 return false;
2064 return checkPaginationAndFloatsAtEndLine(layoutState);
2065 }
2066
2067 // The first clean line doesn't match, but we can check a handful of following lines to try
2068 // to match back up.
2069 static int numLines = 8; // The # of lines we're willing to match against.
2070 RootInlineBox* originalEndLine = layoutState.endLine();
2071 RootInlineBox* line = originalEndLine;
2072 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
2073 if (line->lineBreakObj() == resolver.position().object() && line->lineBreakPos() == resolver.position().m_pos) {
2074 // We have a match.
2075 if (line->lineBreakBidiStatus() != resolver.status())
2076 return false; // ...but the bidi state doesn't match.
2077
2078 bool matched = false;
2079 RootInlineBox* result = line->nextRootBox();
2080 layoutState.setEndLine(result);
2081 if (result) {
2082 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
2083 matched = checkPaginationAndFloatsAtEndLine(layoutState);
2084 }
2085
2086 // Now delete the lines that we failed to sync.
2087 deleteLineRange(layoutState, originalEndLine, result);
2088 return matched;
2089 }
2090 }
2091
2092 return false;
2093 }
2094
generatesLineBoxesForInlineChild(RenderObject * inlineObj)2095 bool RenderBlockFlow::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
2096
2097 {
2098 ASSERT(inlineObj->parent() == this);
2099
2100 InlineIterator it(this, inlineObj, 0);
2101 // FIXME: We should pass correct value for WhitespacePosition.
2102 while (!it.atEnd() && !requiresLineBox(it))
2103 it.increment();
2104
2105 return !it.atEnd();
2106 }
2107
skipLeadingWhitespace(InlineBidiResolver & resolver,LineInfo & lineInfo,FloatingObject * lastFloatFromPreviousLine,LineWidth & width)2108 void LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolver, LineInfo& lineInfo,
2109 FloatingObject* lastFloatFromPreviousLine, LineWidth& width)
2110 {
2111 while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) {
2112 RenderObject* object = resolver.position().object();
2113 if (object->isOutOfFlowPositioned()) {
2114 setStaticPositions(m_block, toRenderBox(object));
2115 if (object->style()->isOriginalDisplayInlineType()) {
2116 resolver.runs().addRun(createRun(0, 1, object, resolver));
2117 lineInfo.incrementRunsFromLeadingWhitespace();
2118 }
2119 } else if (object->isFloating())
2120 m_block->positionNewFloatOnLine(m_block->insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, lineInfo, width);
2121 else if (object->isText() && object->style()->hasTextCombine() && object->isCombineText() && !toRenderCombineText(object)->isCombined()) {
2122 toRenderCombineText(object)->combineText();
2123 if (toRenderCombineText(object)->isCombined())
2124 continue;
2125 }
2126 resolver.position().increment(&resolver);
2127 }
2128 resolver.commitExplicitEmbedding();
2129 }
2130
reset()2131 void LineBreaker::reset()
2132 {
2133 m_positionedObjects.clear();
2134 m_hyphenated = false;
2135 m_clear = CNONE;
2136 }
2137
nextLineBreak(InlineBidiResolver & resolver,LineInfo & lineInfo,RenderTextInfo & renderTextInfo,FloatingObject * lastFloatFromPreviousLine,unsigned consecutiveHyphenatedLines,WordMeasurements & wordMeasurements)2138 InlineIterator LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
2139 {
2140 ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo();
2141
2142 if (!shapeInsideInfo || !shapeInsideInfo->lineOverlapsShapeBounds())
2143 return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
2144
2145 InlineIterator end = resolver.position();
2146 InlineIterator oldEnd = end;
2147
2148 if (!shapeInsideInfo->hasSegments()) {
2149 end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
2150 resolver.setPositionIgnoringNestedIsolates(oldEnd);
2151 return oldEnd;
2152 }
2153
2154 const SegmentList& segments = shapeInsideInfo->segments();
2155 SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges();
2156
2157 for (unsigned i = 0; i < segments.size() && !end.atEnd(); i++) {
2158 const InlineIterator segmentStart = resolver.position();
2159 end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
2160
2161 ASSERT(segmentRanges.size() == i);
2162 if (resolver.position().atEnd()) {
2163 segmentRanges.append(LineSegmentRange(segmentStart, end));
2164 break;
2165 }
2166 if (resolver.position() == end) {
2167 // Nothing fit this segment
2168 end = segmentStart;
2169 segmentRanges.append(LineSegmentRange(segmentStart, segmentStart));
2170 resolver.setPositionIgnoringNestedIsolates(segmentStart);
2171 } else {
2172 // Note that resolver.position is already skipping some of the white space at the beginning of the line,
2173 // so that's why segmentStart might be different than resolver.position().
2174 LineSegmentRange range(resolver.position(), end);
2175 segmentRanges.append(range);
2176 resolver.setPosition(end, numberOfIsolateAncestors(end));
2177
2178 if (lineInfo.previousLineBrokeCleanly()) {
2179 // If we hit a new line break, just stop adding anything to this line.
2180 break;
2181 }
2182 }
2183 }
2184 resolver.setPositionIgnoringNestedIsolates(oldEnd);
2185 return end;
2186 }
2187
nextSegmentBreak(InlineBidiResolver & resolver,LineInfo & lineInfo,RenderTextInfo & renderTextInfo,FloatingObject * lastFloatFromPreviousLine,unsigned consecutiveHyphenatedLines,WordMeasurements & wordMeasurements)2188 InlineIterator LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
2189 {
2190 reset();
2191
2192 ASSERT(resolver.position().root() == m_block);
2193
2194 bool appliedStartWidth = resolver.position().m_pos > 0;
2195
2196 LineWidth width(*m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style()));
2197
2198 skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
2199
2200 if (resolver.position().atEnd())
2201 return resolver.position();
2202
2203 BreakingContext context(resolver, lineInfo, width, renderTextInfo, lastFloatFromPreviousLine, appliedStartWidth, m_block);
2204
2205 while (context.currentObject()) {
2206 context.initializeForCurrentObject();
2207 if (context.currentObject()->isBR()) {
2208 context.handleBR(m_clear);
2209 } else if (context.currentObject()->isOutOfFlowPositioned()) {
2210 context.handleOutOfFlowPositioned(m_positionedObjects);
2211 } else if (context.currentObject()->isFloating()) {
2212 context.handleFloat();
2213 } else if (context.currentObject()->isRenderInline()) {
2214 context.handleEmptyInline();
2215 } else if (context.currentObject()->isReplaced()) {
2216 context.handleReplaced();
2217 } else if (context.currentObject()->isText()) {
2218 if (context.handleText(wordMeasurements, m_hyphenated)) {
2219 // We've hit a hard text line break. Our line break iterator is updated, so go ahead and early return.
2220 return context.lineBreak();
2221 }
2222 } else {
2223 ASSERT_NOT_REACHED();
2224 }
2225
2226 if (context.atEnd())
2227 return context.handleEndOfLine();
2228
2229 context.commitAndUpdateLineBreakIfNeeded();
2230
2231 if (context.atEnd())
2232 return context.handleEndOfLine();
2233
2234 context.increment();
2235 }
2236
2237 context.clearLineBreakIfFitsOnLine();
2238
2239 return context.handleEndOfLine();
2240 }
2241
addOverflowFromInlineChildren()2242 void RenderBlockFlow::addOverflowFromInlineChildren()
2243 {
2244 LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
2245 // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
2246 if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection())
2247 endPadding = 1;
2248 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2249 addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
2250 LayoutRect visualOverflow = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
2251 addContentsVisualOverflow(visualOverflow);
2252 }
2253 }
2254
deleteEllipsisLineBoxes()2255 void RenderBlockFlow::deleteEllipsisLineBoxes()
2256 {
2257 ETextAlign textAlign = style()->textAlign();
2258 bool ltr = style()->isLeftToRightDirection();
2259 bool firstLine = true;
2260 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2261 if (curr->hasEllipsisBox()) {
2262 curr->clearTruncation();
2263
2264 // Shift the line back where it belongs if we cannot accomodate an ellipsis.
2265 float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
2266 float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
2267 float totalLogicalWidth = curr->logicalWidth();
2268 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2269
2270 if (ltr)
2271 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
2272 else
2273 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
2274 }
2275 firstLine = false;
2276 }
2277 }
2278
checkLinesForTextOverflow()2279 void RenderBlockFlow::checkLinesForTextOverflow()
2280 {
2281 // Determine the width of the ellipsis using the current font.
2282 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2283 const Font& font = style()->font();
2284 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2285 const Font& firstLineFont = firstLineStyle()->font();
2286 int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle()));
2287 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style()));
2288
2289 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2290 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2291 // check the left edge of the line box to see if it is less
2292 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2293 bool ltr = style()->isLeftToRightDirection();
2294 ETextAlign textAlign = style()->textAlign();
2295 bool firstLine = true;
2296 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2297 // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed.
2298 // https://bugs.webkit.org/show_bug.cgi?id=105461
2299 float currLogicalLeft = curr->logicalLeft();
2300 int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), currLogicalLeft);
2301 int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
2302 int lineBoxEdge = ltr ? snapSizeToPixel(currLogicalLeft + curr->logicalWidth(), currLogicalLeft) : snapSizeToPixel(currLogicalLeft, 0);
2303 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
2304 // This line spills out of our box in the appropriate direction. Now we need to see if the line
2305 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
2306 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2307 // space.
2308
2309 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth;
2310 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
2311 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) {
2312 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width);
2313
2314 float logicalLeft = 0; // We are only intersted in the delta from the base position.
2315 float truncatedWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine);
2316 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0);
2317 if (ltr)
2318 curr->adjustLogicalPosition(logicalLeft, 0);
2319 else
2320 curr->adjustLogicalPosition(-(truncatedWidth - (logicalLeft + totalLogicalWidth)), 0);
2321 }
2322 }
2323 firstLine = false;
2324 }
2325 }
2326
positionNewFloatOnLine(FloatingObject * newFloat,FloatingObject * lastFloatFromPreviousLine,LineInfo & lineInfo,LineWidth & width)2327 bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
2328 {
2329 if (!positionNewFloats())
2330 return false;
2331
2332 width.shrinkAvailableWidthForNewFloatIfNeeded(newFloat);
2333
2334 // We only connect floats to lines for pagination purposes if the floats occur at the start of
2335 // the line and the previous line had a hard break (so this line is either the first in the block
2336 // or follows a <br>).
2337 if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
2338 return true;
2339
2340 const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
2341 ASSERT(floatingObjectSet.last() == newFloat);
2342
2343 LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
2344 int paginationStrut = newFloat->paginationStrut();
2345
2346 if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
2347 return true;
2348
2349 FloatingObjectSetIterator it = floatingObjectSet.end();
2350 --it; // Last float is newFloat, skip that one.
2351 FloatingObjectSetIterator begin = floatingObjectSet.begin();
2352 while (it != begin) {
2353 --it;
2354 FloatingObject* floatingObject = *it;
2355 if (floatingObject == lastFloatFromPreviousLine)
2356 break;
2357 if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
2358 floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut());
2359 RenderBox* floatBox = floatingObject->renderer();
2360 setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
2361 if (floatBox->isRenderBlock())
2362 floatBox->forceChildLayout();
2363 else
2364 floatBox->layoutIfNeeded();
2365 // Save the old logical top before calling removePlacedObject which will set
2366 // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat.
2367 LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
2368 m_floatingObjects->removePlacedObject(floatingObject);
2369 setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
2370 m_floatingObjects->addPlacedObject(floatingObject);
2371 }
2372 }
2373
2374 // Just update the line info's pagination strut without altering our logical height yet. If the line ends up containing
2375 // no content, then we don't want to improperly grow the height of the block.
2376 lineInfo.setFloatPaginationStrut(lineInfo.floatPaginationStrut() + paginationStrut);
2377 return true;
2378 }
2379
startAlignedOffsetForLine(LayoutUnit position,bool firstLine)2380 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
2381 {
2382 ETextAlign textAlign = style()->textAlign();
2383
2384 if (textAlign == TASTART) // FIXME: Handle TAEND here
2385 return startOffsetForLine(position, firstLine);
2386
2387 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
2388 float totalLogicalWidth = 0;
2389 float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false);
2390 float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
2391 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
2392
2393 if (!style()->isLeftToRightDirection())
2394 return logicalWidth() - logicalLeft;
2395 return logicalLeft;
2396 }
2397
layoutLineGridBox()2398 void RenderBlockFlow::layoutLineGridBox()
2399 {
2400 if (style()->lineGrid() == RenderStyle::initialLineGrid()) {
2401 setLineGridBox(0);
2402 return;
2403 }
2404
2405 setLineGridBox(0);
2406
2407 RootInlineBox* lineGridBox = new RootInlineBox(this);
2408 lineGridBox->setHasTextChildren(); // Needed to make the line ascent/descent actually be honored in quirks mode.
2409 lineGridBox->setConstructed();
2410 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
2411 VerticalPositionCache verticalPositionCache;
2412 lineGridBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache);
2413
2414 setLineGridBox(lineGridBox);
2415
2416 // FIXME: If any of the characteristics of the box change compared to the old one, then we need to do a deep dirtying
2417 // (similar to what happens when the page height changes). Ideally, though, we only do this if someone is actually snapping
2418 // to this grid.
2419 }
2420
2421 }
2422