Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(36)

Side by Side Diff: sky/engine/core/rendering/RenderBlockLineLayout.cpp

Issue 763043002: Remove RenderBlockLineLayout (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: review Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All r ight 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 "sky/engine/config.h"
24
25 #include "sky/engine/core/rendering/BidiRunForLine.h"
26 #include "sky/engine/core/rendering/RenderLayer.h"
27 #include "sky/engine/core/rendering/RenderObjectInlines.h"
28 #include "sky/engine/core/rendering/RenderView.h"
29 #include "sky/engine/core/rendering/TextRunConstructor.h"
30 #include "sky/engine/core/rendering/TrailingFloatsRootInlineBox.h"
31 #include "sky/engine/core/rendering/VerticalPositionCache.h"
32 #include "sky/engine/core/rendering/line/BreakingContextInlineHeaders.h"
33 #include "sky/engine/core/rendering/line/LineLayoutState.h"
34 #include "sky/engine/core/rendering/line/LineWidth.h"
35 #include "sky/engine/core/rendering/line/RenderTextInfo.h"
36 #include "sky/engine/core/rendering/line/WordMeasurement.h"
37 #include "sky/engine/platform/fonts/Character.h"
38 #include "sky/engine/platform/text/BidiResolver.h"
39 #include "sky/engine/wtf/RefCountedLeakCounter.h"
40 #include "sky/engine/wtf/StdLibExtras.h"
41 #include "sky/engine/wtf/Vector.h"
42 #include "sky/engine/wtf/unicode/CharacterNames.h"
43
44 namespace blink {
45
46 using namespace WTF::Unicode;
47
48 static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRo otLineBox, bool isOnlyRun = false)
49 {
50 if (isRootLineBox)
51 return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
52
53 if (obj->isText()) {
54 InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
55 // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode
56 // (Note the use of strict mode. In "almost strict" mode, we don't trea t the box for <br> as text.)
57 return textBox;
58 }
59
60 if (obj->isBox())
61 return toRenderBox(obj)->createInlineBox();
62
63 return toRenderInline(obj)->createAndAppendInlineFlowBox();
64 }
65
66 static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
67 {
68 if (o->isText()) {
69 RenderText* renderText = toRenderText(o);
70 renderText->dirtyLineBoxes(fullLayout);
71 } else
72 toRenderInline(o)->dirtyLineBoxes(fullLayout);
73 }
74
75 static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
76 {
77 do {
78 if (parentBox->isConstructed() || parentBox->nextOnLine())
79 return true;
80 parentBox = parentBox->parent();
81 } while (parentBox);
82 return false;
83 }
84
85 InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInf o& lineInfo, InlineBox* childBox)
86 {
87 // See if we have an unconstructed line box for this object that is also
88 // the last item on the line.
89 unsigned lineDepth = 1;
90 InlineFlowBox* parentBox = 0;
91 InlineFlowBox* result = 0;
92 bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::in itialLineBoxContain();
93 do {
94 ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
95
96 RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
97
98 // Get the last box we made for this render object.
99 parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlock(obj)- >lastLineBox();
100
101 // If this box or its ancestor is constructed then it is from a previous line, and we need
102 // to make a new box for our line. If this box or its ancestor is uncon structed but it has
103 // something following it on the line, then we know we have to make a ne w box
104 // as well. In this situation our inline has actually been split in two on
105 // the same line (this can happen with very fancy language mixtures).
106 bool constructedNewBox = false;
107 bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes();
108 bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNe xt(parentBox);
109 if (allowedToConstructNewBox && !canUseExistingParentBox) {
110 // We need to make a new box for this render object. Once
111 // made, we need to place it at the end of the current line.
112 InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this);
113 ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
114 parentBox = toInlineFlowBox(newBox);
115 parentBox->setFirstLineStyleBit(lineInfo.isFirstLine());
116 if (!hasDefaultLineBoxContain)
117 parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
118 constructedNewBox = true;
119 }
120
121 if (constructedNewBox || canUseExistingParentBox) {
122 if (!result)
123 result = parentBox;
124
125 // If we have hit the block itself, then |box| represents the root
126 // inline box for the line, and it doesn't have to be appended to an y parent
127 // inline.
128 if (childBox)
129 parentBox->addToLine(childBox);
130
131 if (!constructedNewBox || obj == this)
132 break;
133
134 childBox = parentBox;
135 }
136
137 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
138 // intermediate inline flows.
139 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
140
141 } while (true);
142
143 return result;
144 }
145
146 template <typename CharacterType>
147 static inline bool endsWithASCIISpaces(const CharacterType* characters, unsigned pos, unsigned end)
148 {
149 while (isASCIISpace(characters[pos])) {
150 pos++;
151 if (pos >= end)
152 return true;
153 }
154 return false;
155 }
156
157 static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
158 {
159 BidiRun* run = bidiRuns.logicallyLastRun();
160 if (!run)
161 return true;
162 unsigned pos = run->stop();
163 RenderObject* r = run->m_object;
164 if (!r->isText())
165 return false;
166 RenderText* renderText = toRenderText(r);
167 unsigned length = renderText->textLength();
168 if (pos >= length)
169 return true;
170
171 if (renderText->is8Bit())
172 return endsWithASCIISpaces(renderText->characters8(), pos, length);
173 return endsWithASCIISpaces(renderText->characters16(), pos, length);
174 }
175
176 RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co nst LineInfo& lineInfo)
177 {
178 ASSERT(bidiRuns.firstRun());
179
180 bool rootHasSelectedChildren = false;
181 InlineFlowBox* parentBox = 0;
182 int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace();
183 for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) {
184 // Create a box for our object.
185 bool isOnlyRun = (runCount == 1);
186 if (runCount == 2)
187 isOnlyRun = false;
188
189 if (lineInfo.isEmpty())
190 continue;
191
192 InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRu n);
193 r->m_box = box;
194
195 ASSERT(box);
196 if (!box)
197 continue;
198
199 if (!rootHasSelectedChildren && box->renderer().selectionState() != Rend erObject::SelectionNone)
200 rootHasSelectedChildren = true;
201
202 // If we have no parent box yet, or if the run is not simply a sibling,
203 // then we need to construct inline boxes as necessary to properly enclo se the
204 // run's inline box. Segments can only be siblings at the root level, as
205 // they are positioned separately.
206 if (!parentBox || parentBox->renderer() != r->m_object->parent()) {
207 // Create new inline boxes all the way back to the appropriate inser tion point.
208 parentBox = createLineBoxes(r->m_object->parent(), lineInfo, box);
209 } else {
210 // Append the inline box to this line.
211 parentBox->addToLine(box);
212 }
213
214 box->setBidiLevel(r->level());
215
216 if (box->isInlineTextBox()) {
217 InlineTextBox* text = toInlineTextBox(box);
218 text->setStart(r->m_start);
219 text->setLen(r->m_stop - r->m_start);
220 text->setDirOverride(r->dirOverride());
221 if (r->m_hasHyphen)
222 text->setHasHyphen(true);
223 }
224 }
225
226 ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
227
228 // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
229 // from the bidi runs walk above has a selection state.
230 if (rootHasSelectedChildren)
231 lastLineBox()->root().setHasSelectedChildren(true);
232
233 // Set bits on our inline flow boxes that indicate which sides should
234 // paint borders/margins/padding. This knowledge will ultimately be used wh en
235 // we determine the horizontal positions and widths of all the inline boxes on
236 // the line.
237 bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->m_object && bi diRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRu ns) : true;
238 lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogical lyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
239
240 // Now mark the line boxes as being constructed.
241 lastLineBox()->setConstructed();
242
243 // Return the last line.
244 return lastRootBox();
245 }
246
247 ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
248 {
249 ETextAlign alignment = style()->textAlign();
250 if (endsWithSoftBreak)
251 return alignment;
252
253 if (!RuntimeEnabledFeatures::css3TextEnabled())
254 return (alignment == JUSTIFY) ? TASTART : alignment;
255
256 if (alignment != JUSTIFY)
257 return alignment;
258
259 TextAlignLast alignmentLast = style()->textAlignLast();
260 switch (alignmentLast) {
261 case TextAlignLastStart:
262 return TASTART;
263 case TextAlignLastEnd:
264 return TAEND;
265 case TextAlignLastLeft:
266 return LEFT;
267 case TextAlignLastRight:
268 return RIGHT;
269 case TextAlignLastCenter:
270 return CENTER;
271 case TextAlignLastJustify:
272 return JUSTIFY;
273 case TextAlignLastAuto:
274 if (style()->textJustify() == TextJustifyDistribute)
275 return JUSTIFY;
276 return TASTART;
277 }
278
279 return alignment;
280 }
281
282 static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, B idiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float av ailableLogicalWidth)
283 {
284 // The direction of the block should determine what happens with wide lines.
285 // In particular with RTL blocks, wide lines should still spill out to the l eft.
286 if (isLeftToRightDirection) {
287 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
288 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailing SpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
289 return;
290 }
291
292 if (trailingSpaceRun)
293 trailingSpaceRun->m_box->setLogicalWidth(0);
294 else if (totalLogicalWidth > availableLogicalWidth)
295 logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
296 }
297
298 static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float a vailableLogicalWidth)
299 {
300 // Wide lines spill out of the block based off direction.
301 // So even if text-align is right, if direction is LTR, wide lines should ov erflow out of the right
302 // side of the block.
303 if (isLeftToRightDirection) {
304 if (trailingSpaceRun) {
305 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
306 trailingSpaceRun->m_box->setLogicalWidth(0);
307 }
308 if (totalLogicalWidth < availableLogicalWidth)
309 logicalLeft += availableLogicalWidth - totalLogicalWidth;
310 return;
311 }
312
313 if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
314 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpac eRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
315 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
316 } else
317 logicalLeft += availableLogicalWidth - totalLogicalWidth;
318 }
319
320 static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
321 {
322 float trailingSpaceWidth = 0;
323 if (trailingSpaceRun) {
324 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
325 trailingSpaceWidth = std::min(trailingSpaceRun->m_box->logicalWidth(), ( availableLogicalWidth - totalLogicalWidth + 1) / 2);
326 trailingSpaceRun->m_box->setLogicalWidth(std::max<float>(0, trailingSpac eWidth));
327 }
328 if (isLeftToRightDirection)
329 logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidt h) / 2, 0);
330 else
331 logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLog icalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
332 }
333
334 static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru n, RenderText* renderer, float xPos, const LineInfo& lineInfo,
335 GlyphOverflowAndFallbackFontsMap& t extBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& w ordMeasurements)
336 {
337 HashSet<const SimpleFontData*> fallbackFonts;
338 GlyphOverflow glyphOverflow;
339
340 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
341 // Always compute glyph overflow if the block's line-box-contain value is "g lyphs".
342 if (lineBox->fitsToGlyphs()) {
343 // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization
344 // will keep us from computing glyph bounds in nearly all cases.
345 bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
346 int baselineShift = lineBox->verticalPositionForBox(run->m_box, vertical PositionCache);
347 int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
348 int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
349 int boxAscent = font.fontMetrics().ascent() - baselineShift;
350 int boxDescent = font.fontMetrics().descent() + baselineShift;
351 if (boxAscent > rootDescent || boxDescent > rootAscent)
352 glyphOverflow.computeBounds = true;
353 }
354
355 LayoutUnit hyphenWidth = 0;
356 if (toInlineTextBox(run->m_box)->hasHyphen()) {
357 const Font& font = renderer->style(lineInfo.isFirstLine())->font();
358 hyphenWidth = measureHyphenWidth(renderer, font, run->direction());
359 }
360 float measuredWidth = 0;
361
362 bool kerningIsEnabled = font.fontDescription().typesettingFeatures() & Kerni ng;
363
364 bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
365
366 // Since we don't cache glyph overflows, we need to re-measure the run if
367 // the style is linebox-contain: glyph.
368
369 if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
370 int lastEndOffset = run->m_start;
371 for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOf fset < run->m_stop; ++i) {
372 const WordMeasurement& wordMeasurement = wordMeasurements[i];
373 if (wordMeasurement.width <=0 || wordMeasurement.startOffset == word Measurement.endOffset)
374 continue;
375 if (wordMeasurement.renderer != renderer || wordMeasurement.startOff set != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
376 continue;
377
378 lastEndOffset = wordMeasurement.endOffset;
379 if (kerningIsEnabled && lastEndOffset == run->m_stop) {
380 int wordLength = lastEndOffset - wordMeasurement.startOffset;
381 measuredWidth += renderer->width(wordMeasurement.startOffset, wo rdLength, xPos, run->direction(), lineInfo.isFirstLine());
382 if (i > 0 && wordLength == 1 && renderer->characterAt(wordMeasur ement.startOffset) == ' ')
383 measuredWidth += renderer->style()->wordSpacing();
384 } else
385 measuredWidth += wordMeasurement.width;
386 if (!wordMeasurement.fallbackFonts.isEmpty()) {
387 HashSet<const SimpleFontData*>::const_iterator end = wordMeasure ment.fallbackFonts.end();
388 for (HashSet<const SimpleFontData*>::const_iterator it = wordMea surement.fallbackFonts.begin(); it != end; ++it)
389 fallbackFonts.add(*it);
390 }
391 }
392 if (measuredWidth && lastEndOffset != run->m_stop) {
393 // If we don't have enough cached data, we'll measure the run again.
394 measuredWidth = 0;
395 fallbackFonts.clear();
396 }
397 }
398
399 if (!measuredWidth)
400 measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start , xPos, run->direction(), lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow );
401
402 run->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
403 if (!fallbackFonts.isEmpty()) {
404 ASSERT(run->m_box->isText());
405 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toI nlineTextBox(run->m_box), std::make_pair(Vector<const SimpleFontData*>(), GlyphO verflow())).storedValue;
406 ASSERT(it->value.first.isEmpty());
407 copyToVector(fallbackFonts, it->value.first);
408 run->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
409 }
410 if (!glyphOverflow.isZero()) {
411 ASSERT(run->m_box->isText());
412 GlyphOverflowAndFallbackFontsMap::ValueType* it = textBoxDataMap.add(toI nlineTextBox(run->m_box), std::make_pair(Vector<const SimpleFontData*>(), GlyphO verflow())).storedValue;
413 it->value.second = glyphOverflow;
414 run->m_box->clearKnownToHaveNoOverflow();
415 }
416 }
417
418 static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansi onOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
419 {
420 if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth )
421 return;
422
423 size_t i = 0;
424 for (BidiRun* r = firstRun; r; r = r->next()) {
425 if (!r->m_box || r == trailingSpaceRun)
426 continue;
427
428 if (r->m_object->isText()) {
429 unsigned opportunitiesInRun = expansionOpportunities[i++];
430
431 ASSERT(opportunitiesInRun <= expansionOpportunityCount);
432
433 // Don't justify for white-space: pre.
434 if (r->m_object->style()->whiteSpace() != PRE) {
435 InlineTextBox* textBox = toInlineTextBox(r->m_box);
436 int expansion = (availableLogicalWidth - totalLogicalWidth) * op portunitiesInRun / expansionOpportunityCount;
437 textBox->setExpansion(expansion);
438 totalLogicalWidth += expansion;
439 }
440 expansionOpportunityCount -= opportunitiesInRun;
441 if (!expansionOpportunityCount)
442 break;
443 }
444 }
445 }
446
447 void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign , const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalL eft, float& totalLogicalWidth, float& availableLogicalWidth, unsigned expansionO pportunityCount)
448 {
449 TextDirection direction;
450 if (rootInlineBox && rootInlineBox->renderer().style()->unicodeBidi() == Pla intext)
451 direction = rootInlineBox->direction();
452 else
453 direction = style()->direction();
454
455 // Armed with the total width of the line (without justification),
456 // we now examine our text-align property in order to determine where to pos ition the
457 // objects horizontally. The total width of the line can be increased if we end up
458 // justifying text.
459 switch (textAlign) {
460 case LEFT:
461 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
462 break;
463 case RIGHT:
464 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection() , trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
465 break;
466 case CENTER:
467 updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection( ), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
468 break;
469 case JUSTIFY:
470 adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
471 if (expansionOpportunityCount) {
472 if (trailingSpaceRun) {
473 totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
474 trailingSpaceRun->m_box->setLogicalWidth(0);
475 }
476 break;
477 }
478 // Fall through
479 case TASTART:
480 if (direction == LTR)
481 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirectio n(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
482 else
483 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirecti on(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
484 break;
485 case TAEND:
486 if (direction == LTR)
487 updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirecti on(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
488 else
489 updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirectio n(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
490 break;
491 }
492 }
493
494 static void updateLogicalInlinePositions(RenderBlockFlow* block, float& lineLogi calLeft, float& lineLogicalRight, float& availableLogicalWidth, IndentTextOrNot shouldIndentText)
495 {
496 lineLogicalLeft = block->logicalLeftOffsetForLine(shouldIndentText == Indent Text).toFloat();
497 lineLogicalRight = block->logicalRightOffsetForLine(shouldIndentText == Inde ntText).toFloat();
498 availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
499 }
500
501 void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* line Box, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, boo l reachedEnd,
502 GlyphOverflowAndFallbac kFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMea surements& wordMeasurements)
503 {
504 ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWit hBreak());
505
506 // 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
507 // box is only affected if it is the first child of its parent element."
508 // CSS3 "text-indent", "each-line" affects the first line of the block conta iner as well as each line after a forced line break,
509 // but does not affect lines after a soft wrap break.
510 bool isFirstLine = lineInfo.isFirstLine() && !(isAnonymousBlock() && parent( )->slowFirstChild() != this);
511 bool isAfterHardLineBreak = lineBox->prevRootBox() && lineBox->prevRootBox() ->endsWithBreak();
512 IndentTextOrNot shouldIndentText = requiresIndent(isFirstLine, isAfterHardLi neBreak, style());
513 float lineLogicalLeft;
514 float lineLogicalRight;
515 float availableLogicalWidth;
516 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availa bleLogicalWidth, shouldIndentText);
517 bool needsWordSpacing;
518
519 if (firstRun && firstRun->m_object->isReplaced())
520 updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, av ailableLogicalWidth, shouldIndentText);
521
522 computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, line LogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements);
523 // The widths of all runs are now known. We can now place every inline box ( and
524 // compute accurate widths for the inline flow boxes).
525 needsWordSpacing = false;
526 lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing);
527 }
528
529 BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBo x* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
530 float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& vertica lPositionCache,
531 WordMeasurements& wordMeasurements)
532 {
533 bool needsWordSpacing = true;
534 float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth().toFloat();
535 unsigned expansionOpportunityCount = 0;
536 bool isAfterExpansion = true;
537 Vector<unsigned, 16> expansionOpportunities;
538 RenderObject* previousObject = 0;
539 TextJustify textJustify = style()->textJustify();
540
541 BidiRun* r = firstRun;
542 for (; r; r = r->next()) {
543 if (!r->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLin eBreak())
544 continue; // Positioned objects are only participating to figure out their
545 // correct static x position. They have no effect on the width.
546 // Similarly, line break boxes have no effect on the width .
547 if (r->m_object->isText()) {
548 RenderText* rt = toRenderText(r->m_object);
549 if (textAlign == JUSTIFY && r != trailingSpaceRun && textJustify != TextJustifyNone) {
550 if (!isAfterExpansion)
551 toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
552 unsigned opportunitiesInRun;
553 if (rt->is8Bit())
554 opportunitiesInRun = Character::expansionOpportunityCount(rt ->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isA fterExpansion);
555 else
556 opportunitiesInRun = Character::expansionOpportunityCount(rt ->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), is AfterExpansion);
557 expansionOpportunities.append(opportunitiesInRun);
558 expansionOpportunityCount += opportunitiesInRun;
559 }
560
561 if (rt->textLength()) {
562 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->char acterAt(r->m_start)))
563 totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font ().fontDescription().wordSpacing();
564 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1));
565 }
566
567 setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInf o, textBoxDataMap, verticalPositionCache, wordMeasurements);
568 } else {
569 isAfterExpansion = false;
570 if (!r->m_object->isRenderInline()) {
571 RenderBox* renderBox = toRenderBox(r->m_object);
572 r->m_box->setLogicalWidth(logicalWidthForChild(renderBox).toFloa t());
573 totalLogicalWidth += marginStartForChild(renderBox) + marginEndF orChild(renderBox);
574 }
575 }
576
577 totalLogicalWidth += r->m_box->logicalWidth();
578 previousObject = r->m_object;
579 }
580
581 if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
582 expansionOpportunities.last()--;
583 expansionOpportunityCount--;
584 }
585
586 updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logical Left, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
587
588 computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpport unities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
589
590 return r;
591 }
592
593 void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineB ox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
594 VerticalPositionCache& v erticalPositionCache)
595 {
596 setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBo xDataMap, verticalPositionCache));
597
598 // Now make sure we place replaced render objects correctly.
599 for (BidiRun* r = firstRun; r; r = r->next()) {
600 ASSERT(r->m_box);
601 if (!r->m_box)
602 continue; // Skip runs with no line boxes.
603
604 // Align positioned boxes with the top of the line box. This is
605 // a reasonable approximation of an appropriate y position.
606 if (r->m_object->isOutOfFlowPositioned())
607 r->m_box->setLogicalTop(logicalHeight().toFloat());
608
609 // Position is used to properly position both replaced elements and
610 // to update the static normal flow x/y of positioned elements.
611 if (r->m_object->isText())
612 toRenderText(r->m_object)->positionLineBox(r->m_box);
613 else if (r->m_object->isBox())
614 toRenderBox(r->m_object)->positionLineBox(r->m_box);
615 }
616 }
617
618 // This function constructs line boxes for all of the text runs in the resolver and computes their position.
619 RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, V erticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeas urements& wordMeasurements)
620 {
621 if (!bidiRuns.runCount())
622 return 0;
623
624 // FIXME: Why is this only done when we had runs?
625 lineInfo.setLastLine(!end.object());
626
627 RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
628 if (!lineBox)
629 return 0;
630
631 lineBox->setBidiLevel(bidiLevel);
632 lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
633
634 GlyphOverflowAndFallbackFontsMap textBoxDataMap;
635
636 // Now we position all of our text runs horizontally.
637 computeInlineDirectionPositionsForLine(lineBox, lineInfo, bidiRuns.firstRun( ), trailingSpaceRun, end.atEnd(), textBoxDataMap, verticalPositionCache, wordMea surements);
638
639 // Now position our text runs vertically.
640 computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxD ataMap, verticalPositionCache);
641
642 // Compute our overflow now.
643 lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxD ataMap);
644
645 return lineBox;
646 }
647
648 static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLi ne, RootInlineBox* stopLine = 0)
649 {
650 RootInlineBox* boxToDelete = startLine;
651 while (boxToDelete && boxToDelete != stopLine) {
652 layoutState.updatePaintInvalidationRangeFromBox(boxToDelete);
653 // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineB oxTree().
654 // deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when tr aversing.
655 RootInlineBox* next = boxToDelete->nextRootBox();
656 boxToDelete->deleteLine();
657 boxToDelete = next;
658 }
659 }
660
661 void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState)
662 {
663 // We want to skip ahead to the first dirty line
664 InlineBidiResolver resolver;
665 RootInlineBox* startLine = determineStartPosition(layoutState, resolver);
666
667 // We also find the first clean line and extract these lines. We will add t hem back
668 // if we determine that we're able to synchronize after handling all our dir ty lines.
669 InlineIterator cleanLineStart;
670 BidiStatus cleanLineBidiStatus;
671 if (!layoutState.isFullLayout() && startLine)
672 determineEndPosition(layoutState, startLine, cleanLineStart, cleanLineBi diStatus);
673
674 if (startLine) {
675 if (!layoutState.usesPaintInvalidationBounds())
676 layoutState.setPaintInvalidationRange(logicalHeight());
677 deleteLineRange(layoutState, startLine);
678 }
679
680 layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineB idiStatus);
681 linkToEndLineIfNeeded(layoutState);
682 markDirtyFloatsForPaintInvalidation(layoutState.floats());
683 }
684
685 // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
686 inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange( LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastF loatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEn d)
687 {
688 setLogicalHeight(newLogicalHeight);
689 resolver.setPositionIgnoringNestedIsolates(oldEnd);
690 return oldEnd;
691 }
692
693 void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState,
694 InlineBidiResolver& resolver, const InlineIterator& cleanLineStart,
695 const BidiStatus& cleanLineBidiStatus)
696 {
697 RenderStyle* styleToUse = style();
698 LineMidpointState& lineMidpointState = resolver.midpointState();
699 InlineIterator endOfLine = resolver.position();
700 bool checkForEndLineMatch = layoutState.endLine();
701 RenderTextInfo renderTextInfo;
702 VerticalPositionCache verticalPositionCache;
703
704 LineBreaker lineBreaker(this);
705
706 while (!endOfLine.atEnd()) {
707 // FIXME: Is this check necessary before the first iteration or can it b e moved to the end?
708 if (checkForEndLineMatch) {
709 layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
710 if (layoutState.endLineMatched()) {
711 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
712 break;
713 }
714 }
715
716 lineMidpointState.reset();
717
718 layoutState.lineInfo().setEmpty(true);
719 layoutState.lineInfo().resetRunsFromLeadingWhitespace();
720
721 bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly ();
722 FloatingObject* lastFloatFromPreviousLine = 0;
723
724 WordMeasurements wordMeasurements;
725 endOfLine = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo,
726 lastFloatFromPreviousLine, wordMeasurements);
727 renderTextInfo.m_lineBreakIterator.resetPriorContext();
728 if (resolver.position().atEnd()) {
729 // FIXME: We shouldn't be creating any runs in nextLineBreak to begi n with!
730 // Once BidiRunList is separated from BidiResolver this will not be needed.
731 resolver.runs().deleteRuns();
732 resolver.markCurrentRunEmpty(); // FIXME: This can probably be repla ced by an ASSERT (or just removed).
733 layoutState.setCheckForFloatsFromLastLine(true);
734 resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0 ), 0);
735 break;
736 }
737
738 ASSERT(endOfLine != resolver.position());
739
740 // This is a short-cut for empty lines.
741 if (layoutState.lineInfo().isEmpty()) {
742 if (lastRootBox())
743 lastRootBox()->setLineBreakInfo(endOfLine.object(), endOfLine.of fset(), resolver.status());
744 } else {
745 VisualDirectionOverride override = (styleToUse->rtlOrdering() == Vis ualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualR ightToLeftOverride) : NoVisualOverride);
746 if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && ! resolver.context()->parent()) {
747 TextDirection direction = determinePlaintextDirectionality(resol ver.position().root(), resolver.position().object(), resolver.position().offset( ));
748 resolver.setStatus(BidiStatus(direction, isOverride(styleToUse-> unicodeBidi())));
749 }
750 // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine.
751 BidiRunList<BidiRun>& bidiRuns = resolver.runs();
752 constructBidiRunsForLine(resolver, bidiRuns, endOfLine, override, la youtState.lineInfo().previousLineBrokeCleanly(), isNewUBAParagraph);
753 ASSERT(resolver.position() == endOfLine);
754
755 BidiRun* trailingSpaceRun = resolver.trailingSpaceRun();
756
757 if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated())
758 bidiRuns.logicallyLastRun()->m_hasHyphen = true;
759
760 // Now that the runs have been ordered, we create the line boxes.
761 // At the same time we figure out where border/padding/margin should be applied for
762 // inline flow boxes.
763
764 RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status ().context->level(), bidiRuns, endOfLine, layoutState.lineInfo(), verticalPositi onCache, trailingSpaceRun, wordMeasurements);
765
766 bidiRuns.deleteRuns();
767 resolver.markCurrentRunEmpty(); // FIXME: This can probably be repla ced by an ASSERT (or just removed).
768
769 if (lineBox) {
770 lineBox->setLineBreakInfo(endOfLine.object(), endOfLine.offset() , resolver.status());
771 if (layoutState.usesPaintInvalidationBounds())
772 layoutState.updatePaintInvalidationRangeFromBox(lineBox);
773 }
774 }
775
776 for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
777 setStaticPositions(this, lineBreaker.positionedObjects()[i]);
778
779 if (!layoutState.lineInfo().isEmpty())
780 layoutState.lineInfo().setFirstLine(false);
781
782 lineMidpointState.reset();
783 resolver.setPosition(endOfLine, numberOfIsolateAncestors(endOfLine));
784 }
785 }
786
787 void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
788 {
789 if (layoutState.endLine()) {
790 if (layoutState.endLineMatched()) {
791 // Attach all the remaining lines, and then adjust their y-positions as needed.
792 LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop() ;
793 for (RootInlineBox* line = layoutState.endLine(); line; line = line- >nextRootBox()) {
794 line->attachLine();
795 if (delta) {
796 layoutState.updatePaintInvalidationRangeFromBox(line, delta) ;
797 line->adjustBlockDirectionPosition(delta.toFloat());
798 }
799 }
800 setLogicalHeight(lastRootBox()->lineBottomWithLeading());
801 } else {
802 // Delete all the remaining lines.
803 deleteLineRange(layoutState, layoutState.endLine());
804 }
805 }
806 }
807
808 void RenderBlockFlow::markDirtyFloatsForPaintInvalidation(Vector<FloatWithRect>& floats)
809 {
810 size_t floatCount = floats.size();
811 // Floats that did not have layout did not paint invalidations when we laid them out. They would have
812 // painted by now if they had moved, but if they stayed at (0, 0), they stil l need to be
813 // painted.
814 for (size_t i = 0; i < floatCount; ++i) {
815 if (!floats[i].everHadLayout) {
816 RenderBox* f = floats[i].object;
817 if (!f->x() && !f->y() && f->checkForPaintInvalidation()) {
818 f->setShouldDoFullPaintInvalidation(true);
819 }
820 }
821 }
822 }
823
824 struct InlineMinMaxIterator {
825 /* InlineMinMaxIterator is a class that will iterate over all render objects tha t contribute to
826 inline min/max width calculations. Note the following about the way it walks :
827 (1) Positioned content is skipped (since it does not contribute to min/max wi dth of a block)
828 (2) We do not drill into the children of floats or replaced elements, since y ou can't break
829 in the middle of such an element.
830 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side c an have
831 distinct borders/margin/padding that contribute to the min/max width.
832 */
833 RenderObject* parent;
834 RenderObject* current;
835 bool endOfInline;
836
837 InlineMinMaxIterator(RenderObject* p, bool end = false)
838 : parent(p), current(p), endOfInline(end)
839 {
840
841 }
842
843 RenderObject* next();
844 };
845
846 RenderObject* InlineMinMaxIterator::next()
847 {
848 RenderObject* result = 0;
849 bool oldEndOfInline = endOfInline;
850 endOfInline = false;
851 while (current || current == parent) {
852 if (!oldEndOfInline && (current == parent || (!current->isReplaced() && !current->isOutOfFlowPositioned())))
853 result = current->slowFirstChild();
854
855 if (!result) {
856 // We hit the end of our inline. (It was empty, e.g., <span></span>. )
857 if (!oldEndOfInline && current->isRenderInline()) {
858 result = current;
859 endOfInline = true;
860 break;
861 }
862
863 while (current && current != parent) {
864 result = current->nextSibling();
865 if (result)
866 break;
867 current = current->parent();
868 if (current && current != parent && current->isRenderInline()) {
869 result = current;
870 endOfInline = true;
871 break;
872 }
873 }
874 }
875
876 if (!result)
877 break;
878
879 if (!result->isOutOfFlowPositioned() && (result->isText() || result->isR eplaced() || result->isRenderInline()))
880 break;
881
882 current = result;
883 result = 0;
884 }
885
886 // Update our position.
887 current = result;
888 return current;
889 }
890
891 static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit)
892 {
893 if (cssUnit.type() != Auto)
894 return (cssUnit.isFixed() ? static_cast<LayoutUnit>(cssUnit.value()) : c hildValue);
895 return 0;
896 }
897
898 static LayoutUnit getBorderPaddingMargin(RenderBoxModelObject* child, bool endOf Inline)
899 {
900 RenderStyle* childStyle = child->style();
901 if (endOfInline) {
902 return getBPMWidth(child->marginEnd(), childStyle->marginEnd()) +
903 getBPMWidth(child->paddingEnd(), childStyle->paddingEnd()) +
904 child->borderEnd();
905 }
906 return getBPMWidth(child->marginStart(), childStyle->marginStart()) +
907 getBPMWidth(child->paddingStart(), childStyle->paddingStart()) +
908 child->borderStart();
909 }
910
911 static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, Render Object* trailingSpaceChild)
912 {
913 if (trailingSpaceChild && trailingSpaceChild->isText()) {
914 // Collapse away the trailing space at the end of a block.
915 RenderText* t = toRenderText(trailingSpaceChild);
916 const UChar space = ' ';
917 const Font& font = t->style()->font(); // FIXME: This ignores first-line .
918 float spaceWidth = font.width(constructTextRun(t, font, &space, 1, t->st yle(), LTR));
919 inlineMax -= spaceWidth + font.fontDescription().wordSpacing();
920 if (inlineMin > inlineMax)
921 inlineMin = inlineMax;
922 }
923 }
924
925 static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& resul t)
926 {
927 LayoutUnit snappedResult = LayoutUnit::fromFloatCeil(result);
928 preferredWidth = std::max(snappedResult, preferredWidth);
929 }
930
931 // When converting between floating point and LayoutUnits we risk losing precisi on
932 // with each conversion. When this occurs while accumulating our preferred width s,
933 // we can wind up with a line width that's larger than our maxPreferredWidth due to
934 // pure float accumulation.
935 static inline LayoutUnit adjustFloatForSubPixelLayout(float value)
936 {
937 return LayoutUnit::fromFloatCeil(value);
938 }
939
940 // FIXME: This function should be broken into something less monolithic.
941 // FIXME: The main loop here is very similar to LineBreaker::nextSegmentBreak. T hey can probably reuse code.
942 void RenderBlockFlow::computeInlinePreferredLogicalWidths(LayoutUnit& minLogical Width, LayoutUnit& maxLogicalWidth)
943 {
944 float inlineMax = 0;
945 float inlineMin = 0;
946
947 RenderStyle* styleToUse = style();
948 RenderBlock* containingBlock = this->containingBlock();
949 LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : L ayoutUnit();
950
951 // If we are at the start of a line, we want to ignore all white-space.
952 // Also strip spaces if we previously had text that ended in a trailing spac e.
953 bool stripFrontSpaces = true;
954 RenderObject* trailingSpaceChild = 0;
955
956 bool autoWrap, oldAutoWrap;
957 autoWrap = oldAutoWrap = styleToUse->autoWrap();
958
959 InlineMinMaxIterator childIterator(this);
960
961 // Only gets added to the max preffered width once.
962 bool addedTextIndent = false;
963 // Signals the text indent was more negative than the min preferred width
964 bool hasRemainingNegativeTextIndent = false;
965
966 LayoutUnit textIndent = minimumValueForLength(styleToUse->textIndent(), cw);
967 bool isPrevChildInlineFlow = false;
968 bool shouldBreakLineAfterText = false;
969 while (RenderObject* child = childIterator.next()) {
970 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() :
971 child->style()->autoWrap();
972
973 // Step One: determine whether or not we need to go ahead and
974 // terminate our current line. Each discrete chunk can become
975 // the new min-width, if it is the widest chunk seen so far, and
976 // it can also become the max-width.
977
978 // Children fall into three categories:
979 // (1) An inline flow object. These objects always have a min/max of 0,
980 // and are included in the iteration solely so that their margins can
981 // be added in.
982 //
983 // (2) An inline non-text non-flow object, e.g., an inline replaced elem ent.
984 // These objects can always be on a line by themselves, so in this situa tion
985 // we need to go ahead and break the current line, and then add in our o wn
986 // margins and min/max width on its own line, and then terminate the lin e.
987 //
988 // (3) A text object. Text runs can have breakable characters at the sta rt,
989 // the middle or the end. They may also lose whitespace off the front if
990 // we're already ignoring whitespace. In order to compute accurate min-w idth
991 // information, we need three pieces of information.
992 // (a) the min-width of the first non-breakable run. Should be 0 if the text string
993 // starts with whitespace.
994 // (b) the min-width of the last non-breakable run. Should be 0 if the t ext string
995 // ends with whitespace.
996 // (c) the min/max width of the string (trimmed for whitespace).
997 //
998 // If the text string starts with whitespace, then we need to go ahead a nd
999 // terminate our current line (unless we're already in a whitespace stri pping
1000 // mode.
1001 //
1002 // If the text string has a breakable character in the middle, but didn' t start
1003 // with whitespace, then we add the width of the first non-breakable run and
1004 // then end the current line. We then need to use the intermediate min/m ax width
1005 // values (if any of them are larger than our current min/max). We then look at
1006 // the width of the last non-breakable run and use that to start a new l ine
1007 // (unless we end in whitespace).
1008 RenderStyle* childStyle = child->style();
1009 float childMin = 0;
1010 float childMax = 0;
1011
1012 if (!child->isText()) {
1013 // Case (1) and (2). Inline replaced and inline flow elements.
1014 if (child->isRenderInline()) {
1015 // Add in padding/border/margin from the appropriate side of
1016 // the element.
1017 float bpm = getBorderPaddingMargin(toRenderInline(child), childI terator.endOfInline).toFloat();
1018 childMin += bpm;
1019 childMax += bpm;
1020
1021 inlineMin += childMin;
1022 inlineMax += childMax;
1023
1024 child->clearPreferredLogicalWidthsDirty();
1025 } else {
1026 // Inline replaced elts add in their margins to their min/max va lues.
1027 LayoutUnit margins = 0;
1028 Length startMargin = childStyle->marginStart();
1029 Length endMargin = childStyle->marginEnd();
1030 if (startMargin.isFixed())
1031 margins += adjustFloatForSubPixelLayout(startMargin.value()) ;
1032 if (endMargin.isFixed())
1033 margins += adjustFloatForSubPixelLayout(endMargin.value());
1034 childMin += margins.ceilToFloat();
1035 childMax += margins.ceilToFloat();
1036 }
1037 }
1038
1039 if (!child->isRenderInline() && !child->isText()) {
1040 // Case (2). Inline replaced elements and floats.
1041 // Go ahead and terminate the current line as far as
1042 // minwidth is concerned.
1043 LayoutUnit childMinPreferredLogicalWidth = child->minPreferredLogica lWidth();
1044 LayoutUnit childMaxPreferredLogicalWidth = child->maxPreferredLogica lWidth();
1045 childMin += childMinPreferredLogicalWidth.ceilToFloat();
1046 childMax += childMaxPreferredLogicalWidth.ceilToFloat();
1047
1048 bool canBreakReplacedElement = true;
1049 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPre vChildInlineFlow || shouldBreakLineAfterText))) {
1050 updatePreferredWidth(minLogicalWidth, inlineMin);
1051 inlineMin = 0;
1052 }
1053
1054 // Add in text-indent. This is added in only once.
1055 if (!addedTextIndent) {
1056 float ceiledTextIndent = textIndent.ceilToFloat();
1057 childMin += ceiledTextIndent;
1058 childMax += ceiledTextIndent;
1059
1060 if (childMin < 0)
1061 textIndent = adjustFloatForSubPixelLayout(childMin);
1062 else
1063 addedTextIndent = true;
1064 }
1065
1066 // Add our width to the max.
1067 inlineMax += std::max<float>(0, childMax);
1068
1069 if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) {
1070 inlineMin += childMin;
1071 } else {
1072 // Now check our line.
1073 updatePreferredWidth(minLogicalWidth, childMin);
1074
1075 // Now start a new line.
1076 inlineMin = 0;
1077 }
1078
1079 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) {
1080 updatePreferredWidth(minLogicalWidth, inlineMin);
1081 inlineMin = 0;
1082 }
1083
1084 // We are no longer stripping whitespace at the start of
1085 // a line.
1086 stripFrontSpaces = false;
1087 trailingSpaceChild = 0;
1088 } else if (child->isText()) {
1089 // Case (3). Text.
1090 RenderText* t = toRenderText(child);
1091
1092 // Determine if we have a breakable character. Pass in
1093 // whether or not we should ignore any spaces at the front
1094 // of the string. If those are going to be stripped out,
1095 // then they shouldn't be considered in the breakable char
1096 // check.
1097 bool hasBreakableChar, hasBreak;
1098 float firstLineMinWidth, lastLineMinWidth;
1099 bool hasBreakableStart, hasBreakableEnd;
1100 float firstLineMaxWidth, lastLineMaxWidth;
1101 t->trimmedPrefWidths(inlineMax,
1102 firstLineMinWidth, hasBreakableStart, lastLineMinWidth, hasBreak ableEnd,
1103 hasBreakableChar, hasBreak, firstLineMaxWidth, lastLineMaxWidth,
1104 childMin, childMax, stripFrontSpaces, styleToUse->direction());
1105
1106 // This text object will not be rendered, but it may still provide a breaking opportunity.
1107 if (!hasBreak && !childMax) {
1108 if (autoWrap && (hasBreakableStart || hasBreakableEnd)) {
1109 updatePreferredWidth(minLogicalWidth, inlineMin);
1110 inlineMin = 0;
1111 }
1112 continue;
1113 }
1114
1115 if (stripFrontSpaces)
1116 trailingSpaceChild = child;
1117 else
1118 trailingSpaceChild = 0;
1119
1120 // Add in text-indent. This is added in only once.
1121 float ti = 0;
1122 if (!addedTextIndent || hasRemainingNegativeTextIndent) {
1123 ti = textIndent.ceilToFloat();
1124 childMin += ti;
1125 firstLineMinWidth += ti;
1126
1127 // It the text indent negative and larger than the child minimum , we re-use the remainder
1128 // in future minimum calculations, but using the negative value again on the maximum
1129 // will lead to under-counting the max pref width.
1130 if (!addedTextIndent) {
1131 childMax += ti;
1132 firstLineMaxWidth += ti;
1133 addedTextIndent = true;
1134 }
1135
1136 if (childMin < 0) {
1137 textIndent = childMin;
1138 hasRemainingNegativeTextIndent = true;
1139 }
1140 }
1141
1142 // If we have no breakable characters at all,
1143 // then this is the easy case. We add ourselves to the current
1144 // min and max and continue.
1145 if (!hasBreakableChar) {
1146 inlineMin += childMin;
1147 } else {
1148 if (hasBreakableStart) {
1149 updatePreferredWidth(minLogicalWidth, inlineMin);
1150 } else {
1151 inlineMin += firstLineMinWidth;
1152 updatePreferredWidth(minLogicalWidth, inlineMin);
1153 childMin -= ti;
1154 }
1155
1156 inlineMin = childMin;
1157
1158 if (hasBreakableEnd) {
1159 updatePreferredWidth(minLogicalWidth, inlineMin);
1160 inlineMin = 0;
1161 shouldBreakLineAfterText = false;
1162 } else {
1163 updatePreferredWidth(minLogicalWidth, inlineMin);
1164 inlineMin = lastLineMinWidth;
1165 shouldBreakLineAfterText = true;
1166 }
1167 }
1168
1169 if (hasBreak) {
1170 inlineMax += firstLineMaxWidth;
1171 updatePreferredWidth(maxLogicalWidth, inlineMax);
1172 updatePreferredWidth(maxLogicalWidth, childMax);
1173 inlineMax = lastLineMaxWidth;
1174 addedTextIndent = true;
1175 } else {
1176 inlineMax += std::max<float>(0, childMax);
1177 }
1178 }
1179
1180 if (!child->isText() && child->isRenderInline())
1181 isPrevChildInlineFlow = true;
1182 else
1183 isPrevChildInlineFlow = false;
1184
1185 oldAutoWrap = autoWrap;
1186 }
1187
1188 if (styleToUse->collapseWhiteSpace())
1189 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild);
1190
1191 updatePreferredWidth(minLogicalWidth, inlineMin);
1192 updatePreferredWidth(maxLogicalWidth, inlineMax);
1193 }
1194
1195 void RenderBlockFlow::layoutInlineChildren(bool relayoutChildren, LayoutUnit& pa intInvalidationLogicalTop, LayoutUnit& paintInvalidationLogicalBottom, LayoutUni t afterEdge)
1196 {
1197 // Figure out if we should clear out our line boxes.
1198 // FIXME: Handle resize eventually!
1199 bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren ;
1200 LineLayoutState layoutState(isFullLayout, paintInvalidationLogicalTop, paint InvalidationLogicalBottom);
1201
1202 if (isFullLayout) {
1203 // Ensure the old line boxes will be erased.
1204 if (firstLineBox())
1205 setShouldDoFullPaintInvalidation(true);
1206 lineBoxes()->deleteLineBoxes();
1207 }
1208
1209 // Text truncation kicks in in two cases:
1210 // 1) If your overflow isn't visible and your text-overflow-mode isn't c lip.
1211 // 2) If you're an anonymous paragraph with a parent that satisfies #1.
1212 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
1213 // difficult to figure out in general (especially in the middle of doing lay out), so we only handle the
1214 // simple case of an anonymous block truncating when it's parent is clipped.
1215 bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
1216 || (isAnonymousBlock() && parent() && parent()->style()->textOverflow() && parent()->hasOverflowClip());
1217
1218 // Walk all the lines and delete our ellipsis line boxes if they exist.
1219 if (hasTextOverflow)
1220 deleteEllipsisLineBoxes();
1221
1222 if (firstChild()) {
1223 // In full layout mode, clear the line boxes of children upfront. Otherw ise,
1224 // siblings can run into stale root lineboxes during layout. Then layout
1225 // the replaced elements later. In partial layout mode, line boxes are n ot
1226 // deleted and only dirtied. In that case, we can layout the replaced
1227 // elements at the same time.
1228 Vector<RenderBox*> replacedChildren;
1229 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
1230 RenderObject* o = walker.current();
1231
1232 if (!layoutState.hasInlineChild() && o->isInline())
1233 layoutState.setHasInlineChild(true);
1234
1235 if (o->isReplaced() || o->isOutOfFlowPositioned()) {
1236 RenderBox* box = toRenderBox(o);
1237
1238 updateBlockChildDirtyBitsBeforeLayout(relayoutChildren, box);
1239
1240 if (o->isOutOfFlowPositioned()) {
1241 o->containingBlock()->insertPositionedObject(box);
1242 } else if (isFullLayout || o->needsLayout()) {
1243 // Replaced element.
1244 box->dirtyLineBoxes(isFullLayout);
1245 if (isFullLayout)
1246 replacedChildren.append(box);
1247 else
1248 o->layoutIfNeeded();
1249 }
1250 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInl ine())) {
1251 if (!o->isText())
1252 toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.i sFullLayout());
1253 if (layoutState.isFullLayout() || o->selfNeedsLayout())
1254 dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
1255 o->clearNeedsLayout();
1256 }
1257 }
1258
1259 for (size_t i = 0; i < replacedChildren.size(); i++)
1260 replacedChildren[i]->layoutIfNeeded();
1261
1262 layoutRunsAndFloats(layoutState);
1263 }
1264
1265 // Expand the last line to accommodate Ruby and emphasis marks.
1266 int lastLineAnnotationsAdjustment = 0;
1267 if (lastRootBox()) {
1268 LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
1269 lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdj ustment(lowestAllowedPosition);
1270 }
1271
1272 // Now add in the bottom border/padding.
1273 setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + afterEdge );
1274
1275 if (!firstLineBox() && hasLineIfEmpty())
1276 setLogicalHeight(logicalHeight() + lineHeight(true, HorizontalLine, Posi tionOfInteriorLineBoxes));
1277
1278 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1279 // truncate text.
1280 if (hasTextOverflow)
1281 checkLinesForTextOverflow();
1282
1283 // Ensure the new line boxes will be painted.
1284 if (isFullLayout && firstLineBox())
1285 setShouldDoFullPaintInvalidation(true);
1286 }
1287
1288 void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWi thRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByF loat)
1289 {
1290 Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
1291 if (!cleanLineFloats)
1292 return;
1293
1294 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1295 for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
1296 RenderBox* floatingBox = *it;
1297 floatingBox->layoutIfNeeded();
1298 LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), fl oatingBox->height() + floatingBox->marginHeight());
1299 if (floats[floatIndex].object != floatingBox) {
1300 encounteredNewFloat = true;
1301 return;
1302 }
1303
1304 if (floats[floatIndex].rect.size() != newSize) {
1305 LayoutUnit floatTop = floats[floatIndex].rect.y();
1306 LayoutUnit floatHeight = std::max(floats[floatIndex].rect.height(), newSize.height());
1307 floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop);
1308 line->markDirty();
1309 markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
1310 floats[floatIndex].rect.setSize(newSize);
1311 dirtiedByFloat = true;
1312 }
1313 floatIndex++;
1314 }
1315 }
1316
1317 RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutSt ate, InlineBidiResolver& resolver)
1318 {
1319 RootInlineBox* curr = 0;
1320 RootInlineBox* last = 0;
1321
1322 // FIXME: This entire float-checking block needs to be broken into a new fun ction.
1323 bool dirtiedByFloat = false;
1324 if (!layoutState.isFullLayout()) {
1325 size_t floatIndex = 0;
1326 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextR ootBox()) {
1327 // If a new float has been inserted before this line or before its l ast known float, just do a full layout.
1328 bool encounteredNewFloat = false;
1329 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encou nteredNewFloat, dirtiedByFloat);
1330 if (encounteredNewFloat)
1331 layoutState.markForFullLayout();
1332
1333 if (dirtiedByFloat || layoutState.isFullLayout())
1334 break;
1335 }
1336 // Check if a new float has been inserted after the last known float.
1337 if (!curr && floatIndex < layoutState.floats().size())
1338 layoutState.markForFullLayout();
1339 }
1340
1341 if (layoutState.isFullLayout()) {
1342 // If we encountered a new float and have inline children, mark ourself to force us to issue paint invalidations.
1343 if (layoutState.hasInlineChild() && !selfNeedsLayout()) {
1344 setNeedsLayoutAndFullPaintInvalidation(MarkOnlyThis);
1345 setShouldDoFullPaintInvalidation(true);
1346 }
1347
1348 // FIXME: This should just call deleteLineBoxTree, but that causes
1349 // crashes for fast/repaint tests.
1350 curr = firstRootBox();
1351 while (curr) {
1352 // Note: This uses nextRootBox() insted of nextLineBox() like delete LineBoxTree does.
1353 RootInlineBox* next = curr->nextRootBox();
1354 curr->deleteLine();
1355 curr = next;
1356 }
1357 ASSERT(!firstLineBox() && !lastLineBox());
1358 } else {
1359 if (curr) {
1360 // We have a dirty line.
1361 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1362 // We have a previous line.
1363 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRo otBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox-> lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
1364 // The previous line didn't break cleanly or broke at a newl ine
1365 // that has been deleted, so treat it as dirty too.
1366 curr = prevRootBox;
1367 }
1368 } else {
1369 // No dirty lines were found.
1370 // If the last line didn't break cleanly, treat it as dirty.
1371 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1372 curr = lastRootBox();
1373 }
1374
1375 // If we have no dirty lines, then last is just the last root box.
1376 last = curr ? curr->prevRootBox() : lastRootBox();
1377 }
1378
1379 layoutState.lineInfo().setFirstLine(!last);
1380 layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBr eak());
1381
1382 if (last) {
1383 setLogicalHeight(last->lineBottomWithLeading());
1384 InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->l ineBreakPos());
1385 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1386 resolver.setStatus(last->lineBreakBidiStatus());
1387 } else {
1388 TextDirection direction = style()->direction();
1389 if (style()->unicodeBidi() == Plaintext)
1390 direction = determinePlaintextDirectionality(this);
1391 resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi ())));
1392 InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines (this, resolver.runs(), &resolver), 0);
1393 resolver.setPosition(iter, numberOfIsolateAncestors(iter));
1394 }
1395 return curr;
1396 }
1397
1398 void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInl ineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStat us)
1399 {
1400 ASSERT(!layoutState.endLine());
1401 size_t floatIndex = layoutState.floatIndex();
1402 RootInlineBox* last = 0;
1403 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->next RootBox()) {
1404 if (!curr->isDirty()) {
1405 bool encounteredNewFloat = false;
1406 bool dirtiedByFloat = false;
1407 checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encou nteredNewFloat, dirtiedByFloat);
1408 if (encounteredNewFloat)
1409 return;
1410 }
1411 if (curr->isDirty())
1412 last = 0;
1413 else if (!last)
1414 last = curr;
1415 }
1416
1417 if (!last)
1418 return;
1419
1420 // At this point, |last| is the first line in a run of clean lines that ends with the last line
1421 // in the block.
1422
1423 RootInlineBox* prev = last->prevRootBox();
1424 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakP os());
1425 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1426 layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
1427
1428 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1429 line->extractLine(); // Disconnect all line boxes from their render obje cts while preserving
1430 // their connections to one another.
1431
1432 layoutState.setEndLine(last);
1433 }
1434
1435 bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutS tate)
1436 {
1437 // FIXME(sky): Remove this.
1438 return true;
1439 }
1440
1441 bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineB idiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& end LineStatus)
1442 {
1443 if (resolver.position() == endLineStart) {
1444 if (resolver.status() != endLineStatus)
1445 return false;
1446 return checkPaginationAndFloatsAtEndLine(layoutState);
1447 }
1448
1449 // The first clean line doesn't match, but we can check a handful of followi ng lines to try
1450 // to match back up.
1451 static int numLines = 8; // The # of lines we're willing to match against.
1452 RootInlineBox* originalEndLine = layoutState.endLine();
1453 RootInlineBox* line = originalEndLine;
1454 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1455 if (line->lineBreakObj() == resolver.position().object() && line->lineBr eakPos() == resolver.position().offset()) {
1456 // We have a match.
1457 if (line->lineBreakBidiStatus() != resolver.status())
1458 return false; // ...but the bidi state doesn't match.
1459
1460 bool matched = false;
1461 RootInlineBox* result = line->nextRootBox();
1462 layoutState.setEndLine(result);
1463 if (result) {
1464 layoutState.setEndLineLogicalTop(line->lineBottomWithLeading());
1465 matched = checkPaginationAndFloatsAtEndLine(layoutState);
1466 }
1467
1468 // Now delete the lines that we failed to sync.
1469 deleteLineRange(layoutState, originalEndLine, result);
1470 return matched;
1471 }
1472 }
1473
1474 return false;
1475 }
1476
1477 bool RenderBlockFlow::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
1478
1479 {
1480 ASSERT(inlineObj->parent() == this);
1481
1482 InlineIterator it(this, inlineObj, 0);
1483 // FIXME: We should pass correct value for WhitespacePosition.
1484 while (!it.atEnd() && !requiresLineBox(it))
1485 it.increment();
1486
1487 return !it.atEnd();
1488 }
1489
1490 void RenderBlockFlow::deleteEllipsisLineBoxes()
1491 {
1492 ETextAlign textAlign = style()->textAlign();
1493 bool ltr = style()->isLeftToRightDirection();
1494 bool firstLine = true;
1495 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
1496 if (curr->hasEllipsisBox()) {
1497 curr->clearTruncation();
1498
1499 // Shift the line back where it belongs if we cannot accomodate an e llipsis.
1500 float logicalLeft = logicalLeftOffsetForLine(firstLine).toFloat();
1501 float availableLogicalWidth = logicalRightOffsetForLine(false) - log icalLeft;
1502 float totalLogicalWidth = curr->logicalWidth();
1503 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, tota lLogicalWidth, availableLogicalWidth, 0);
1504
1505 if (ltr)
1506 curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
1507 else
1508 curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft) , 0);
1509 }
1510 firstLine = false;
1511 }
1512 }
1513
1514 void RenderBlockFlow::checkLinesForTextOverflow()
1515 {
1516 // Determine the width of the ellipsis using the current font.
1517 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP ) if horizontal ellipsis is "not renderable"
1518 const Font& font = style()->font();
1519 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
1520 const Font& firstLineFont = firstLineStyle()->font();
1521 // FIXME: We should probably not hard-code the direction here. https://crbug .com/333004
1522 TextDirection ellipsisDirection = LTR;
1523 float firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, fi rstLineFont, &horizontalEllipsis, 1, firstLineStyle(), ellipsisDirection));
1524 float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : fon t.width(constructTextRun(this, font, &horizontalEllipsis, 1, style(), ellipsisDi rection));
1525
1526 // For LTR text truncation, we want to get the right edge of our padding box , and then we want to see
1527 // if the right edge of a line box exceeds that. For RTL, we use the left e dge of the padding box and
1528 // check the left edge of the line box to see if it is less
1529 // Include the scrollbar for overflow blocks, which means we want to use "co ntentWidth()"
1530 bool ltr = style()->isLeftToRightDirection();
1531 ETextAlign textAlign = style()->textAlign();
1532 bool firstLine = true;
1533 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
1534 float currLogicalLeft = curr->logicalLeft();
1535 LayoutUnit blockRightEdge = logicalRightOffsetForLine(firstLine);
1536 LayoutUnit blockLeftEdge = logicalLeftOffsetForLine(firstLine);
1537 LayoutUnit lineBoxEdge = ltr ? currLogicalLeft + curr->logicalWidth() : currLogicalLeft;
1538 if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < bloc kLeftEdge)) {
1539 // This line spills out of our box in the appropriate direction. No w we need to see if the line
1540 // can be truncated. In order for truncation to be possible, the li ne must have sufficient space to
1541 // accommodate our truncation string, and no replaced elements (imag es, tables) can overlap the ellipsis
1542 // space.
1543
1544 LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidt h;
1545 LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge;
1546 if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, wi dth)) {
1547 float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge.toFloat(), blockRightEdge.toFloat(), width.toFloat());
1548
1549 float logicalLeft = 0; // We are only intersted in the delta fro m the base position.
1550 float availableLogicalWidth = (blockRightEdge - blockLeftEdge).t oFloat();
1551 updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
1552 if (ltr)
1553 curr->adjustLogicalPosition(logicalLeft, 0);
1554 else
1555 curr->adjustLogicalPosition(logicalLeft - (availableLogicalW idth - totalLogicalWidth), 0);
1556 }
1557 }
1558 firstLine = false;
1559 }
1560 }
1561
1562 LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(bool firstLine)
1563 {
1564 ETextAlign textAlign = style()->textAlign();
1565
1566 if (textAlign == TASTART) // FIXME: Handle TAEND here
1567 return startOffsetForLine(firstLine);
1568
1569 // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
1570 float totalLogicalWidth = 0;
1571 float logicalLeft = logicalLeftOffsetForLine(false).toFloat();
1572 float availableLogicalWidth = logicalRightOffsetForLine(false) - logicalLeft ;
1573 updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWid th, availableLogicalWidth, 0);
1574
1575 if (!style()->isLeftToRightDirection())
1576 return logicalWidth() - logicalLeft;
1577 return logicalLeft;
1578 }
1579
1580 }
OLDNEW
« no previous file with comments | « sky/engine/core/rendering/RenderBlockFlow.cpp ('k') | sky/engine/core/rendering/RenderLineBoxList.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698