2
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3
* Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Library General Public
7
* License as published by the Free Software Foundation; either
8
* version 2 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Library General Public License for more details.
15
* You should have received a copy of the GNU Library General Public License
16
* along with this library; see the file COPYING.LIB. If not, write to
17
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
* Boston, MA 02110-1301, USA.
25
#include "CharacterNames.h"
28
#include "FrameView.h"
29
#include "InlineTextBox.h"
31
#include "RenderArena.h"
32
#include "RenderLayer.h"
33
#include "RenderListMarker.h"
34
#include "RenderView.h"
35
#include "break_lines.h"
36
#include <wtf/AlwaysInline.h>
37
#include <wtf/RefCountedLeakCounter.h>
38
#include <wtf/StdLibExtras.h>
39
#include <wtf/Vector.h>
43
using namespace Unicode;
47
// We don't let our line box tree for a single line get any deeper than this.
48
const unsigned cMaxLineDepth = 200;
50
class InlineIterator {
56
, nextBreakablePosition(-1)
60
InlineIterator(RenderBlock* b, RenderObject* o, unsigned p)
64
, nextBreakablePosition(-1)
68
void increment(InlineBidiResolver* resolver = 0);
71
UChar current() const;
72
WTF::Unicode::Direction direction() const;
77
int nextBreakablePosition;
80
// Midpoint globals. The goal is not to do any allocation when dealing with
81
// these midpoints, so we just keep an array around and never clear it. We track
82
// the number of items and position using the two other variables.
83
static Vector<InlineIterator>* smidpoints;
84
static unsigned sNumMidpoints;
85
static unsigned sCurrMidpoint;
86
static bool betweenMidpoints;
88
static bool isLineEmpty = true;
89
static bool previousLineBrokeCleanly = true;
91
static int getBorderPaddingMargin(RenderObject* child, bool endOfInline)
93
bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline;
95
return child->marginLeft() + child->paddingLeft() + child->borderLeft();
96
return child->marginRight() + child->paddingRight() + child->borderRight();
99
static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
101
unsigned lineDepth = 1;
103
RenderObject* parent = child->parent();
104
while (parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) {
105
if (start && parent->firstChild() == child)
106
extraWidth += getBorderPaddingMargin(parent, false);
107
if (end && parent->lastChild() == child)
108
extraWidth += getBorderPaddingMargin(parent, true);
110
parent = child->parent();
116
static WTF::RefCountedLeakCounter bidiRunCounter("BidiRun");
118
static bool inBidiRunDestroy;
121
void BidiRun::destroy()
124
inBidiRunDestroy = true;
126
RenderArena* renderArena = m_object->renderArena();
129
inBidiRunDestroy = false;
132
// Recover the size left there for us by operator delete and free the memory.
133
renderArena->free(*reinterpret_cast<size_t*>(this), this);
136
void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
139
bidiRunCounter.increment();
141
return renderArena->allocate(sz);
144
void BidiRun::operator delete(void* ptr, size_t sz)
147
bidiRunCounter.decrement();
149
ASSERT(inBidiRunDestroy);
151
// Stash size where destroy() can find it.
155
// ---------------------------------------------------------------------
157
inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
159
return it1.pos == it2.pos && it1.obj == it2.obj;
162
inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
164
return it1.pos != it2.pos || it1.obj != it2.obj;
167
static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
169
RenderObject* next = 0;
170
bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
171
bool endOfInline = false;
175
if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
176
next = current->firstChild();
177
if (next && resolver && next->isInlineFlow()) {
178
EUnicodeBidi ub = next->style()->unicodeBidi();
179
if (ub != UBNormal) {
180
TextDirection dir = next->style()->direction();
181
Direction d = (ub == Embed
182
? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
183
: (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
190
if (!skipInlines && !oldEndOfInline && current->isInlineFlow()) {
196
while (current && current != block) {
197
if (resolver && current->isInlineFlow() && current->style()->unicodeBidi() != UBNormal)
198
resolver->embed(PopDirectionalFormat);
200
next = current->nextSibling();
202
if (resolver && next->isInlineFlow()) {
203
EUnicodeBidi ub = next->style()->unicodeBidi();
204
if (ub != UBNormal) {
205
TextDirection dir = next->style()->direction();
206
Direction d = (ub == Embed
207
? (dir == RTL ? RightToLeftEmbedding: LeftToRightEmbedding)
208
: (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
215
current = current->parent();
216
if (!skipInlines && current && current != block && current->isInlineFlow()) {
227
if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned()
228
|| ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
229
&& next->isInlineFlow()))
235
*endOfInlinePtr = endOfInline;
240
static RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true)
242
if (!block->firstChild())
245
RenderObject* o = block->firstChild();
246
if (o->isInlineFlow()) {
248
EUnicodeBidi ub = o->style()->unicodeBidi();
249
if (ub != UBNormal) {
250
TextDirection dir = o->style()->direction();
251
Direction d = (ub == Embed
252
? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
253
: (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
257
if (skipInlines && o->firstChild())
258
o = bidiNext(block, o, resolver, skipInlines);
260
// Never skip empty inlines.
262
resolver->commitExplicitEmbedding();
267
if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
268
o = bidiNext(block, o, resolver, skipInlines);
271
resolver->commitExplicitEmbedding();
275
inline void InlineIterator::increment(InlineBidiResolver* resolver)
281
if (pos >= static_cast<RenderText*>(obj)->textLength()) {
282
obj = bidiNext(block, obj, resolver);
284
nextBreakablePosition = -1;
287
obj = bidiNext(block, obj, resolver);
289
nextBreakablePosition = -1;
294
inline void InlineBidiResolver::increment()
296
current.increment(this);
299
inline bool InlineIterator::atEnd() const
304
inline UChar InlineIterator::current() const
306
if (!obj || !obj->isText())
309
RenderText* text = static_cast<RenderText*>(obj);
310
if (pos >= text->textLength())
313
return text->characters()[pos];
316
ALWAYS_INLINE Direction InlineIterator::direction() const
318
if (UChar c = current())
319
return Unicode::direction(c);
321
if (obj && obj->isListMarker())
322
return obj->style()->direction() == LTR ? LeftToRight : RightToLeft;
327
// -------------------------------------------------------------------------------------------------
329
static void chopMidpointsAt(RenderObject* obj, unsigned pos)
333
InlineIterator* midpoints = smidpoints->data();
334
for (int i = sNumMidpoints - 1; i >= 0; i--) {
335
const InlineIterator& point = midpoints[i];
336
if (point.obj == obj && point.pos == pos) {
343
static void checkMidpoints(InlineIterator& lBreak)
345
// Check to see if our last midpoint is a start point beyond the line break. If so,
346
// shave it off the list, and shave off a trailing space if the previous end point doesn't
347
// preserve whitespace.
348
if (lBreak.obj && sNumMidpoints && sNumMidpoints % 2 == 0) {
349
InlineIterator* midpoints = smidpoints->data();
350
InlineIterator& endpoint = midpoints[sNumMidpoints-2];
351
const InlineIterator& startpoint = midpoints[sNumMidpoints-1];
352
InlineIterator currpoint = endpoint;
353
while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
354
currpoint.increment();
355
if (currpoint == lBreak) {
356
// We hit the line break before the start point. Shave off the start point.
358
if (endpoint.obj->style()->collapseWhiteSpace()) {
359
if (endpoint.obj->isText()) {
360
// Don't shave a character off the endpoint if it was from a soft hyphen.
361
RenderText* textObj = static_cast<RenderText*>(endpoint.obj);
362
if (endpoint.pos + 1 < textObj->textLength()) {
363
if (textObj->characters()[endpoint.pos+1] == softHyphen)
365
} else if (startpoint.obj->isText()) {
366
RenderText *startText = static_cast<RenderText*>(startpoint.obj);
367
if (startText->textLength() && startText->characters()[0] == softHyphen)
377
static void addMidpoint(const InlineIterator& midpoint)
379
if (smidpoints->size() <= sNumMidpoints)
380
smidpoints->grow(sNumMidpoints + 10);
382
InlineIterator* midpoints = smidpoints->data();
383
midpoints[sNumMidpoints++] = midpoint;
386
static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
388
if (start > end || obj->isFloating() ||
389
(obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isInlineFlow()))
392
bool haveNextMidpoint = (sCurrMidpoint < sNumMidpoints);
393
InlineIterator nextMidpoint;
394
if (haveNextMidpoint)
395
nextMidpoint = smidpoints->at(sCurrMidpoint);
396
if (betweenMidpoints) {
397
if (!(haveNextMidpoint && nextMidpoint.obj == obj))
399
// This is a new start point. Stop ignoring objects and
401
betweenMidpoints = false;
402
start = nextMidpoint.pos;
405
return appendRunsForObject(start, end, obj, resolver);
407
if (!haveNextMidpoint || (obj != nextMidpoint.obj)) {
408
resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
412
// An end midpoint has been encountered within our object. We
413
// need to go ahead and append a run with our endpoint.
414
if (static_cast<int>(nextMidpoint.pos + 1) <= end) {
415
betweenMidpoints = true;
417
if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
418
if (static_cast<int>(nextMidpoint.pos + 1) > start)
419
resolver.addRun(new (obj->renderArena())
420
BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir()));
421
return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver);
424
resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
429
void InlineBidiResolver::appendRun()
431
if (!emptyRun && !eor.atEnd()) {
433
RenderObject *obj = sor.obj;
434
while (obj && obj != eor.obj && obj != endOfLine.obj) {
435
appendRunsForObject(start, obj->length(), obj, *this);
437
obj = bidiNext(sor.block, obj);
440
unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX;
441
if (obj == endOfLine.obj && endOfLine.pos <= pos) {
442
reachedEndOfLine = true;
445
// It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
446
int end = obj->length() ? pos+1 : 0;
447
appendRunsForObject(start, end, obj, *this);
454
m_direction = OtherNeutral;
455
m_status.eor = OtherNeutral;
458
InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
460
// See if we have an unconstructed line box for this object that is also
461
// the last item on the line.
462
unsigned lineDepth = 1;
463
InlineFlowBox* childBox = 0;
464
InlineFlowBox* parentBox = 0;
465
InlineFlowBox* result = 0;
467
ASSERT(obj->isInlineFlow() || obj == this);
468
RenderFlow* flow = static_cast<RenderFlow*>(obj);
470
// Get the last box we made for this render object.
471
parentBox = flow->lastLineBox();
473
// If this box is constructed then it is from a previous line, and we need
474
// to make a new box for our line. If this box is unconstructed but it has
475
// something following it on the line, then we know we have to make a new box
476
// as well. In this situation our inline has actually been split in two on
477
// the same line (this can happen with very fancy language mixtures).
478
bool constructedNewBox = false;
479
if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) {
480
// We need to make a new box for this render object. Once
481
// made, we need to place it at the end of the current line.
482
InlineBox* newBox = obj->createInlineBox(false, obj == this);
483
ASSERT(newBox->isInlineFlowBox());
484
parentBox = static_cast<InlineFlowBox*>(newBox);
485
parentBox->setFirstLineStyleBit(m_firstLine);
486
constructedNewBox = true;
492
// If we have hit the block itself, then |box| represents the root
493
// inline box for the line, and it doesn't have to be appended to any parent
496
parentBox->addToLine(childBox);
498
if (!constructedNewBox || obj == this)
501
childBox = parentBox;
503
// If we've exceeded our line depth, then jump straight to the root and skip all the remaining
504
// intermediate inline flows.
505
obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
512
RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject)
516
InlineFlowBox* parentBox = 0;
517
for (BidiRun* r = firstRun; r; r = r->next()) {
518
// Create a box for our object.
519
bool isOnlyRun = (runCount == 1);
520
if (runCount == 2 && !r->m_object->isListMarker())
521
isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker();
523
InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun);
527
// If we have no parent box yet, or if the run is not simply a sibling,
528
// then we need to construct inline boxes as necessary to properly enclose the
530
if (!parentBox || parentBox->object() != r->m_object->parent())
531
// Create new inline boxes all the way back to the appropriate insertion point.
532
parentBox = createLineBoxes(r->m_object->parent());
534
// Append the inline box to this line.
535
parentBox->addToLine(box);
537
bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
538
box->setBidiLevel(visuallyOrdered ? 0 : r->level());
540
if (box->isInlineTextBox()) {
541
InlineTextBox* text = static_cast<InlineTextBox*>(box);
542
text->setStart(r->m_start);
543
text->setLen(r->m_stop - r->m_start);
544
text->m_dirOverride = r->dirOverride(visuallyOrdered);
549
// We should have a root inline box. It should be unconstructed and
550
// be the last continuation of our line list.
551
ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
553
// Set bits on our inline flow boxes that indicate which sides should
554
// paint borders/margins/padding. This knowledge will ultimately be used when
555
// we determine the horizontal positions and widths of all the inline boxes on
557
lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
559
// Now mark the line boxes as being constructed.
560
lastLineBox()->setConstructed();
562
// Return the last line.
563
return lastRootBox();
566
void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd)
568
// First determine our total width.
569
int availableWidth = lineWidth(m_height);
570
int totWidth = lineBox->getFlowSpacingWidth();
571
bool needsWordSpacing = false;
572
unsigned numSpaces = 0;
573
ETextAlign textAlign = style()->textAlign();
575
for (BidiRun* r = firstRun; r; r = r->next()) {
576
if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak())
577
continue; // Positioned objects are only participating to figure out their
578
// correct static x position. They have no effect on the width.
579
// Similarly, line break boxes have no effect on the width.
580
if (r->m_object->isText()) {
581
RenderText* rt = static_cast<RenderText*>(r->m_object);
583
if (textAlign == JUSTIFY && r != trailingSpaceRun) {
584
const UChar* characters = rt->characters();
585
for (int i = r->m_start; i < r->m_stop; i++) {
586
UChar c = characters[i];
587
if (c == ' ' || c == '\n' || c == '\t')
592
if (int length = rt->textLength()) {
593
if (!r->m_compact && !r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start]))
594
totWidth += rt->style(m_firstLine)->font().wordSpacing();
595
needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length;
597
r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, m_firstLine));
598
} else if (!r->m_object->isInlineFlow()) {
599
r->m_object->calcWidth();
600
r->m_box->setWidth(r->m_object->width());
602
totWidth += r->m_object->marginLeft() + r->m_object->marginRight();
605
// Compacts don't contribute to the width of the line, since they are placed in the margin.
607
totWidth += r->m_box->width();
610
// Armed with the total width of the line (without justification),
611
// we now examine our text-align property in order to determine where to position the
612
// objects horizontally. The total width of the line can be increased if we end up
614
int x = leftOffset(m_height);
618
// The direction of the block should determine what happens with wide lines. In
619
// particular with RTL blocks, wide lines should still spill out to the left.
620
if (style()->direction() == LTR) {
621
if (totWidth > availableWidth && trailingSpaceRun)
622
trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
624
if (trailingSpaceRun)
625
trailingSpaceRun->m_box->setWidth(0);
626
else if (totWidth > availableWidth)
627
x -= (totWidth - availableWidth);
631
if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) {
632
if (trailingSpaceRun) {
633
totWidth -= trailingSpaceRun->m_box->width();
634
trailingSpaceRun->m_box->setWidth(0);
641
// for right to left fall through to right aligned
642
if (style()->direction() == LTR) {
643
if (totWidth > availableWidth && trailingSpaceRun)
644
trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
649
// Wide lines spill out of the block based off direction.
650
// So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
651
// side of the block.
652
if (style()->direction() == LTR) {
653
if (trailingSpaceRun) {
654
totWidth -= trailingSpaceRun->m_box->width();
655
trailingSpaceRun->m_box->setWidth(0);
657
if (totWidth < availableWidth)
658
x += availableWidth - totWidth;
660
if (totWidth > availableWidth && trailingSpaceRun) {
661
trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
662
totWidth -= trailingSpaceRun->m_box->width();
664
x += availableWidth - totWidth;
669
int trailingSpaceWidth = 0;
670
if (trailingSpaceRun) {
671
totWidth -= trailingSpaceRun->m_box->width();
672
trailingSpaceWidth = min(trailingSpaceRun->m_box->width(), (availableWidth - totWidth + 1) / 2);
673
trailingSpaceRun->m_box->setWidth(trailingSpaceWidth);
675
if (style()->direction() == LTR)
676
x += max((availableWidth - totWidth) / 2, 0);
678
x += totWidth > availableWidth ? (availableWidth - totWidth) : (availableWidth - totWidth) / 2 - trailingSpaceWidth;
683
for (BidiRun* r = firstRun; r; r = r->next()) {
684
if (!r->m_box || r == trailingSpaceRun)
688
if (r->m_object->isText() && !r->m_compact) {
690
const UChar* characters = static_cast<RenderText*>(r->m_object)->characters();
691
for (int i = r->m_start; i < r->m_stop; i++) {
692
UChar c = characters[i];
693
if (c == ' ' || c == '\n' || c == '\t')
697
ASSERT(spaces <= numSpaces);
699
// Only justify text if whitespace is collapsed.
700
if (r->m_object->style()->collapseWhiteSpace()) {
701
spaceAdd = (availableWidth - totWidth) * spaces / numSpaces;
702
static_cast<InlineTextBox*>(r->m_box)->setSpaceAdd(spaceAdd);
703
totWidth += spaceAdd;
712
// The widths of all runs are now known. We can now place every inline box (and
713
// compute accurate widths for the inline flow boxes).
714
int leftPosition = x;
715
int rightPosition = x;
716
needsWordSpacing = false;
717
lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing);
718
lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition);
721
void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun)
723
lineBox->verticallyAlignBoxes(m_height);
724
lineBox->setBlockHeight(m_height);
726
// See if the line spilled out. If so set overflow height accordingly.
727
int bottomOfLine = lineBox->bottomOverflow();
728
if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight)
729
m_overflowHeight = bottomOfLine;
731
// Now make sure we place replaced render objects correctly.
732
for (BidiRun* r = firstRun; r; r = r->next()) {
734
continue; // Skip runs with no line boxes.
736
// Align positioned boxes with the top of the line box. This is
737
// a reasonable approximation of an appropriate y position.
738
if (r->m_object->isPositioned())
739
r->m_box->setYPos(m_height);
741
// Position is used to properly position both replaced elements and
742
// to update the static normal flow x/y of positioned elements.
743
r->m_object->position(r->m_box);
745
// Positioned objects and zero-length text nodes destroy their boxes in
746
// position(), which unnecessarily dirties the line.
747
lineBox->markDirty(false);
750
// collects one line of the paragraph and transforms it to visual order
751
void RenderBlock::bidiReorderLine(InlineBidiResolver& resolver, const InlineIterator& end)
753
resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly);
756
static void buildCompactRuns(RenderObject* compactObj, InlineBidiResolver& resolver)
758
ASSERT(compactObj->isRenderBlock());
759
ASSERT(!resolver.firstRun());
761
// Format the compact like it is its own single line. We build up all the runs for
762
// the little compact and then reorder them for bidi.
763
RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj);
765
InlineIterator start(compactBlock, bidiFirst(compactBlock, &resolver), 0);
766
resolver.setPosition(start);
768
betweenMidpoints = false;
770
previousLineBrokeCleanly = true;
772
InlineIterator end = compactBlock->findNextLineBreak(resolver);
774
compactBlock->bidiReorderLine(resolver, end);
776
for (BidiRun* run = resolver.firstRun(); run; run = run->next())
777
run->m_compact = true;
781
betweenMidpoints = false;
784
static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
786
if (character == ' ' || character == '\t' || character == softHyphen)
788
if (character == '\n')
789
return !renderer->style()->preserveNewline();
790
if (character == noBreakSpace)
791
return renderer->style()->nbspMode() == SPACE;
795
void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom)
797
bool useRepaintBounds = false;
799
invalidateVerticalPosition();
801
m_overflowHeight = 0;
803
m_height = borderTop() + paddingTop();
804
int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
806
// Figure out if we should clear out our line boxes.
807
// FIXME: Handle resize eventually!
808
// FIXME: Do something better when floats are present.
809
bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren;
813
// Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
815
// FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
816
// difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
817
// anyway, so we won't worry about following the draft here.
818
bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
820
// Walk all the lines and delete our ellipsis line boxes if they exist.
822
deleteEllipsisLineBoxes();
825
// layout replaced elements
826
bool endOfInline = false;
827
RenderObject* o = bidiFirst(this, 0, false);
828
Vector<FloatWithRect> floats;
829
int containerWidth = max(0, containingBlockWidth());
831
o->invalidateVerticalPosition();
832
if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
833
if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
834
o->setChildNeedsLayout(true, false);
836
// If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths.
837
if (relayoutChildren && (o->style()->paddingLeft().isPercent() || o->style()->paddingRight().isPercent()))
838
o->setPrefWidthsDirty(true, false);
840
if (o->isPositioned())
841
o->containingBlock()->insertPositionedObject(o);
844
floats.append(FloatWithRect(o));
845
else if (fullLayout || o->needsLayout()) // Replaced elements
846
o->dirtyLineBoxes(fullLayout);
850
} else if (o->isText() || (o->isInlineFlow() && !endOfInline)) {
851
if (fullLayout || o->selfNeedsLayout())
852
o->dirtyLineBoxes(fullLayout);
854
// Calculate margins of inline flows so that they can be used later by line layout.
855
if (o->isInlineFlow())
856
static_cast<RenderFlow*>(o)->calcMargins(containerWidth);
857
o->setNeedsLayout(false);
859
o = bidiNext(this, o, 0, false, &endOfInline);
862
// We want to skip ahead to the first dirty line
863
InlineBidiResolver resolver;
865
RootInlineBox* startLine = determineStartPosition(fullLayout, resolver, floats, floatIndex);
867
if (fullLayout && !selfNeedsLayout()) {
868
setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like
869
// we're supposed to.
870
if (!document()->view()->needsFullRepaint() && m_layer) {
871
// Because we waited until we were already inside layout to discover
872
// that the block really needed a full layout, we missed our chance to repaint the layer
873
// before layout started. Luckily the layer has cached the repaint rect for its original
874
// position and size, and so we can use that to make a repaint happen now.
875
RenderView* c = view();
876
if (c && !c->printing())
877
c->repaintViewRectangle(m_layer->repaintRect());
881
FloatingObject* lastFloat = m_floatingObjects ? m_floatingObjects->last() : 0;
884
smidpoints = new Vector<InlineIterator>();
889
// We also find the first clean line and extract these lines. We will add them back
890
// if we determine that we're able to synchronize after handling all our dirty lines.
891
InlineIterator cleanLineStart;
892
BidiStatus cleanLineBidiStatus;
894
RootInlineBox* endLine = (fullLayout || !startLine) ?
895
0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineYPos);
898
useRepaintBounds = true;
899
repaintTop = m_height;
900
repaintBottom = m_height;
901
RenderArena* arena = renderArena();
902
RootInlineBox* box = startLine;
904
repaintTop = min(repaintTop, box->topOverflow());
905
repaintBottom = max(repaintBottom, box->bottomOverflow());
906
RootInlineBox* next = box->nextRootBox();
907
box->deleteLine(arena);
912
InlineIterator end = resolver.position();
914
if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) {
915
// If the last line before the start line ends with a line break that clear floats,
916
// adjust the height accordingly.
917
// A line break can be either the first or the last object on a line, depending on its direction.
918
if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
919
RenderObject* lastObject = lastLeafChild->object();
920
if (!lastObject->isBR())
921
lastObject = lastRootBox()->firstLeafChild()->object();
922
if (lastObject->isBR()) {
923
EClear clear = lastObject->style()->clear();
930
bool endLineMatched = false;
931
bool checkForEndLineMatch = endLine;
932
bool checkForFloatsFromLastLine = false;
933
int lastHeight = m_height;
935
while (!end.atEnd()) {
936
// FIXME: Is this check necessary before the first iteration or can it be moved to the end?
937
if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineYPos, repaintBottom, repaintTop)))
940
betweenMidpoints = false;
942
if (m_firstLine && firstChild()->isCompact() && firstChild()->isRenderBlock()) {
943
buildCompactRuns(firstChild(), resolver);
944
resolver.setPosition(InlineIterator(this, firstChild()->nextSibling(), 0));
946
EClear clear = CNONE;
947
end = findNextLineBreak(resolver, &clear);
948
if (resolver.position().atEnd()) {
949
resolver.deleteRuns();
950
checkForFloatsFromLastLine = true;
953
ASSERT(end != resolver.position());
956
bidiReorderLine(resolver, end);
957
ASSERT(resolver.position() == end);
959
BidiRun* trailingSpaceRun = 0;
960
if (!previousLineBrokeCleanly && resolver.runCount() && resolver.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()) {
961
trailingSpaceRun = resolver.logicallyLastRun();
962
RenderObject* lastObject = trailingSpaceRun->m_object;
963
if (lastObject->isText()) {
964
RenderText* lastText = static_cast<RenderText*>(lastObject);
965
const UChar* characters = lastText->characters();
966
int firstSpace = trailingSpaceRun->stop();
967
while (firstSpace > trailingSpaceRun->start()) {
968
UChar current = characters[firstSpace - 1];
969
if (!isCollapsibleSpace(current, lastText))
973
if (firstSpace == trailingSpaceRun->stop())
974
trailingSpaceRun = 0;
976
TextDirection direction = style()->direction();
977
bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun());
978
if (firstSpace != trailingSpaceRun->start()) {
979
ETextAlign textAlign = style()->textAlign();
980
// If the trailing white space is at the right hand side of a left-aligned line, then computeHorizontalPositionsForLine()
981
// does not care if trailingSpaceRun includes non-spaces at the beginning. In all other cases, trailingSpaceRun has to
982
// contain only the spaces, either because we re-order them or because computeHorizontalPositionsForLine() needs to know
984
bool shouldSeparateSpaces = textAlign != LEFT && textAlign != WEBKIT_LEFT && textAlign != TAAUTO || trailingSpaceRun->m_level % 2 || direction == RTL || shouldReorder;
985
if (shouldSeparateSpaces) {
986
BidiContext* baseContext = resolver.context();
987
while (BidiContext* parent = baseContext->parent())
988
baseContext = parent;
990
BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
991
trailingSpaceRun->m_stop = firstSpace;
992
if (direction == LTR)
993
resolver.addRun(newTrailingRun);
995
resolver.prependRun(newTrailingRun);
996
trailingSpaceRun = newTrailingRun;
997
shouldReorder = false;
1000
if (shouldReorder) {
1001
if (direction == LTR) {
1002
resolver.moveRunToEnd(trailingSpaceRun);
1003
trailingSpaceRun->m_level = 0;
1005
resolver.moveRunToBeginning(trailingSpaceRun);
1006
trailingSpaceRun->m_level = 1;
1011
trailingSpaceRun = 0;
1014
// Now that the runs have been ordered, we create the line boxes.
1015
// At the same time we figure out where border/padding/margin should be applied for
1016
// inline flow boxes.
1018
RootInlineBox* lineBox = 0;
1019
if (resolver.runCount()) {
1020
lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), !end.obj, end.obj && !end.pos ? end.obj : 0);
1022
lineBox->setEndsWithBreak(previousLineBrokeCleanly);
1024
// Now we position all of our text runs horizontally.
1025
computeHorizontalPositionsForLine(lineBox, resolver.firstRun(), trailingSpaceRun, end.atEnd());
1027
// Now position our text runs vertically.
1028
computeVerticalPositionsForLine(lineBox, resolver.firstRun());
1031
// Special SVG text layout code
1032
lineBox->computePerCharacterLayoutInformation();
1036
// Highlight acts as an overflow inflation.
1037
if (style()->highlight() != nullAtom)
1038
lineBox->addHighlightOverflow();
1043
resolver.deleteRuns();
1046
lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status());
1047
if (useRepaintBounds) {
1048
repaintTop = min(repaintTop, lineBox->topOverflow());
1049
repaintBottom = max(repaintBottom, lineBox->bottomOverflow());
1053
m_firstLine = false;
1057
if (m_floatingObjects && lastRootBox()) {
1059
for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1061
m_floatingObjects->next();
1063
m_floatingObjects->first();
1064
for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1065
if (f->m_bottom > lastHeight)
1066
lastRootBox()->floats().append(f->m_renderer);
1067
ASSERT(f->m_renderer == floats[floatIndex].object);
1068
// If a float's geometry has changed, give up on syncing with clean lines.
1069
if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top))
1070
checkForEndLineMatch = false;
1073
lastFloat = m_floatingObjects->last();
1076
lastHeight = m_height;
1079
resolver.setPosition(end);
1083
if (endLineMatched) {
1084
// Attach all the remaining lines, and then adjust their y-positions as needed.
1085
int delta = m_height - endLineYPos;
1086
for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) {
1089
repaintTop = min(repaintTop, line->topOverflow() + min(delta, 0));
1090
repaintBottom = max(repaintBottom, line->bottomOverflow() + max(delta, 0));
1091
line->adjustPosition(0, delta);
1093
if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) {
1094
Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1095
for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1096
int floatTop = (*f)->yPos() - (*f)->marginTop();
1097
insertFloatingObject(*f);
1098
m_height = floatTop + delta;
1099
positionNewFloats();
1103
m_height = lastRootBox()->blockHeight();
1105
// Delete all the remaining lines.
1106
InlineRunBox* line = endLine;
1107
RenderArena* arena = renderArena();
1109
repaintTop = min(repaintTop, line->topOverflow());
1110
repaintBottom = max(repaintBottom, line->bottomOverflow());
1111
InlineRunBox* next = line->nextLineBox();
1112
line->deleteLine(arena);
1117
if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) {
1118
// In case we have a float on the last line, it might not be positioned up to now.
1119
// This has to be done before adding in the bottom border/padding, or the float will
1120
// include the padding incorrectly. -dwh
1122
for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1124
m_floatingObjects->next();
1126
m_floatingObjects->first();
1127
for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1128
if (f->m_bottom > lastHeight)
1129
lastRootBox()->floats().append(f->m_renderer);
1131
lastFloat = m_floatingObjects->last();
1138
// Now add in the bottom border/padding.
1141
// Always make sure this is at least our height.
1142
m_overflowHeight = max(m_height, m_overflowHeight);
1144
// See if any lines spill out of the block. If so, we need to update our overflow width.
1145
checkLinesForOverflow();
1147
if (!firstLineBox() && hasLineIfEmpty())
1148
m_height += lineHeight(true, true);
1150
// See if we have any lines that spill out of our block. If we do, then we will possibly need to
1152
if (hasTextOverflow)
1153
checkLinesForTextOverflow();
1156
RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats)
1158
RootInlineBox* curr = 0;
1159
RootInlineBox* last = 0;
1161
bool dirtiedByFloat = false;
1163
size_t floatIndex = 0;
1164
for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1165
if (Vector<RenderObject*>* cleanLineFloats = curr->floatsPtr()) {
1166
Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1167
for (Vector<RenderObject*>::iterator o = cleanLineFloats->begin(); o != end; ++o) {
1168
RenderObject* f = *o;
1169
IntSize newSize(f->width() + f->marginLeft() +f->marginRight(), f->height() + f->marginTop() + f->marginBottom());
1170
ASSERT(floatIndex < floats.size());
1171
if (floats[floatIndex].object != f) {
1172
// A new float has been inserted before this line or before its last known float.
1173
// Just do a full layout.
1177
if (floats[floatIndex].rect.size() != newSize) {
1178
int floatTop = floats[floatIndex].rect.y();
1180
markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()));
1181
floats[floatIndex].rect.setSize(newSize);
1182
dirtiedByFloat = true;
1187
if (dirtiedByFloat || fullLayout)
1190
// Check if a new float has been inserted after the last known float.
1191
if (!curr && floatIndex < floats.size())
1196
// Nuke all our lines.
1197
if (firstRootBox()) {
1198
RenderArena* arena = renderArena();
1199
curr = firstRootBox();
1201
RootInlineBox* next = curr->nextRootBox();
1202
curr->deleteLine(arena);
1205
ASSERT(!firstLineBox() && !lastLineBox());
1209
// We have a dirty line.
1210
if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1211
// We have a previous line.
1212
if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= static_cast<RenderText*>(prevRootBox->lineBreakObj())->textLength()))
1213
// The previous line didn't break cleanly or broke at a newline
1214
// that has been deleted, so treat it as dirty too.
1218
// No dirty lines were found.
1219
// If the last line didn't break cleanly, treat it as dirty.
1220
if (lastRootBox() && !lastRootBox()->endsWithBreak())
1221
curr = lastRootBox();
1224
// If we have no dirty lines, then last is just the last root box.
1225
last = curr ? curr->prevRootBox() : lastRootBox();
1229
if (!floats.isEmpty()) {
1230
int savedHeight = m_height;
1231
// Restore floats from clean lines.
1232
RootInlineBox* line = firstRootBox();
1233
while (line != curr) {
1234
if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) {
1235
Vector<RenderObject*>::iterator end = cleanLineFloats->end();
1236
for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1237
insertFloatingObject(*f);
1238
m_height = (*f)->yPos() - (*f)->marginTop();
1239
positionNewFloats();
1240
ASSERT(floats[numCleanFloats].object == *f);
1244
line = line->nextRootBox();
1246
m_height = savedHeight;
1249
m_firstLine = !last;
1250
previousLineBrokeCleanly = !last || last->endsWithBreak();
1252
RenderObject* startObj;
1255
m_height = last->blockHeight();
1256
startObj = last->lineBreakObj();
1257
pos = last->lineBreakPos();
1258
resolver.setStatus(last->lineBreakBidiStatus());
1260
bool ltr = style()->direction() == LTR
1262
|| (style()->unicodeBidi() == UBNormal && isSVGText())
1266
BidiContext* context = new BidiContext(ltr ? 0 : 1, ltr ? LeftToRight : RightToLeft, style()->unicodeBidi() == Override);
1268
resolver.setLastStrongDir(context->dir());
1269
resolver.setLastDir(context->dir());
1270
resolver.setEorDir(context->dir());
1271
resolver.setContext(context);
1272
startObj = bidiFirst(this, &resolver);
1275
resolver.setPosition(InlineIterator(this, startObj, pos));
1280
RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos)
1282
RootInlineBox* last = 0;
1286
for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1287
if (curr->isDirty())
1297
RootInlineBox* prev = last->prevRootBox();
1298
cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1299
cleanLineBidiStatus = prev->lineBreakBidiStatus();
1300
yPos = prev->blockHeight();
1302
for (RootInlineBox* line = last; line; line = line->nextRootBox())
1303
line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1304
// their connections to one another.
1309
bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop)
1311
if (resolver.position() == endLineStart) {
1312
if (resolver.status() != endLineStatus)
1315
int delta = m_height - endYPos;
1316
if (!delta || !m_floatingObjects)
1319
// See if any floats end in the range along which we want to shift the lines vertically.
1320
int top = min(m_height, endYPos);
1322
RootInlineBox* lastLine = endLine;
1323
while (RootInlineBox* nextLine = lastLine->nextRootBox())
1324
lastLine = nextLine;
1326
int bottom = lastLine->blockHeight() + abs(delta);
1328
for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1329
if (f->m_bottom >= top && f->m_bottom < bottom)
1336
// The first clean line doesn't match, but we can check a handful of following lines to try
1337
// to match back up.
1338
static int numLines = 8; // The # of lines we're willing to match against.
1339
RootInlineBox* line = endLine;
1340
for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1341
if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) {
1343
if (line->lineBreakBidiStatus() != resolver.status())
1344
return false; // ...but the bidi state doesn't match.
1345
RootInlineBox* result = line->nextRootBox();
1347
// Set our yPos to be the block height of endLine.
1349
endYPos = line->blockHeight();
1351
int delta = m_height - endYPos;
1352
if (delta && m_floatingObjects) {
1353
// See if any floats end in the range along which we want to shift the lines vertically.
1354
int top = min(m_height, endYPos);
1356
RootInlineBox* lastLine = endLine;
1357
while (RootInlineBox* nextLine = lastLine->nextRootBox())
1358
lastLine = nextLine;
1360
int bottom = lastLine->blockHeight() + abs(delta);
1362
for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1363
if (f->m_bottom >= top && f->m_bottom < bottom)
1368
// Now delete the lines that we failed to sync.
1369
RootInlineBox* boxToDelete = endLine;
1370
RenderArena* arena = renderArena();
1371
while (boxToDelete && boxToDelete != result) {
1372
repaintTop = min(repaintTop, boxToDelete->topOverflow());
1373
repaintBottom = max(repaintBottom, boxToDelete->bottomOverflow());
1374
RootInlineBox* next = boxToDelete->nextRootBox();
1375
boxToDelete->deleteLine(arena);
1387
static inline bool skipNonBreakingSpace(const InlineIterator& it)
1389
if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace)
1392
// FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly
1393
// with m_minWidth/m_maxWidth.
1394
// Do not skip a non-breaking space if it is the first character
1395
// on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
1397
if (isLineEmpty && previousLineBrokeCleanly)
1403
static inline bool shouldCollapseWhiteSpace(const RenderStyle* style)
1405
return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly));
1408
static inline bool shouldPreserveNewline(RenderObject* object)
1411
if (object->isSVGText())
1415
return object->style()->preserveNewline();
1418
static bool inlineFlowRequiresLineBox(RenderObject* flow)
1420
// FIXME: Right now, we only allow line boxes for inlines that are truly empty.
1421
// We need to fix this, though, because at the very least, inlines containing only
1422
// ignorable whitespace should should also have line boxes.
1423
return flow->isInlineFlow() && !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin();
1426
static inline bool requiresLineBox(const InlineIterator& it)
1428
if (it.obj->isFloatingOrPositioned())
1431
if (it.obj->isInlineFlow() && !inlineFlowRequiresLineBox(it.obj))
1434
if (!shouldCollapseWhiteSpace(it.obj->style()) || it.obj->isBR())
1437
UChar current = it.current();
1438
return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) && !skipNonBreakingSpace(it);
1441
bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
1443
ASSERT(inlineObj->parent() == this);
1445
InlineIterator it(this, inlineObj, 0);
1446
while (!it.atEnd() && !requiresLineBox(it))
1452
// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
1453
// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
1454
// elements quite right. In other words, we need to build this function's work into the normal line
1455
// object iteration process.
1456
// NB. this function will insert any floating elements that would otherwise
1457
// be skipped but it will not position them.
1458
void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator)
1460
while (!iterator.atEnd() && !requiresLineBox(iterator)) {
1461
RenderObject* object = iterator.obj;
1462
if (object->isFloating()) {
1463
insertFloatingObject(object);
1464
} else if (object->isPositioned()) {
1465
// FIXME: The math here is actually not really right. It's a best-guess approximation that
1466
// will work for the common cases
1467
RenderObject* c = object->container();
1468
if (c->isInlineFlow()) {
1469
// A relative positioned inline encloses us. In this case, we also have to determine our
1470
// position as though we were an inline. Set |staticX| and |staticY| on the relative positioned
1471
// inline so that we can obtain the value later.
1472
c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height));
1473
c->setStaticY(m_height);
1476
if (object->hasStaticX()) {
1477
if (object->style()->isOriginalDisplayInlineType())
1478
object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height));
1480
object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1483
if (object->hasStaticY())
1484
object->setStaticY(m_height);
1486
iterator.increment();
1490
int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver)
1492
int availableWidth = lineWidth(m_height);
1493
while (!resolver.position().atEnd() && !requiresLineBox(resolver.position())) {
1494
RenderObject* object = resolver.position().obj;
1495
if (object->isFloating()) {
1496
insertFloatingObject(object);
1497
positionNewFloats();
1498
availableWidth = lineWidth(m_height);
1499
} else if (object->isPositioned()) {
1500
// FIXME: The math here is actually not really right. It's a best-guess approximation that
1501
// will work for the common cases
1502
RenderObject* c = object->container();
1503
if (c->isInlineFlow()) {
1504
// A relative positioned inline encloses us. In this case, we also have to determine our
1505
// position as though we were an inline. Set |staticX| and |staticY| on the relative positioned
1506
// inline so that we can obtain the value later.
1507
c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height));
1508
c->setStaticY(m_height);
1511
if (object->hasStaticX()) {
1512
if (object->style()->isOriginalDisplayInlineType())
1513
object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height));
1515
object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1518
if (object->hasStaticY())
1519
object->setStaticY(m_height);
1521
resolver.increment();
1523
resolver.commitExplicitEmbedding();
1524
return availableWidth;
1527
// This is currently just used for list markers and inline flows that have line boxes. Neither should
1528
// have an effect on whitespace at the start of the line.
1529
static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o)
1531
RenderObject* next = bidiNext(block, o);
1532
if (next && !next->isBR() && next->isText() && static_cast<RenderText*>(next)->textLength() > 0) {
1533
RenderText* nextText = static_cast<RenderText*>(next);
1534
UChar nextChar = nextText->characters()[0];
1535
if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
1536
addMidpoint(InlineIterator(0, o, 0));
1544
void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth)
1546
ASSERT(widthToFit > availableWidth);
1549
int lastFloatBottom = m_height;
1550
int newLineWidth = availableWidth;
1552
floatBottom = nextFloatBottomBelow(lastFloatBottom);
1556
newLineWidth = lineWidth(floatBottom);
1557
lastFloatBottom = floatBottom;
1558
if (newLineWidth >= widthToFit)
1562
if (newLineWidth > availableWidth) {
1563
m_height = lastFloatBottom;
1564
availableWidth = newLineWidth;
1568
InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, EClear* clear)
1570
ASSERT(resolver.position().block == this);
1572
bool appliedStartWidth = resolver.position().pos > 0;
1574
int width = skipLeadingWhitespace(resolver);
1579
if (resolver.position().atEnd())
1580
return resolver.position();
1582
// This variable is used only if whitespace isn't set to PRE, and it tells us whether
1583
// or not we are currently ignoring whitespace.
1584
bool ignoringSpaces = false;
1585
InlineIterator ignoreStart;
1587
// This variable tracks whether the very last character we saw was a space. We use
1588
// this to detect when we encounter a second space so we know we have to terminate
1590
bool currentCharacterIsSpace = false;
1591
bool currentCharacterIsWS = false;
1592
RenderObject* trailingSpaceObject = 0;
1594
InlineIterator lBreak = resolver.position();
1596
RenderObject *o = resolver.position().obj;
1597
RenderObject *last = o;
1598
unsigned pos = resolver.position().pos;
1599
int nextBreakable = resolver.position().nextBreakablePosition;
1600
bool atStart = true;
1602
bool prevLineBrokeCleanly = previousLineBrokeCleanly;
1603
previousLineBrokeCleanly = false;
1605
bool autoWrapWasEverTrueOnLine = false;
1606
bool floatsFitOnLine = true;
1608
// Firefox and Opera will allow a table cell to grow to fit an image inside it under
1609
// very specific circumstances (in order to match common WinIE renderings).
1610
// Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
1611
bool allowImagesToBreak = !style()->htmlHacks() || !isTableCell() || !style()->width().isIntrinsicOrAuto();
1613
EWhiteSpace currWS = style()->whiteSpace();
1614
EWhiteSpace lastWS = currWS;
1616
currWS = o->isReplaced() ? o->parent()->style()->whiteSpace() : o->style()->whiteSpace();
1617
lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace();
1619
bool autoWrap = RenderStyle::autoWrap(currWS);
1620
autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;
1623
bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS);
1625
bool preserveNewline = RenderStyle::preserveNewline(currWS);
1628
bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS);
1631
if (w + tmpW <= width) {
1634
lBreak.nextBreakablePosition = -1;
1637
// A <br> always breaks a line, so don't let the line be collapsed
1638
// away. Also, the space at the end of a line with a <br> does not
1639
// get collapsed away. It only does this if the previous line broke
1640
// cleanly. Otherwise the <br> has no effect on whether the line is
1642
if (prevLineBrokeCleanly)
1643
isLineEmpty = false;
1644
trailingSpaceObject = 0;
1645
previousLineBrokeCleanly = true;
1647
if (!isLineEmpty && clear)
1648
*clear = o->style()->clear();
1653
if (o->isFloatingOrPositioned()) {
1654
// add to special objects...
1655
if (o->isFloating()) {
1656
insertFloatingObject(o);
1657
// check if it fits in the current line.
1658
// If it does, position it now, otherwise, position
1659
// it after moving to next line (in newLine() func)
1660
if (floatsFitOnLine && o->width() + o->marginLeft() + o->marginRight() + w + tmpW <= width) {
1661
positionNewFloats();
1662
width = lineWidth(m_height);
1664
floatsFitOnLine = false;
1665
} else if (o->isPositioned()) {
1666
// If our original display wasn't an inline type, then we can
1667
// go ahead and determine our static x position now.
1668
bool isInlineType = o->style()->isOriginalDisplayInlineType();
1669
bool needToSetStaticX = o->hasStaticX();
1670
if (o->hasStaticX() && !isInlineType) {
1671
o->setStaticX(o->parent()->style()->direction() == LTR ?
1672
borderLeft() + paddingLeft() :
1673
borderRight() + paddingRight());
1674
needToSetStaticX = false;
1677
// If our original display was an INLINE type, then we can go ahead
1678
// and determine our static y position now.
1679
bool needToSetStaticY = o->hasStaticY();
1680
if (o->hasStaticY() && isInlineType) {
1681
o->setStaticY(m_height);
1682
needToSetStaticY = false;
1685
bool needToCreateLineBox = needToSetStaticX || needToSetStaticY;
1686
RenderObject* c = o->container();
1687
if (c->isInlineFlow() && (!needToSetStaticX || !needToSetStaticY))
1688
needToCreateLineBox = true;
1690
// If we're ignoring spaces, we have to stop and include this object and
1691
// then start ignoring spaces again.
1692
if (needToCreateLineBox) {
1693
trailingSpaceObject = 0;
1694
ignoreStart.obj = o;
1695
ignoreStart.pos = 0;
1696
if (ignoringSpaces) {
1697
addMidpoint(ignoreStart); // Stop ignoring spaces.
1698
addMidpoint(ignoreStart); // Start ignoring again.
1703
} else if (o->isInlineFlow()) {
1704
// Right now, we should only encounter empty inlines here.
1705
ASSERT(!o->firstChild());
1707
// Now that some inline flows have line boxes, if we are already ignoring spaces, we need
1708
// to make sure that we stop to include this object and then start ignoring spaces again.
1709
// If this object is at the start of the line, we need to behave like list markers and
1710
// start ignoring spaces.
1711
if (inlineFlowRequiresLineBox(o)) {
1712
isLineEmpty = false;
1713
if (ignoringSpaces) {
1714
trailingSpaceObject = 0;
1715
addMidpoint(InlineIterator(0, o, 0)); // Stop ignoring spaces.
1716
addMidpoint(InlineIterator(0, o, 0)); // Start ignoring again.
1717
} else if (style()->collapseWhiteSpace() && resolver.position().obj == o
1718
&& shouldSkipWhitespaceAfterStartObject(this, o)) {
1719
// Like with list markers, we start ignoring spaces to make sure that any
1720
// additional spaces we see will be discarded.
1721
currentCharacterIsSpace = true;
1722
currentCharacterIsWS = true;
1723
ignoringSpaces = true;
1727
tmpW += o->marginLeft() + o->borderLeft() + o->paddingLeft() +
1728
o->marginRight() + o->borderRight() + o->paddingRight();
1729
} else if (o->isReplaced()) {
1730
// Break on replaced elements if either has normal white-space.
1731
if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) {
1736
lBreak.nextBreakablePosition = -1;
1740
addMidpoint(InlineIterator(0, o, 0));
1742
isLineEmpty = false;
1743
ignoringSpaces = false;
1744
currentCharacterIsSpace = false;
1745
currentCharacterIsWS = false;
1746
trailingSpaceObject = 0;
1748
// Optimize for a common case. If we can't find whitespace after the list
1749
// item, then this is all moot. -dwh
1750
if (o->isListMarker() && !static_cast<RenderListMarker*>(o)->isInside()) {
1751
if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o)) {
1752
// Like with inline flows, we start ignoring spaces to make sure that any
1753
// additional spaces we see will be discarded.
1754
currentCharacterIsSpace = true;
1755
currentCharacterIsWS = true;
1756
ignoringSpaces = true;
1759
tmpW += o->width() + o->marginLeft() + o->marginRight() + inlineWidth(o);
1760
} else if (o->isText()) {
1762
appliedStartWidth = false;
1764
RenderText* t = static_cast<RenderText*>(o);
1766
int strlen = t->textLength();
1767
int len = strlen - pos;
1768
const UChar* str = t->characters();
1770
const Font& f = t->style(m_firstLine)->font();
1772
int lastSpace = pos;
1773
int wordSpacing = o->style()->wordSpacing();
1774
int lastSpaceWordSpacing = 0;
1776
int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true);
1778
bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE;
1779
// Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
1780
// which is only possible if the word is the first thing on the line, that is, if |w| is zero.
1781
bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE);
1782
bool midWordBreak = false;
1783
bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap;
1785
if (t->isWordBreak()) {
1790
lBreak.nextBreakablePosition = -1;
1795
bool previousCharacterIsSpace = currentCharacterIsSpace;
1796
bool previousCharacterIsWS = currentCharacterIsWS;
1798
currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n'));
1800
if (!collapseWhiteSpace || !currentCharacterIsSpace)
1801
isLineEmpty = false;
1803
// Check for soft hyphens. Go ahead and ignore them.
1804
if (c == softHyphen) {
1805
if (!ignoringSpaces) {
1806
// Ignore soft hyphens
1807
InlineIterator beforeSoftHyphen;
1809
beforeSoftHyphen = InlineIterator(0, o, pos - 1);
1811
beforeSoftHyphen = InlineIterator(0, last, last->isText() ? static_cast<RenderText*>(last)->textLength() - 1 : 0);
1812
// Two consecutive soft hyphens. Avoid overlapping midpoints.
1813
if (sNumMidpoints && smidpoints->at(sNumMidpoints - 1).obj == o && smidpoints->at(sNumMidpoints - 1).pos == pos)
1816
addMidpoint(beforeSoftHyphen);
1818
// Add the width up to but not including the hyphen.
1819
tmpW += t->width(lastSpace, pos - lastSpace, f, w + tmpW) + lastSpaceWordSpacing;
1821
// For wrapping text only, include the hyphen. We need to ensure it will fit
1822
// on the line if it shows when we break.
1824
tmpW += t->width(pos, 1, f, w + tmpW);
1826
InlineIterator afterSoftHyphen(0, o, pos);
1827
afterSoftHyphen.increment();
1828
addMidpoint(afterSoftHyphen);
1833
lastSpaceWordSpacing = 0;
1834
lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
1838
bool applyWordSpacing = false;
1840
currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
1842
if ((breakAll || breakWords) && !midWordBreak) {
1844
charWidth = t->width(pos, 1, f, w + wrapW);
1845
midWordBreak = w + wrapW + charWidth > width;
1848
bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP));
1850
if (betweenWords || midWordBreak) {
1851
bool stoppedIgnoringSpaces = false;
1852
if (ignoringSpaces) {
1853
if (!currentCharacterIsSpace) {
1854
// Stop ignoring spaces and begin at this
1856
ignoringSpaces = false;
1857
lastSpaceWordSpacing = 0;
1858
lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
1859
addMidpoint(InlineIterator(0, o, pos));
1860
stoppedIgnoringSpaces = true;
1862
// Just keep ignoring these spaces.
1869
int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
1870
tmpW += additionalTmpW;
1871
if (!appliedStartWidth) {
1872
tmpW += inlineWidth(o, true, false);
1873
appliedStartWidth = true;
1876
applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace;
1878
if (!w && autoWrap && tmpW > width)
1879
fitBelowFloats(tmpW, width);
1881
if (autoWrap || breakWords) {
1882
// If we break only after white-space, consider the current character
1883
// as candidate width for this line.
1884
bool lineWasTooWide = false;
1885
if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) {
1886
int charWidth = t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0);
1887
// Check if line is too big even without the extra space
1888
// at the end of the line. If it is not, do nothing.
1889
// If the line needs the extra whitespace to be too long,
1890
// then move the line break to the space and skip all
1891
// additional whitespace.
1892
if (w + tmpW + charWidth > width) {
1893
lineWasTooWide = true;
1896
lBreak.nextBreakablePosition = nextBreakable;
1897
skipTrailingWhitespace(lBreak);
1900
if (lineWasTooWide || w + tmpW > width) {
1901
if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !static_cast<RenderText*>(lBreak.obj)->isWordBreak() && static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos] == '\n') {
1902
if (!stoppedIgnoringSpaces && pos > 0) {
1903
// We need to stop right before the newline and then start up again.
1904
addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1905
addMidpoint(InlineIterator(0, o, pos)); // Start
1908
previousLineBrokeCleanly = true;
1910
goto end; // Didn't fit. Jump to the end.
1912
if (!betweenWords || (midWordBreak && !autoWrap))
1913
tmpW -= additionalTmpW;
1914
if (pos > 0 && str[pos-1] == softHyphen)
1915
// Subtract the width of the soft hyphen out since we fit on a line.
1916
tmpW -= t->width(pos-1, 1, f, w+tmpW);
1920
if (c == '\n' && preserveNewline) {
1921
if (!stoppedIgnoringSpaces && pos > 0) {
1922
// We need to stop right before the newline and then start up again.
1923
addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1924
addMidpoint(InlineIterator(0, o, pos)); // Start
1928
lBreak.nextBreakablePosition = nextBreakable;
1930
previousLineBrokeCleanly = true;
1934
if (autoWrap && betweenWords) {
1940
lBreak.nextBreakablePosition = nextBreakable;
1941
// Auto-wrapping text should not wrap in the middle of a word once it has had an
1942
// opportunity to break after a word.
1947
// Remember this as a breakable position in case
1948
// adding the end width forces a break.
1951
lBreak.nextBreakablePosition = nextBreakable;
1952
midWordBreak &= (breakWords || breakAll);
1956
lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
1960
if (!ignoringSpaces && o->style()->collapseWhiteSpace()) {
1961
// If we encounter a newline, or if we encounter a
1962
// second space, we need to go ahead and break up this
1963
// run and enter a mode where we start collapsing spaces.
1964
if (currentCharacterIsSpace && previousCharacterIsSpace) {
1965
ignoringSpaces = true;
1967
// We just entered a mode where we are ignoring
1968
// spaces. Create a midpoint to terminate the run
1969
// before the second space.
1970
addMidpoint(ignoreStart);
1973
} else if (ignoringSpaces) {
1974
// Stop ignoring spaces and begin at this
1976
ignoringSpaces = false;
1977
lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
1978
lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
1979
addMidpoint(InlineIterator(0, o, pos));
1982
if (currentCharacterIsSpace && !previousCharacterIsSpace) {
1983
ignoreStart.obj = o;
1984
ignoreStart.pos = pos;
1987
if (!currentCharacterIsWS && previousCharacterIsWS) {
1988
if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) {
1991
lBreak.nextBreakablePosition = nextBreakable;
1995
if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces)
1996
trailingSpaceObject = o;
1997
else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace)
1998
trailingSpaceObject = 0;
2005
// IMPORTANT: pos is > length here!
2006
if (!ignoringSpaces)
2007
tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
2008
tmpW += inlineWidth(o, !appliedStartWidth, true);
2010
ASSERT_NOT_REACHED();
2012
RenderObject* next = bidiNext(this, o);
2013
bool checkForBreak = autoWrap;
2014
if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP)
2015
checkForBreak = true;
2016
else if (next && o->isText() && next->isText() && !next->isBR()) {
2017
if (autoWrap || (next->style()->autoWrap())) {
2018
if (currentCharacterIsSpace)
2019
checkForBreak = true;
2021
checkForBreak = false;
2022
RenderText* nextText = static_cast<RenderText*>(next);
2023
if (nextText->textLength()) {
2024
UChar c = nextText->characters()[0];
2025
if (c == ' ' || c == '\t' || (c == '\n' && !shouldPreserveNewline(next)))
2026
// If the next item on the line is text, and if we did not end with
2027
// a space, then the next text run continues our word (and so it needs to
2028
// keep adding to |tmpW|. Just update and continue.
2029
checkForBreak = true;
2030
} else if (nextText->isWordBreak())
2031
checkForBreak = true;
2032
bool willFitOnLine = w + tmpW <= width;
2033
if (!willFitOnLine && !w) {
2034
fitBelowFloats(tmpW, width);
2035
willFitOnLine = tmpW <= width;
2037
bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine;
2038
if (canPlaceOnLine && checkForBreak) {
2043
lBreak.nextBreakablePosition = -1;
2049
if (checkForBreak && (w + tmpW > width)) {
2050
// if we have floats, try to get below them.
2051
if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace())
2052
trailingSpaceObject = 0;
2057
fitBelowFloats(tmpW, width);
2059
// |width| may have been adjusted because we got shoved down past a float (thus
2060
// giving us more room), so we need to retest, and only jump to
2061
// the end label if we still don't fit on the line. -dwh
2062
if (w + tmpW > width)
2066
if (!o->isFloatingOrPositioned()) {
2068
if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || static_cast<RenderListMarker*>(last)->isInside())) {
2073
lBreak.nextBreakablePosition = -1;
2080
// Clear out our character space bool, since inline <pre>s don't collapse whitespace
2081
// with adjacent inline normal/nowrap spans.
2082
if (!collapseWhiteSpace)
2083
currentCharacterIsSpace = false;
2090
if (w + tmpW <= width || lastWS == NOWRAP) {
2093
lBreak.nextBreakablePosition = -1;
2098
if (lBreak == resolver.position() && !lBreak.obj->isBR()) {
2099
// we just add as much as possible
2100
if (style()->whiteSpace() == PRE) {
2101
// FIXME: Don't really understand this case.
2104
lBreak.pos = pos - 1;
2107
lBreak.pos = last->isText() ? last->length() : 0;
2108
lBreak.nextBreakablePosition = -1;
2110
} else if (lBreak.obj) {
2111
if (last != o && !last->isListMarker()) {
2112
// better to break between object boundaries than in the middle of a word (except for list markers)
2115
lBreak.nextBreakablePosition = -1;
2117
// Don't ever break in the middle of a word if we can help it.
2118
// There's no room at all. We just have to be on this line,
2119
// even though we'll spill out.
2122
lBreak.nextBreakablePosition = -1;
2127
// make sure we consume at least one char/object.
2128
if (lBreak == resolver.position())
2131
// Sanity check our midpoints.
2132
checkMidpoints(lBreak);
2134
if (trailingSpaceObject) {
2135
// This object is either going to be part of the last midpoint, or it is going
2136
// to be the actual endpoint. In both cases we just decrease our pos by 1 level to
2137
// exclude the space, allowing it to - in effect - collapse into the newline.
2138
if (sNumMidpoints%2==1) {
2139
InlineIterator* midpoints = smidpoints->data();
2140
midpoints[sNumMidpoints-1].pos--;
2142
//else if (lBreak.pos > 0)
2144
else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
2145
// Add a new end midpoint that stops right at the very end.
2146
RenderText* text = static_cast<RenderText *>(trailingSpaceObject);
2147
unsigned length = text->textLength();
2148
unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
2149
InlineIterator endMid(0, trailingSpaceObject, pos);
2150
addMidpoint(endMid);
2154
// We might have made lBreak an iterator that points past the end
2155
// of the object. Do this adjustment to make it point to the start
2156
// of the next object instead to avoid confusing the rest of the
2158
if (lBreak.pos > 0) {
2163
if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2164
// For soft hyphens on line breaks, we have to chop out the midpoints that made us
2165
// ignore the hyphen so that it will render at the end of the line.
2166
UChar c = static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos-1];
2167
if (c == softHyphen)
2168
chopMidpointsAt(lBreak.obj, lBreak.pos-2);
2174
void RenderBlock::checkLinesForOverflow()
2176
m_overflowWidth = m_width;
2177
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2178
m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
2179
m_overflowTop = min(curr->topOverflow(), m_overflowTop);
2180
m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
2181
m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight);
2185
void RenderBlock::deleteEllipsisLineBoxes()
2187
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
2188
curr->clearTruncation();
2191
void RenderBlock::checkLinesForTextOverflow()
2193
// Determine the width of the ellipsis using the current font.
2194
// FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2195
TextRun ellipsisRun(&horizontalEllipsis, 1);
2196
DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2197
const Font& firstLineFont = firstLineStyle()->font();
2198
const Font& font = style()->font();
2199
int firstLineEllipsisWidth = firstLineFont.width(ellipsisRun);
2200
int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(ellipsisRun);
2202
// For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2203
// if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2204
// check the left edge of the line box to see if it is less
2205
// Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2206
bool ltr = style()->direction() == LTR;
2207
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2208
int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
2209
int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
2210
if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
2211
// This line spills out of our box in the appropriate direction. Now we need to see if the line
2212
// can be truncated. In order for truncation to be possible, the line must have sufficient space to
2213
// accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2215
int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
2216
if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
2217
curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);