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

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

Powered by Google App Engine
This is Rietveld 408576698