1 /*
2 * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All right reserved.
4 *
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.
9 *
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.
14 *
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.
19 *
20 */
21
22 #include "config.h"
23 #include "bidi.h"
24
25 #include "CharacterNames.h"
26 #include "Document.h"
27 #include "Element.h"
28 #include "FrameView.h"
29 #include "InlineTextBox.h"
30 #include "Logging.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>
40 #ifdef ANDROID_LAYOUT
41 #include "Frame.h"
42 #include "FrameTree.h"
43 #include "Settings.h"
44 #include "Text.h"
45 #include "HTMLNames.h"
46 #endif // ANDROID_LAYOUT
47
48 using namespace std;
49 using namespace WTF;
50 using namespace Unicode;
51
52 namespace WebCore {
53
54 // We don't let our line box tree for a single line get any deeper than this.
55 const unsigned cMaxLineDepth = 200;
56
57 class InlineIterator {
58 public:
InlineIterator()59 InlineIterator()
60 : block(0)
61 , obj(0)
62 , pos(0)
63 , nextBreakablePosition(-1)
64 {
65 }
66
InlineIterator(RenderBlock * b,RenderObject * o,unsigned p)67 InlineIterator(RenderBlock* b, RenderObject* o, unsigned p)
68 : block(b)
69 , obj(o)
70 , pos(p)
71 , nextBreakablePosition(-1)
72 {
73 }
74
75 void increment(InlineBidiResolver* resolver = 0);
76 bool atEnd() const;
77
78 UChar current() const;
79 WTF::Unicode::Direction direction() const;
80
81 RenderBlock* block;
82 RenderObject* obj;
83 unsigned pos;
84 int nextBreakablePosition;
85 };
86
87 // Midpoint globals. The goal is not to do any allocation when dealing with
88 // these midpoints, so we just keep an array around and never clear it. We track
89 // the number of items and position using the two other variables.
90 static Vector<InlineIterator>* smidpoints;
91 static unsigned sNumMidpoints;
92 static unsigned sCurrMidpoint;
93 static bool betweenMidpoints;
94
95 static bool isLineEmpty = true;
96 static bool previousLineBrokeCleanly = true;
97
getBorderPaddingMargin(RenderBox * child,bool endOfInline)98 static int getBorderPaddingMargin(RenderBox* child, bool endOfInline)
99 {
100 bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline;
101 if (leftSide)
102 return child->marginLeft() + child->paddingLeft() + child->borderLeft();
103 return child->marginRight() + child->paddingRight() + child->borderRight();
104 }
105
inlineWidth(RenderObject * child,bool start=true,bool end=true)106 static int inlineWidth(RenderObject* child, bool start = true, bool end = true)
107 {
108 unsigned lineDepth = 1;
109 int extraWidth = 0;
110 RenderObject* parent = child->parent();
111 while (parent->isBox() && parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) {
112 if (start && parent->firstChild() == child)
113 extraWidth += getBorderPaddingMargin(toRenderBox(parent), false);
114 if (end && parent->lastChild() == child)
115 extraWidth += getBorderPaddingMargin(toRenderBox(parent), true);
116 child = parent;
117 parent = child->parent();
118 }
119 return extraWidth;
120 }
121
122 #ifndef NDEBUG
123 static WTF::RefCountedLeakCounter bidiRunCounter("BidiRun");
124
125 static bool inBidiRunDestroy;
126 #endif
127
destroy()128 void BidiRun::destroy()
129 {
130 #ifndef NDEBUG
131 inBidiRunDestroy = true;
132 #endif
133 RenderArena* renderArena = m_object->renderArena();
134 delete this;
135 #ifndef NDEBUG
136 inBidiRunDestroy = false;
137 #endif
138
139 // Recover the size left there for us by operator delete and free the memory.
140 renderArena->free(*reinterpret_cast<size_t*>(this), this);
141 }
142
operator new(size_t sz,RenderArena * renderArena)143 void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw()
144 {
145 #ifndef NDEBUG
146 bidiRunCounter.increment();
147 #endif
148 return renderArena->allocate(sz);
149 }
150
operator delete(void * ptr,size_t sz)151 void BidiRun::operator delete(void* ptr, size_t sz)
152 {
153 #ifndef NDEBUG
154 bidiRunCounter.decrement();
155 #endif
156 ASSERT(inBidiRunDestroy);
157
158 // Stash size where destroy() can find it.
159 *(size_t*)ptr = sz;
160 }
161
162 // ---------------------------------------------------------------------
163
operator ==(const InlineIterator & it1,const InlineIterator & it2)164 inline bool operator==(const InlineIterator& it1, const InlineIterator& it2)
165 {
166 return it1.pos == it2.pos && it1.obj == it2.obj;
167 }
168
operator !=(const InlineIterator & it1,const InlineIterator & it2)169 inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2)
170 {
171 return it1.pos != it2.pos || it1.obj != it2.obj;
172 }
173
bidiNext(RenderBlock * block,RenderObject * current,InlineBidiResolver * resolver=0,bool skipInlines=true,bool * endOfInlinePtr=0)174 static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0)
175 {
176 RenderObject* next = 0;
177 bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false;
178 bool endOfInline = false;
179
180 while (current) {
181 next = 0;
182 if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) {
183 next = current->firstChild();
184 if (next && resolver && next->isRenderInline()) {
185 EUnicodeBidi ub = next->style()->unicodeBidi();
186 if (ub != UBNormal) {
187 TextDirection dir = next->style()->direction();
188 Direction d = (ub == Embed
189 ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
190 : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
191 resolver->embed(d);
192 }
193 }
194 }
195
196 if (!next) {
197 if (!skipInlines && !oldEndOfInline && current->isRenderInline()) {
198 next = current;
199 endOfInline = true;
200 break;
201 }
202
203 while (current && current != block) {
204 if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal)
205 resolver->embed(PopDirectionalFormat);
206
207 next = current->nextSibling();
208 if (next) {
209 if (resolver && next->isRenderInline()) {
210 EUnicodeBidi ub = next->style()->unicodeBidi();
211 if (ub != UBNormal) {
212 TextDirection dir = next->style()->direction();
213 Direction d = (ub == Embed
214 ? (dir == RTL ? RightToLeftEmbedding: LeftToRightEmbedding)
215 : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
216 resolver->embed(d);
217 }
218 }
219 break;
220 }
221
222 current = current->parent();
223 if (!skipInlines && current && current != block && current->isRenderInline()) {
224 next = current;
225 endOfInline = true;
226 break;
227 }
228 }
229 }
230
231 if (!next)
232 break;
233
234 if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned()
235 || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines.
236 && next->isRenderInline()))
237 break;
238 current = next;
239 }
240
241 if (endOfInlinePtr)
242 *endOfInlinePtr = endOfInline;
243
244 return next;
245 }
246
bidiFirst(RenderBlock * block,InlineBidiResolver * resolver,bool skipInlines=true)247 static RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true)
248 {
249 if (!block->firstChild())
250 return 0;
251
252 RenderObject* o = block->firstChild();
253 if (o->isRenderInline()) {
254 if (resolver) {
255 EUnicodeBidi ub = o->style()->unicodeBidi();
256 if (ub != UBNormal) {
257 TextDirection dir = o->style()->direction();
258 Direction d = (ub == Embed
259 ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding)
260 : (dir == RTL ? RightToLeftOverride : LeftToRightOverride));
261 resolver->embed(d);
262 }
263 }
264 if (skipInlines && o->firstChild())
265 o = bidiNext(block, o, resolver, skipInlines);
266 else {
267 // Never skip empty inlines.
268 if (resolver)
269 resolver->commitExplicitEmbedding();
270 return o;
271 }
272 }
273
274 if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned())
275 o = bidiNext(block, o, resolver, skipInlines);
276
277 if (resolver)
278 resolver->commitExplicitEmbedding();
279 return o;
280 }
281
increment(InlineBidiResolver * resolver)282 inline void InlineIterator::increment(InlineBidiResolver* resolver)
283 {
284 if (!obj)
285 return;
286 if (obj->isText()) {
287 pos++;
288 if (pos >= toRenderText(obj)->textLength()) {
289 obj = bidiNext(block, obj, resolver);
290 pos = 0;
291 nextBreakablePosition = -1;
292 }
293 } else {
294 obj = bidiNext(block, obj, resolver);
295 pos = 0;
296 nextBreakablePosition = -1;
297 }
298 }
299
300 template<>
increment()301 inline void InlineBidiResolver::increment()
302 {
303 current.increment(this);
304 }
305
atEnd() const306 inline bool InlineIterator::atEnd() const
307 {
308 return !obj;
309 }
310
current() const311 inline UChar InlineIterator::current() const
312 {
313 if (!obj || !obj->isText())
314 return 0;
315
316 RenderText* text = toRenderText(obj);
317 if (pos >= text->textLength())
318 return 0;
319
320 return text->characters()[pos];
321 }
322
direction() const323 ALWAYS_INLINE Direction InlineIterator::direction() const
324 {
325 if (UChar c = current())
326 return Unicode::direction(c);
327
328 if (obj && obj->isListMarker())
329 return obj->style()->direction() == LTR ? LeftToRight : RightToLeft;
330
331 return OtherNeutral;
332 }
333
334 // -------------------------------------------------------------------------------------------------
335
chopMidpointsAt(RenderObject * obj,unsigned pos)336 static void chopMidpointsAt(RenderObject* obj, unsigned pos)
337 {
338 if (!sNumMidpoints)
339 return;
340 InlineIterator* midpoints = smidpoints->data();
341 for (int i = sNumMidpoints - 1; i >= 0; i--) {
342 const InlineIterator& point = midpoints[i];
343 if (point.obj == obj && point.pos == pos) {
344 sNumMidpoints = i;
345 break;
346 }
347 }
348 }
349
checkMidpoints(InlineIterator & lBreak)350 static void checkMidpoints(InlineIterator& lBreak)
351 {
352 // Check to see if our last midpoint is a start point beyond the line break. If so,
353 // shave it off the list, and shave off a trailing space if the previous end point doesn't
354 // preserve whitespace.
355 if (lBreak.obj && sNumMidpoints && sNumMidpoints % 2 == 0) {
356 InlineIterator* midpoints = smidpoints->data();
357 InlineIterator& endpoint = midpoints[sNumMidpoints-2];
358 const InlineIterator& startpoint = midpoints[sNumMidpoints-1];
359 InlineIterator currpoint = endpoint;
360 while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
361 currpoint.increment();
362 if (currpoint == lBreak) {
363 // We hit the line break before the start point. Shave off the start point.
364 sNumMidpoints--;
365 if (endpoint.obj->style()->collapseWhiteSpace()) {
366 if (endpoint.obj->isText()) {
367 // Don't shave a character off the endpoint if it was from a soft hyphen.
368 RenderText* textObj = toRenderText(endpoint.obj);
369 if (endpoint.pos + 1 < textObj->textLength()) {
370 if (textObj->characters()[endpoint.pos+1] == softHyphen)
371 return;
372 } else if (startpoint.obj->isText()) {
373 RenderText *startText = toRenderText(startpoint.obj);
374 if (startText->textLength() && startText->characters()[0] == softHyphen)
375 return;
376 }
377 }
378 endpoint.pos--;
379 }
380 }
381 }
382 }
383
addMidpoint(const InlineIterator & midpoint)384 static void addMidpoint(const InlineIterator& midpoint)
385 {
386 if (smidpoints->size() <= sNumMidpoints)
387 smidpoints->grow(sNumMidpoints + 10);
388
389 InlineIterator* midpoints = smidpoints->data();
390 midpoints[sNumMidpoints++] = midpoint;
391 }
392
appendRunsForObject(int start,int end,RenderObject * obj,InlineBidiResolver & resolver)393 static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
394 {
395 if (start > end || obj->isFloating() ||
396 (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isRenderInline()))
397 return;
398
399 bool haveNextMidpoint = (sCurrMidpoint < sNumMidpoints);
400 InlineIterator nextMidpoint;
401 if (haveNextMidpoint)
402 nextMidpoint = smidpoints->at(sCurrMidpoint);
403 if (betweenMidpoints) {
404 if (!(haveNextMidpoint && nextMidpoint.obj == obj))
405 return;
406 // This is a new start point. Stop ignoring objects and
407 // adjust our start.
408 betweenMidpoints = false;
409 start = nextMidpoint.pos;
410 sCurrMidpoint++;
411 if (start < end)
412 return appendRunsForObject(start, end, obj, resolver);
413 } else {
414 if (!haveNextMidpoint || (obj != nextMidpoint.obj)) {
415 resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
416 return;
417 }
418
419 // An end midpoint has been encountered within our object. We
420 // need to go ahead and append a run with our endpoint.
421 if (static_cast<int>(nextMidpoint.pos + 1) <= end) {
422 betweenMidpoints = true;
423 sCurrMidpoint++;
424 if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
425 if (static_cast<int>(nextMidpoint.pos + 1) > start)
426 resolver.addRun(new (obj->renderArena())
427 BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir()));
428 return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver);
429 }
430 } else
431 resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir()));
432 }
433 }
434
435 template <>
appendRun()436 void InlineBidiResolver::appendRun()
437 {
438 if (!emptyRun && !eor.atEnd()) {
439 int start = sor.pos;
440 RenderObject *obj = sor.obj;
441 while (obj && obj != eor.obj && obj != endOfLine.obj) {
442 appendRunsForObject(start, obj->length(), obj, *this);
443 start = 0;
444 obj = bidiNext(sor.block, obj);
445 }
446 if (obj) {
447 unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX;
448 if (obj == endOfLine.obj && endOfLine.pos <= pos) {
449 reachedEndOfLine = true;
450 pos = endOfLine.pos;
451 }
452 // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be
453 int end = obj->length() ? pos+1 : 0;
454 appendRunsForObject(start, end, obj, *this);
455 }
456
457 eor.increment();
458 sor = eor;
459 }
460
461 m_direction = OtherNeutral;
462 m_status.eor = OtherNeutral;
463 }
464
createLineBoxes(RenderObject * obj)465 InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj)
466 {
467 // See if we have an unconstructed line box for this object that is also
468 // the last item on the line.
469 unsigned lineDepth = 1;
470 InlineFlowBox* childBox = 0;
471 InlineFlowBox* parentBox = 0;
472 InlineFlowBox* result = 0;
473 do {
474 ASSERT(obj->isRenderInline() || obj == this);
475 RenderFlow* flow = static_cast<RenderFlow*>(obj);
476
477 // Get the last box we made for this render object.
478 parentBox = flow->lastLineBox();
479
480 // If this box is constructed then it is from a previous line, and we need
481 // to make a new box for our line. If this box is unconstructed but it has
482 // something following it on the line, then we know we have to make a new box
483 // as well. In this situation our inline has actually been split in two on
484 // the same line (this can happen with very fancy language mixtures).
485 bool constructedNewBox = false;
486 if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) {
487 // We need to make a new box for this render object. Once
488 // made, we need to place it at the end of the current line.
489 InlineBox* newBox = obj->createInlineBox(false, obj == this);
490 ASSERT(newBox->isInlineFlowBox());
491 parentBox = static_cast<InlineFlowBox*>(newBox);
492 parentBox->setFirstLineStyleBit(m_firstLine);
493 constructedNewBox = true;
494 }
495
496 if (!result)
497 result = parentBox;
498
499 // If we have hit the block itself, then |box| represents the root
500 // inline box for the line, and it doesn't have to be appended to any parent
501 // inline.
502 if (childBox)
503 parentBox->addToLine(childBox);
504
505 if (!constructedNewBox || obj == this)
506 break;
507
508 childBox = parentBox;
509
510 // If we've exceeded our line depth, then jump straight to the root and skip all the remaining
511 // intermediate inline flows.
512 obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent();
513
514 } while (true);
515
516 return result;
517 }
518
constructLine(unsigned runCount,BidiRun * firstRun,BidiRun * lastRun,bool lastLine,RenderObject * endObject)519 RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject)
520 {
521 ASSERT(firstRun);
522
523 InlineFlowBox* parentBox = 0;
524 for (BidiRun* r = firstRun; r; r = r->next()) {
525 // Create a box for our object.
526 bool isOnlyRun = (runCount == 1);
527 if (runCount == 2 && !r->m_object->isListMarker())
528 isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker();
529
530 InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun);
531 r->m_box = box;
532
533 if (box) {
534 // If we have no parent box yet, or if the run is not simply a sibling,
535 // then we need to construct inline boxes as necessary to properly enclose the
536 // run's inline box.
537 if (!parentBox || parentBox->object() != r->m_object->parent())
538 // Create new inline boxes all the way back to the appropriate insertion point.
539 parentBox = createLineBoxes(r->m_object->parent());
540
541 // Append the inline box to this line.
542 parentBox->addToLine(box);
543
544 bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
545 box->setBidiLevel(visuallyOrdered ? 0 : r->level());
546
547 if (box->isInlineTextBox()) {
548 InlineTextBox* text = static_cast<InlineTextBox*>(box);
549 text->setStart(r->m_start);
550 text->setLen(r->m_stop - r->m_start);
551 text->m_dirOverride = r->dirOverride(visuallyOrdered);
552 }
553 }
554 }
555
556 // We should have a root inline box. It should be unconstructed and
557 // be the last continuation of our line list.
558 ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
559
560 // Set bits on our inline flow boxes that indicate which sides should
561 // paint borders/margins/padding. This knowledge will ultimately be used when
562 // we determine the horizontal positions and widths of all the inline boxes on
563 // the line.
564 lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject);
565
566 // Now mark the line boxes as being constructed.
567 lastLineBox()->setConstructed();
568
569 // Return the last line.
570 return lastRootBox();
571 }
572
computeHorizontalPositionsForLine(RootInlineBox * lineBox,BidiRun * firstRun,BidiRun * trailingSpaceRun,bool reachedEnd)573 void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd)
574 {
575 // First determine our total width.
576 int availableWidth = lineWidth(height());
577 int totWidth = lineBox->getFlowSpacingWidth();
578 bool needsWordSpacing = false;
579 unsigned numSpaces = 0;
580 ETextAlign textAlign = style()->textAlign();
581
582 for (BidiRun* r = firstRun; r; r = r->next()) {
583 if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak())
584 continue; // Positioned objects are only participating to figure out their
585 // correct static x position. They have no effect on the width.
586 // Similarly, line break boxes have no effect on the width.
587 if (r->m_object->isText()) {
588 RenderText* rt = toRenderText(r->m_object);
589
590 if (textAlign == JUSTIFY && r != trailingSpaceRun) {
591 const UChar* characters = rt->characters();
592 for (int i = r->m_start; i < r->m_stop; i++) {
593 UChar c = characters[i];
594 if (c == ' ' || c == '\n' || c == '\t')
595 numSpaces++;
596 }
597 }
598
599 if (int length = rt->textLength()) {
600 if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start]))
601 totWidth += rt->style(m_firstLine)->font().wordSpacing();
602 needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length;
603 }
604 r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, m_firstLine));
605 } else if (!r->m_object->isRenderInline()) {
606 RenderBox* renderBox = toRenderBox(r->m_object);
607 renderBox->calcWidth();
608 r->m_box->setWidth(renderBox->width());
609 totWidth += renderBox->marginLeft() + renderBox->marginRight();
610 }
611
612 totWidth += r->m_box->width();
613 }
614
615 // Armed with the total width of the line (without justification),
616 // we now examine our text-align property in order to determine where to position the
617 // objects horizontally. The total width of the line can be increased if we end up
618 // justifying text.
619 int x = leftOffset(height());
620 switch(textAlign) {
621 case LEFT:
622 case WEBKIT_LEFT:
623 // The direction of the block should determine what happens with wide lines. In
624 // particular with RTL blocks, wide lines should still spill out to the left.
625 if (style()->direction() == LTR) {
626 if (totWidth > availableWidth && trailingSpaceRun)
627 trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
628 } else {
629 if (trailingSpaceRun)
630 trailingSpaceRun->m_box->setWidth(0);
631 else if (totWidth > availableWidth)
632 x -= (totWidth - availableWidth);
633 }
634 break;
635 case JUSTIFY:
636 if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) {
637 if (trailingSpaceRun) {
638 totWidth -= trailingSpaceRun->m_box->width();
639 trailingSpaceRun->m_box->setWidth(0);
640 }
641 break;
642 }
643 // fall through
644 case TAAUTO:
645 numSpaces = 0;
646 // for right to left fall through to right aligned
647 if (style()->direction() == LTR) {
648 if (totWidth > availableWidth && trailingSpaceRun)
649 trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
650 break;
651 }
652 case RIGHT:
653 case WEBKIT_RIGHT:
654 // Wide lines spill out of the block based off direction.
655 // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right
656 // side of the block.
657 if (style()->direction() == LTR) {
658 if (trailingSpaceRun) {
659 totWidth -= trailingSpaceRun->m_box->width();
660 trailingSpaceRun->m_box->setWidth(0);
661 }
662 if (totWidth < availableWidth)
663 x += availableWidth - totWidth;
664 } else {
665 if (totWidth > availableWidth && trailingSpaceRun) {
666 trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth);
667 totWidth -= trailingSpaceRun->m_box->width();
668 } else
669 x += availableWidth - totWidth;
670 }
671 break;
672 case CENTER:
673 case WEBKIT_CENTER:
674 int trailingSpaceWidth = 0;
675 if (trailingSpaceRun) {
676 totWidth -= trailingSpaceRun->m_box->width();
677 trailingSpaceWidth = min(trailingSpaceRun->m_box->width(), (availableWidth - totWidth + 1) / 2);
678 trailingSpaceRun->m_box->setWidth(trailingSpaceWidth);
679 }
680 if (style()->direction() == LTR)
681 x += max((availableWidth - totWidth) / 2, 0);
682 else
683 x += totWidth > availableWidth ? (availableWidth - totWidth) : (availableWidth - totWidth) / 2 - trailingSpaceWidth;
684 break;
685 }
686
687 if (numSpaces) {
688 for (BidiRun* r = firstRun; r; r = r->next()) {
689 if (!r->m_box || r == trailingSpaceRun)
690 continue;
691
692 int spaceAdd = 0;
693 if (r->m_object->isText()) {
694 unsigned spaces = 0;
695 const UChar* characters = toRenderText(r->m_object)->characters();
696 for (int i = r->m_start; i < r->m_stop; i++) {
697 UChar c = characters[i];
698 if (c == ' ' || c == '\n' || c == '\t')
699 spaces++;
700 }
701
702 ASSERT(spaces <= numSpaces);
703
704 // Only justify text if whitespace is collapsed.
705 if (r->m_object->style()->collapseWhiteSpace()) {
706 spaceAdd = (availableWidth - totWidth) * spaces / numSpaces;
707 static_cast<InlineTextBox*>(r->m_box)->setSpaceAdd(spaceAdd);
708 totWidth += spaceAdd;
709 }
710 numSpaces -= spaces;
711 if (!numSpaces)
712 break;
713 }
714 }
715 }
716
717 // The widths of all runs are now known. We can now place every inline box (and
718 // compute accurate widths for the inline flow boxes).
719 int leftPosition = x;
720 int rightPosition = x;
721 needsWordSpacing = false;
722 lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing);
723 lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition);
724 }
725
computeVerticalPositionsForLine(RootInlineBox * lineBox,BidiRun * firstRun)726 void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun)
727 {
728 setHeight(lineBox->verticallyAlignBoxes(height()));
729 lineBox->setBlockHeight(height());
730
731 // See if the line spilled out. If so set overflow height accordingly.
732 int bottomOfLine = lineBox->bottomOverflow();
733 if (bottomOfLine > height() && bottomOfLine > m_overflowHeight)
734 m_overflowHeight = bottomOfLine;
735
736 // Now make sure we place replaced render objects correctly.
737 for (BidiRun* r = firstRun; r; r = r->next()) {
738 if (!r->m_box)
739 continue; // Skip runs with no line boxes.
740
741 // Align positioned boxes with the top of the line box. This is
742 // a reasonable approximation of an appropriate y position.
743 if (r->m_object->isPositioned())
744 r->m_box->setYPos(height());
745
746 // Position is used to properly position both replaced elements and
747 // to update the static normal flow x/y of positioned elements.
748 r->m_object->position(r->m_box);
749 }
750 // Positioned objects and zero-length text nodes destroy their boxes in
751 // position(), which unnecessarily dirties the line.
752 lineBox->markDirty(false);
753 }
754
755 // collects one line of the paragraph and transforms it to visual order
bidiReorderLine(InlineBidiResolver & resolver,const InlineIterator & end)756 void RenderBlock::bidiReorderLine(InlineBidiResolver& resolver, const InlineIterator& end)
757 {
758 resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly);
759 }
760
isCollapsibleSpace(UChar character,RenderText * renderer)761 static inline bool isCollapsibleSpace(UChar character, RenderText* renderer)
762 {
763 if (character == ' ' || character == '\t' || character == softHyphen)
764 return true;
765 if (character == '\n')
766 return !renderer->style()->preserveNewline();
767 if (character == noBreakSpace)
768 return renderer->style()->nbspMode() == SPACE;
769 return false;
770 }
771
layoutInlineChildren(bool relayoutChildren,int & repaintTop,int & repaintBottom)772 void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom)
773 {
774 bool useRepaintBounds = false;
775
776 invalidateVerticalPosition();
777
778 m_overflowHeight = 0;
779
780 setHeight(borderTop() + paddingTop());
781 int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight();
782
783 // Figure out if we should clear out our line boxes.
784 // FIXME: Handle resize eventually!
785 // FIXME: Do something better when floats are present.
786 bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren;
787 if (fullLayout)
788 deleteLineBoxes();
789
790 // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't
791 // clip.
792 // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
793 // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense
794 // anyway, so we won't worry about following the draft here.
795 bool hasTextOverflow = style()->textOverflow() && hasOverflowClip();
796
797 // Walk all the lines and delete our ellipsis line boxes if they exist.
798 if (hasTextOverflow)
799 deleteEllipsisLineBoxes();
800
801 if (firstChild()) {
802 #ifdef ANDROID_LAYOUT
803 // if we are in fitColumnToScreen mode and viewport width is not device-width,
804 // and the current object is not float:right in LTR or not float:left in RTL,
805 // and text align is auto, or justify or left in LTR, or right in RTL, we
806 // will wrap text around screen width so that it doesn't need to scroll
807 // horizontally when reading a paragraph.
808 const Settings* settings = document()->settings();
809 bool doTextWrap = settings && settings->viewportWidth() != 0 &&
810 settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen;
811 if (doTextWrap) {
812 int ta = style()->textAlign();
813 int dir = style()->direction();
814 EFloat cssfloat = style()->floating();
815 doTextWrap = ((dir == LTR && cssfloat != FRIGHT) ||
816 (dir == RTL && cssfloat != FLEFT)) &&
817 ((ta == TAAUTO) || (ta == JUSTIFY) ||
818 ((ta == LEFT || ta == WEBKIT_LEFT) && (dir == LTR)) ||
819 ((ta == RIGHT || ta == WEBKIT_RIGHT) && (dir == RTL)));
820 }
821 bool hasTextToWrap = false;
822 #endif
823 // layout replaced elements
824 bool endOfInline = false;
825 RenderObject* o = bidiFirst(this, 0, false);
826 Vector<FloatWithRect> floats;
827 int containerWidth = max(0, containingBlockWidth());
828 while (o) {
829 o->invalidateVerticalPosition();
830 if (o->isReplaced() || o->isFloating() || o->isPositioned()) {
831 RenderBox* box = toRenderBox(o);
832
833 if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent())
834 o->setChildNeedsLayout(true, false);
835
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);
839
840 if (o->isPositioned())
841 o->containingBlock()->insertPositionedObject(box);
842 else {
843 #ifdef ANDROID_LAYOUT
844 // ignore text wrap for textField or menuList
845 if (doTextWrap && (o->isTextField() || o->isMenuList()))
846 doTextWrap = false;
847 #endif
848 if (o->isFloating())
849 floats.append(FloatWithRect(box));
850 else if (fullLayout || o->needsLayout()) // Replaced elements
851 o->dirtyLineBoxes(fullLayout);
852
853 o->layoutIfNeeded();
854 }
855 } else if (o->isText() || (o->isRenderInline() && !endOfInline)) {
856 if (fullLayout || o->selfNeedsLayout())
857 o->dirtyLineBoxes(fullLayout);
858
859 // Calculate margins of inline flows so that they can be used later by line layout.
860 if (o->isRenderInline())
861 static_cast<RenderFlow*>(o)->calcMargins(containerWidth);
862 o->setNeedsLayout(false);
863 #ifdef ANDROID_LAYOUT
864 if (doTextWrap && !hasTextToWrap && o->isText()) {
865 Node* node = o->element();
866 // as it is very common for sites to use a serial of <a> or
867 // <li> as tabs, we don't force text to wrap if all the text
868 // are short and within an <a> or <li> tag, and only separated
869 // by short word like "|" or ";".
870 if (node && node->isTextNode() &&
871 !static_cast<Text*>(node)->containsOnlyWhitespace()) {
872 int length = static_cast<Text*>(node)->length();
873 // FIXME, need a magic number to decide it is too long to
874 // be a tab. Pick 25 for now as it covers around 160px
875 // (half of 320px) with the default font.
876 if (length > 25 || (length > 3 &&
877 (!node->parent()->hasTagName(HTMLNames::aTag) &&
878 !node->parent()->hasTagName(HTMLNames::liTag))))
879 hasTextToWrap = true;
880 }
881 }
882 #endif
883 }
884 o = bidiNext(this, o, 0, false, &endOfInline);
885 }
886
887 #ifdef ANDROID_LAYOUT
888 // try to make sure that inline text will not span wider than the
889 // screen size unless the container has a fixed height,
890 if (doTextWrap && hasTextToWrap) {
891 // check all the nested containing blocks, unless it is table or
892 // table-cell, to make sure there is no fixed height as it implies
893 // fixed layout. If we constrain the text to fit screen, we may
894 // cause text overlap with the block after.
895 bool isConstrained = false;
896 RenderObject* obj = this;
897 while (obj) {
898 if (obj->style()->height().isFixed() && (!obj->isTable() && !obj->isTableCell())) {
899 isConstrained = true;
900 break;
901 }
902 if (obj->isFloating() || obj->isPositioned()) {
903 // floating and absolute or fixed positioning are done out
904 // of normal flow. Don't need to worry about height any more.
905 break;
906 }
907 obj = obj->container();
908 }
909 if (!isConstrained) {
910 int screenWidth = view()->frameView()->screenWidth();
911 if (screenWidth > 0 && width() > screenWidth) {
912 int maxWidth = screenWidth - 2 * ANDROID_FCTS_MARGIN_PADDING;
913 setWidth(min(width(), maxWidth));
914 m_minPrefWidth = min(m_minPrefWidth, maxWidth);
915 m_maxPrefWidth = min(m_maxPrefWidth, maxWidth);
916 m_overflowWidth = min(m_overflowWidth, maxWidth);
917 }
918 }
919 }
920 #endif
921 // We want to skip ahead to the first dirty line
922 InlineBidiResolver resolver;
923 unsigned floatIndex;
924 RootInlineBox* startLine = determineStartPosition(fullLayout, resolver, floats, floatIndex);
925
926 if (fullLayout && !selfNeedsLayout()) {
927 setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like
928 // we're supposed to.
929 RenderView* v = view();
930 if (v && !v->doingFullRepaint() && m_layer) {
931 // Because we waited until we were already inside layout to discover
932 // that the block really needed a full layout, we missed our chance to repaint the layer
933 // before layout started. Luckily the layer has cached the repaint rect for its original
934 // position and size, and so we can use that to make a repaint happen now.
935 v->repaintViewRectangle(m_layer->repaintRect());
936 }
937 }
938
939 FloatingObject* lastFloat = m_floatingObjects ? m_floatingObjects->last() : 0;
940
941 if (!smidpoints)
942 smidpoints = new Vector<InlineIterator>();
943
944 sNumMidpoints = 0;
945 sCurrMidpoint = 0;
946
947 // We also find the first clean line and extract these lines. We will add them back
948 // if we determine that we're able to synchronize after handling all our dirty lines.
949 InlineIterator cleanLineStart;
950 BidiStatus cleanLineBidiStatus;
951 int endLineYPos = 0;
952 RootInlineBox* endLine = (fullLayout || !startLine) ?
953 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineYPos);
954
955 if (startLine) {
956 useRepaintBounds = true;
957 repaintTop = height();
958 repaintBottom = height();
959 RenderArena* arena = renderArena();
960 RootInlineBox* box = startLine;
961 while (box) {
962 repaintTop = min(repaintTop, box->topOverflow());
963 repaintBottom = max(repaintBottom, box->bottomOverflow());
964 RootInlineBox* next = box->nextRootBox();
965 box->deleteLine(arena);
966 box = next;
967 }
968 }
969
970 InlineIterator end = resolver.position();
971
972 if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) {
973 // If the last line before the start line ends with a line break that clear floats,
974 // adjust the height accordingly.
975 // A line break can be either the first or the last object on a line, depending on its direction.
976 if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
977 RenderObject* lastObject = lastLeafChild->object();
978 if (!lastObject->isBR())
979 lastObject = lastRootBox()->firstLeafChild()->object();
980 if (lastObject->isBR()) {
981 EClear clear = lastObject->style()->clear();
982 if (clear != CNONE)
983 newLine(clear);
984 }
985 }
986 }
987
988 bool endLineMatched = false;
989 bool checkForEndLineMatch = endLine;
990 bool checkForFloatsFromLastLine = false;
991 int lastHeight = height();
992
993 while (!end.atEnd()) {
994 // FIXME: Is this check necessary before the first iteration or can it be moved to the end?
995 if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineYPos, repaintBottom, repaintTop)))
996 break;
997
998 betweenMidpoints = false;
999 isLineEmpty = true;
1000
1001 EClear clear = CNONE;
1002 end = findNextLineBreak(resolver, &clear);
1003 if (resolver.position().atEnd()) {
1004 resolver.deleteRuns();
1005 checkForFloatsFromLastLine = true;
1006 break;
1007 }
1008 ASSERT(end != resolver.position());
1009
1010 if (!isLineEmpty) {
1011 bidiReorderLine(resolver, end);
1012 ASSERT(resolver.position() == end);
1013
1014 BidiRun* trailingSpaceRun = 0;
1015 if (!previousLineBrokeCleanly && resolver.runCount() && resolver.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()) {
1016 trailingSpaceRun = resolver.logicallyLastRun();
1017 RenderObject* lastObject = trailingSpaceRun->m_object;
1018 if (lastObject->isText()) {
1019 RenderText* lastText = toRenderText(lastObject);
1020 const UChar* characters = lastText->characters();
1021 int firstSpace = trailingSpaceRun->stop();
1022 while (firstSpace > trailingSpaceRun->start()) {
1023 UChar current = characters[firstSpace - 1];
1024 if (!isCollapsibleSpace(current, lastText))
1025 break;
1026 firstSpace--;
1027 }
1028 if (firstSpace == trailingSpaceRun->stop())
1029 trailingSpaceRun = 0;
1030 else {
1031 TextDirection direction = style()->direction();
1032 bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun());
1033 if (firstSpace != trailingSpaceRun->start()) {
1034 ETextAlign textAlign = style()->textAlign();
1035 // If the trailing white space is at the right hand side of a left-aligned line, then computeHorizontalPositionsForLine()
1036 // does not care if trailingSpaceRun includes non-spaces at the beginning. In all other cases, trailingSpaceRun has to
1037 // contain only the spaces, either because we re-order them or because computeHorizontalPositionsForLine() needs to know
1038 // their width.
1039 bool shouldSeparateSpaces = textAlign != LEFT && textAlign != WEBKIT_LEFT && textAlign != TAAUTO || trailingSpaceRun->m_level % 2 || direction == RTL || shouldReorder;
1040 if (shouldSeparateSpaces) {
1041 BidiContext* baseContext = resolver.context();
1042 while (BidiContext* parent = baseContext->parent())
1043 baseContext = parent;
1044
1045 BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
1046 trailingSpaceRun->m_stop = firstSpace;
1047 if (direction == LTR)
1048 resolver.addRun(newTrailingRun);
1049 else
1050 resolver.prependRun(newTrailingRun);
1051 trailingSpaceRun = newTrailingRun;
1052 shouldReorder = false;
1053 }
1054 }
1055 if (shouldReorder) {
1056 if (direction == LTR) {
1057 resolver.moveRunToEnd(trailingSpaceRun);
1058 trailingSpaceRun->m_level = 0;
1059 } else {
1060 resolver.moveRunToBeginning(trailingSpaceRun);
1061 trailingSpaceRun->m_level = 1;
1062 }
1063 }
1064 }
1065 } else
1066 trailingSpaceRun = 0;
1067 }
1068
1069 // Now that the runs have been ordered, we create the line boxes.
1070 // At the same time we figure out where border/padding/margin should be applied for
1071 // inline flow boxes.
1072
1073 RootInlineBox* lineBox = 0;
1074 if (resolver.runCount()) {
1075 lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), !end.obj, end.obj && !end.pos ? end.obj : 0);
1076 if (lineBox) {
1077 lineBox->setEndsWithBreak(previousLineBrokeCleanly);
1078
1079 // Now we position all of our text runs horizontally.
1080 computeHorizontalPositionsForLine(lineBox, resolver.firstRun(), trailingSpaceRun, end.atEnd());
1081
1082 // Now position our text runs vertically.
1083 computeVerticalPositionsForLine(lineBox, resolver.firstRun());
1084
1085 #if ENABLE(SVG)
1086 // Special SVG text layout code
1087 lineBox->computePerCharacterLayoutInformation();
1088 #endif
1089
1090 #if PLATFORM(MAC)
1091 // Highlight acts as an overflow inflation.
1092 if (style()->highlight() != nullAtom)
1093 lineBox->addHighlightOverflow();
1094 #endif
1095 }
1096 }
1097
1098 resolver.deleteRuns();
1099
1100 if (lineBox) {
1101 lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status());
1102 if (useRepaintBounds) {
1103 repaintTop = min(repaintTop, lineBox->topOverflow());
1104 repaintBottom = max(repaintBottom, lineBox->bottomOverflow());
1105 }
1106 }
1107
1108 m_firstLine = false;
1109 newLine(clear);
1110 }
1111
1112 if (m_floatingObjects && lastRootBox()) {
1113 if (lastFloat) {
1114 for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1115 }
1116 m_floatingObjects->next();
1117 } else
1118 m_floatingObjects->first();
1119 for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1120 if (f->m_bottom > lastHeight)
1121 lastRootBox()->floats().append(f->m_renderer);
1122 ASSERT(f->m_renderer == floats[floatIndex].object);
1123 // If a float's geometry has changed, give up on syncing with clean lines.
1124 if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top))
1125 checkForEndLineMatch = false;
1126 floatIndex++;
1127 }
1128 lastFloat = m_floatingObjects->last();
1129 }
1130
1131 lastHeight = height();
1132 sNumMidpoints = 0;
1133 sCurrMidpoint = 0;
1134 resolver.setPosition(end);
1135 }
1136
1137 if (endLine) {
1138 if (endLineMatched) {
1139 // Attach all the remaining lines, and then adjust their y-positions as needed.
1140 int delta = height() - endLineYPos;
1141 for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) {
1142 line->attachLine();
1143 if (delta) {
1144 repaintTop = min(repaintTop, line->topOverflow() + min(delta, 0));
1145 repaintBottom = max(repaintBottom, line->bottomOverflow() + max(delta, 0));
1146 line->adjustPosition(0, delta);
1147 }
1148 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1149 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1150 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1151 int floatTop = (*f)->y() - (*f)->marginTop();
1152 insertFloatingObject(*f);
1153 setHeight(floatTop + delta);
1154 positionNewFloats();
1155 }
1156 }
1157 }
1158 setHeight(lastRootBox()->blockHeight());
1159 } else {
1160 // Delete all the remaining lines.
1161 InlineRunBox* line = endLine;
1162 RenderArena* arena = renderArena();
1163 while (line) {
1164 repaintTop = min(repaintTop, line->topOverflow());
1165 repaintBottom = max(repaintBottom, line->bottomOverflow());
1166 InlineRunBox* next = line->nextLineBox();
1167 line->deleteLine(arena);
1168 line = next;
1169 }
1170 }
1171 }
1172 if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) {
1173 // In case we have a float on the last line, it might not be positioned up to now.
1174 // This has to be done before adding in the bottom border/padding, or the float will
1175 // include the padding incorrectly. -dwh
1176 if (lastFloat) {
1177 for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) {
1178 }
1179 m_floatingObjects->next();
1180 } else
1181 m_floatingObjects->first();
1182 for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) {
1183 if (f->m_bottom > lastHeight)
1184 lastRootBox()->floats().append(f->m_renderer);
1185 }
1186 lastFloat = m_floatingObjects->last();
1187 }
1188 }
1189
1190 sNumMidpoints = 0;
1191 sCurrMidpoint = 0;
1192
1193 // Now add in the bottom border/padding.
1194 setHeight(height() + toAdd);
1195
1196 // Always make sure this is at least our height.
1197 m_overflowHeight = max(height(), m_overflowHeight);
1198
1199 // See if any lines spill out of the block. If so, we need to update our overflow width.
1200 checkLinesForOverflow();
1201
1202 if (!firstLineBox() && hasLineIfEmpty())
1203 setHeight(height() + lineHeight(true, true));
1204
1205 // See if we have any lines that spill out of our block. If we do, then we will possibly need to
1206 // truncate text.
1207 if (hasTextOverflow)
1208 checkLinesForTextOverflow();
1209 }
1210
determineStartPosition(bool & fullLayout,InlineBidiResolver & resolver,Vector<FloatWithRect> & floats,unsigned & numCleanFloats)1211 RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats)
1212 {
1213 RootInlineBox* curr = 0;
1214 RootInlineBox* last = 0;
1215
1216 bool dirtiedByFloat = false;
1217 if (!fullLayout) {
1218 size_t floatIndex = 0;
1219 for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
1220 if (Vector<RenderBox*>* cleanLineFloats = curr->floatsPtr()) {
1221 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1222 for (Vector<RenderBox*>::iterator o = cleanLineFloats->begin(); o != end; ++o) {
1223 RenderBox* f = *o;
1224 IntSize newSize(f->width() + f->marginLeft() +f->marginRight(), f->height() + f->marginTop() + f->marginBottom());
1225 ASSERT(floatIndex < floats.size());
1226 if (floats[floatIndex].object != f) {
1227 // A new float has been inserted before this line or before its last known float.
1228 // Just do a full layout.
1229 fullLayout = true;
1230 break;
1231 }
1232 if (floats[floatIndex].rect.size() != newSize) {
1233 int floatTop = floats[floatIndex].rect.y();
1234 curr->markDirty();
1235 markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height()));
1236 floats[floatIndex].rect.setSize(newSize);
1237 dirtiedByFloat = true;
1238 }
1239 floatIndex++;
1240 }
1241 }
1242 if (dirtiedByFloat || fullLayout)
1243 break;
1244 }
1245 // Check if a new float has been inserted after the last known float.
1246 if (!curr && floatIndex < floats.size())
1247 fullLayout = true;
1248 }
1249
1250 if (fullLayout) {
1251 // Nuke all our lines.
1252 if (firstRootBox()) {
1253 RenderArena* arena = renderArena();
1254 curr = firstRootBox();
1255 while (curr) {
1256 RootInlineBox* next = curr->nextRootBox();
1257 curr->deleteLine(arena);
1258 curr = next;
1259 }
1260 ASSERT(!firstLineBox() && !lastLineBox());
1261 }
1262 } else {
1263 if (curr) {
1264 // We have a dirty line.
1265 if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
1266 // We have a previous line.
1267 if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))
1268 // The previous line didn't break cleanly or broke at a newline
1269 // that has been deleted, so treat it as dirty too.
1270 curr = prevRootBox;
1271 }
1272 } else {
1273 // No dirty lines were found.
1274 // If the last line didn't break cleanly, treat it as dirty.
1275 if (lastRootBox() && !lastRootBox()->endsWithBreak())
1276 curr = lastRootBox();
1277 }
1278
1279 // If we have no dirty lines, then last is just the last root box.
1280 last = curr ? curr->prevRootBox() : lastRootBox();
1281 }
1282
1283 numCleanFloats = 0;
1284 if (!floats.isEmpty()) {
1285 int savedHeight = height();
1286 // Restore floats from clean lines.
1287 RootInlineBox* line = firstRootBox();
1288 while (line != curr) {
1289 if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
1290 Vector<RenderBox*>::iterator end = cleanLineFloats->end();
1291 for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
1292 insertFloatingObject(*f);
1293 setHeight((*f)->y() - (*f)->marginTop());
1294 positionNewFloats();
1295 ASSERT(floats[numCleanFloats].object == *f);
1296 numCleanFloats++;
1297 }
1298 }
1299 line = line->nextRootBox();
1300 }
1301 setHeight(savedHeight);
1302 }
1303
1304 m_firstLine = !last;
1305 previousLineBrokeCleanly = !last || last->endsWithBreak();
1306
1307 RenderObject* startObj;
1308 int pos = 0;
1309 if (last) {
1310 setHeight(last->blockHeight());
1311 startObj = last->lineBreakObj();
1312 pos = last->lineBreakPos();
1313 resolver.setStatus(last->lineBreakBidiStatus());
1314 } else {
1315 bool ltr = style()->direction() == LTR
1316 #if ENABLE(SVG)
1317 || (style()->unicodeBidi() == UBNormal && isSVGText())
1318 #endif
1319 ;
1320
1321 BidiContext* context = new BidiContext(ltr ? 0 : 1, ltr ? LeftToRight : RightToLeft, style()->unicodeBidi() == Override);
1322
1323 resolver.setLastStrongDir(context->dir());
1324 resolver.setLastDir(context->dir());
1325 resolver.setEorDir(context->dir());
1326 resolver.setContext(context);
1327 startObj = bidiFirst(this, &resolver);
1328 }
1329
1330 resolver.setPosition(InlineIterator(this, startObj, pos));
1331
1332 return curr;
1333 }
1334
determineEndPosition(RootInlineBox * startLine,InlineIterator & cleanLineStart,BidiStatus & cleanLineBidiStatus,int & yPos)1335 RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos)
1336 {
1337 RootInlineBox* last = 0;
1338 if (!startLine)
1339 last = 0;
1340 else {
1341 for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
1342 if (curr->isDirty())
1343 last = 0;
1344 else if (!last)
1345 last = curr;
1346 }
1347 }
1348
1349 if (!last)
1350 return 0;
1351
1352 RootInlineBox* prev = last->prevRootBox();
1353 cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
1354 cleanLineBidiStatus = prev->lineBreakBidiStatus();
1355 yPos = prev->blockHeight();
1356
1357 for (RootInlineBox* line = last; line; line = line->nextRootBox())
1358 line->extractLine(); // Disconnect all line boxes from their render objects while preserving
1359 // their connections to one another.
1360
1361 return last;
1362 }
1363
matchedEndLine(const InlineBidiResolver & resolver,const InlineIterator & endLineStart,const BidiStatus & endLineStatus,RootInlineBox * & endLine,int & endYPos,int & repaintBottom,int & repaintTop)1364 bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop)
1365 {
1366 if (resolver.position() == endLineStart) {
1367 if (resolver.status() != endLineStatus)
1368 return false;
1369
1370 int delta = height() - endYPos;
1371 if (!delta || !m_floatingObjects)
1372 return true;
1373
1374 // See if any floats end in the range along which we want to shift the lines vertically.
1375 int top = min(height(), endYPos);
1376
1377 RootInlineBox* lastLine = endLine;
1378 while (RootInlineBox* nextLine = lastLine->nextRootBox())
1379 lastLine = nextLine;
1380
1381 int bottom = lastLine->blockHeight() + abs(delta);
1382
1383 for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1384 if (f->m_bottom >= top && f->m_bottom < bottom)
1385 return false;
1386 }
1387
1388 return true;
1389 }
1390
1391 // The first clean line doesn't match, but we can check a handful of following lines to try
1392 // to match back up.
1393 static int numLines = 8; // The # of lines we're willing to match against.
1394 RootInlineBox* line = endLine;
1395 for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) {
1396 if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) {
1397 // We have a match.
1398 if (line->lineBreakBidiStatus() != resolver.status())
1399 return false; // ...but the bidi state doesn't match.
1400 RootInlineBox* result = line->nextRootBox();
1401
1402 // Set our yPos to be the block height of endLine.
1403 if (result)
1404 endYPos = line->blockHeight();
1405
1406 int delta = height() - endYPos;
1407 if (delta && m_floatingObjects) {
1408 // See if any floats end in the range along which we want to shift the lines vertically.
1409 int top = min(height(), endYPos);
1410
1411 RootInlineBox* lastLine = endLine;
1412 while (RootInlineBox* nextLine = lastLine->nextRootBox())
1413 lastLine = nextLine;
1414
1415 int bottom = lastLine->blockHeight() + abs(delta);
1416
1417 for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) {
1418 if (f->m_bottom >= top && f->m_bottom < bottom)
1419 return false;
1420 }
1421 }
1422
1423 // Now delete the lines that we failed to sync.
1424 RootInlineBox* boxToDelete = endLine;
1425 RenderArena* arena = renderArena();
1426 while (boxToDelete && boxToDelete != result) {
1427 repaintTop = min(repaintTop, boxToDelete->topOverflow());
1428 repaintBottom = max(repaintBottom, boxToDelete->bottomOverflow());
1429 RootInlineBox* next = boxToDelete->nextRootBox();
1430 boxToDelete->deleteLine(arena);
1431 boxToDelete = next;
1432 }
1433
1434 endLine = result;
1435 return result;
1436 }
1437 }
1438
1439 return false;
1440 }
1441
skipNonBreakingSpace(const InlineIterator & it)1442 static inline bool skipNonBreakingSpace(const InlineIterator& it)
1443 {
1444 if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace)
1445 return false;
1446
1447 // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly
1448 // with m_minWidth/m_maxWidth.
1449 // Do not skip a non-breaking space if it is the first character
1450 // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
1451 // |true|).
1452 if (isLineEmpty && previousLineBrokeCleanly)
1453 return false;
1454
1455 return true;
1456 }
1457
shouldCollapseWhiteSpace(const RenderStyle * style)1458 static inline bool shouldCollapseWhiteSpace(const RenderStyle* style)
1459 {
1460 return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly));
1461 }
1462
shouldPreserveNewline(RenderObject * object)1463 static inline bool shouldPreserveNewline(RenderObject* object)
1464 {
1465 #if ENABLE(SVG)
1466 if (object->isSVGText())
1467 return false;
1468 #endif
1469
1470 return object->style()->preserveNewline();
1471 }
1472
inlineFlowRequiresLineBox(RenderBox * flow)1473 static bool inlineFlowRequiresLineBox(RenderBox* flow)
1474 {
1475 // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
1476 // We need to fix this, though, because at the very least, inlines containing only
1477 // ignorable whitespace should should also have line boxes.
1478 return flow->isRenderInline() && !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin();
1479 }
1480
requiresLineBox(const InlineIterator & it)1481 static inline bool requiresLineBox(const InlineIterator& it)
1482 {
1483 if (it.obj->isFloatingOrPositioned())
1484 return false;
1485
1486 if (it.obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderBox(it.obj)))
1487 return false;
1488
1489 if (!shouldCollapseWhiteSpace(it.obj->style()) || it.obj->isBR())
1490 return true;
1491
1492 UChar current = it.current();
1493 return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) && !skipNonBreakingSpace(it);
1494 }
1495
generatesLineBoxesForInlineChild(RenderObject * inlineObj)1496 bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
1497 {
1498 ASSERT(inlineObj->parent() == this);
1499
1500 InlineIterator it(this, inlineObj, 0);
1501 while (!it.atEnd() && !requiresLineBox(it))
1502 it.increment();
1503
1504 return !it.atEnd();
1505 }
1506
1507 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
1508 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
1509 // elements quite right. In other words, we need to build this function's work into the normal line
1510 // object iteration process.
1511 // NB. this function will insert any floating elements that would otherwise
1512 // be skipped but it will not position them.
skipTrailingWhitespace(InlineIterator & iterator)1513 void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator)
1514 {
1515 while (!iterator.atEnd() && !requiresLineBox(iterator)) {
1516 RenderObject* object = iterator.obj;
1517 if (object->isFloating()) {
1518 insertFloatingObject(toRenderBox(object));
1519 } else if (object->isPositioned()) {
1520 // FIXME: The math here is actually not really right. It's a best-guess approximation that
1521 // will work for the common cases
1522 RenderObject* c = object->container();
1523 if (c->isRenderInline()) {
1524 // A relative positioned inline encloses us. In this case, we also have to determine our
1525 // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned
1526 // inline so that we can obtain the value later.
1527 c->setStaticX(style()->direction() == LTR ? leftOffset(height()) : rightOffset(height()));
1528 c->setStaticY(height());
1529 }
1530
1531 if (object->hasStaticX()) {
1532 if (object->style()->isOriginalDisplayInlineType())
1533 object->setStaticX(style()->direction() == LTR ? leftOffset(height()) : width() - rightOffset(height()));
1534 else
1535 object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1536 }
1537
1538 if (object->hasStaticY())
1539 object->setStaticY(height());
1540 }
1541 iterator.increment();
1542 }
1543 }
1544
skipLeadingWhitespace(InlineBidiResolver & resolver)1545 int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver)
1546 {
1547 int availableWidth = lineWidth(height());
1548 while (!resolver.position().atEnd() && !requiresLineBox(resolver.position())) {
1549 RenderObject* object = resolver.position().obj;
1550 if (object->isFloating()) {
1551 insertFloatingObject(toRenderBox(object));
1552 positionNewFloats();
1553 availableWidth = lineWidth(height());
1554 } else if (object->isPositioned()) {
1555 // FIXME: The math here is actually not really right. It's a best-guess approximation that
1556 // will work for the common cases
1557 RenderObject* c = object->container();
1558 if (c->isRenderInline()) {
1559 // A relative positioned inline encloses us. In this case, we also have to determine our
1560 // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned
1561 // inline so that we can obtain the value later.
1562 c->setStaticX(style()->direction() == LTR ? leftOffset(height()) : rightOffset(height()));
1563 c->setStaticY(height());
1564 }
1565
1566 if (object->hasStaticX()) {
1567 if (object->style()->isOriginalDisplayInlineType())
1568 object->setStaticX(style()->direction() == LTR ? leftOffset(height()) : width() - rightOffset(height()));
1569 else
1570 object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight());
1571 }
1572
1573 if (object->hasStaticY())
1574 object->setStaticY(height());
1575 }
1576 resolver.increment();
1577 }
1578 resolver.commitExplicitEmbedding();
1579 return availableWidth;
1580 }
1581
1582 // This is currently just used for list markers and inline flows that have line boxes. Neither should
1583 // have an effect on whitespace at the start of the line.
shouldSkipWhitespaceAfterStartObject(RenderBlock * block,RenderObject * o)1584 static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o)
1585 {
1586 RenderObject* next = bidiNext(block, o);
1587 if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) {
1588 RenderText* nextText = toRenderText(next);
1589 UChar nextChar = nextText->characters()[0];
1590 if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
1591 addMidpoint(InlineIterator(0, o, 0));
1592 return true;
1593 }
1594 }
1595
1596 return false;
1597 }
1598
fitBelowFloats(int widthToFit,int & availableWidth)1599 void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth)
1600 {
1601 ASSERT(widthToFit > availableWidth);
1602
1603 int floatBottom;
1604 int lastFloatBottom = height();
1605 int newLineWidth = availableWidth;
1606 while (true) {
1607 floatBottom = nextFloatBottomBelow(lastFloatBottom);
1608 if (!floatBottom)
1609 break;
1610
1611 newLineWidth = lineWidth(floatBottom);
1612 lastFloatBottom = floatBottom;
1613 if (newLineWidth >= widthToFit)
1614 break;
1615 }
1616
1617 if (newLineWidth > availableWidth) {
1618 setHeight(lastFloatBottom);
1619 availableWidth = newLineWidth;
1620 }
1621 }
1622
findNextLineBreak(InlineBidiResolver & resolver,EClear * clear)1623 InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, EClear* clear)
1624 {
1625 ASSERT(resolver.position().block == this);
1626
1627 bool appliedStartWidth = resolver.position().pos > 0;
1628
1629 int width = skipLeadingWhitespace(resolver);
1630
1631 int w = 0;
1632 int tmpW = 0;
1633
1634 if (resolver.position().atEnd())
1635 return resolver.position();
1636
1637 // This variable is used only if whitespace isn't set to PRE, and it tells us whether
1638 // or not we are currently ignoring whitespace.
1639 bool ignoringSpaces = false;
1640 InlineIterator ignoreStart;
1641
1642 // This variable tracks whether the very last character we saw was a space. We use
1643 // this to detect when we encounter a second space so we know we have to terminate
1644 // a run.
1645 bool currentCharacterIsSpace = false;
1646 bool currentCharacterIsWS = false;
1647 RenderObject* trailingSpaceObject = 0;
1648
1649 InlineIterator lBreak = resolver.position();
1650
1651 RenderObject *o = resolver.position().obj;
1652 RenderObject *last = o;
1653 unsigned pos = resolver.position().pos;
1654 int nextBreakable = resolver.position().nextBreakablePosition;
1655 bool atStart = true;
1656
1657 bool prevLineBrokeCleanly = previousLineBrokeCleanly;
1658 previousLineBrokeCleanly = false;
1659
1660 bool autoWrapWasEverTrueOnLine = false;
1661 bool floatsFitOnLine = true;
1662
1663 // Firefox and Opera will allow a table cell to grow to fit an image inside it under
1664 // very specific circumstances (in order to match common WinIE renderings).
1665 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
1666 bool allowImagesToBreak = !style()->htmlHacks() || !isTableCell() || !style()->width().isIntrinsicOrAuto();
1667
1668 EWhiteSpace currWS = style()->whiteSpace();
1669 EWhiteSpace lastWS = currWS;
1670 while (o) {
1671 currWS = o->isReplaced() ? o->parent()->style()->whiteSpace() : o->style()->whiteSpace();
1672 lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace();
1673
1674 bool autoWrap = RenderStyle::autoWrap(currWS);
1675 autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;
1676
1677 #if ENABLE(SVG)
1678 bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS);
1679 #else
1680 bool preserveNewline = RenderStyle::preserveNewline(currWS);
1681 #endif
1682
1683 bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS);
1684
1685 if (o->isBR()) {
1686 if (w + tmpW <= width) {
1687 lBreak.obj = o;
1688 lBreak.pos = 0;
1689 lBreak.nextBreakablePosition = -1;
1690 lBreak.increment();
1691
1692 // A <br> always breaks a line, so don't let the line be collapsed
1693 // away. Also, the space at the end of a line with a <br> does not
1694 // get collapsed away. It only does this if the previous line broke
1695 // cleanly. Otherwise the <br> has no effect on whether the line is
1696 // empty or not.
1697 if (prevLineBrokeCleanly)
1698 isLineEmpty = false;
1699 trailingSpaceObject = 0;
1700 previousLineBrokeCleanly = true;
1701
1702 if (!isLineEmpty && clear)
1703 *clear = o->style()->clear();
1704 }
1705 goto end;
1706 }
1707
1708 if (o->isFloatingOrPositioned()) {
1709 // add to special objects...
1710 if (o->isFloating()) {
1711 RenderBox* floatBox = toRenderBox(o);
1712 insertFloatingObject(floatBox);
1713 // check if it fits in the current line.
1714 // If it does, position it now, otherwise, position
1715 // it after moving to next line (in newLine() func)
1716 if (floatsFitOnLine && floatBox->width() + floatBox->marginLeft() + floatBox->marginRight() + w + tmpW <= width) {
1717 positionNewFloats();
1718 width = lineWidth(height());
1719 } else
1720 floatsFitOnLine = false;
1721 } else if (o->isPositioned()) {
1722 // If our original display wasn't an inline type, then we can
1723 // go ahead and determine our static x position now.
1724 bool isInlineType = o->style()->isOriginalDisplayInlineType();
1725 bool needToSetStaticX = o->hasStaticX();
1726 if (o->hasStaticX() && !isInlineType) {
1727 o->setStaticX(o->parent()->style()->direction() == LTR ?
1728 borderLeft() + paddingLeft() :
1729 borderRight() + paddingRight());
1730 needToSetStaticX = false;
1731 }
1732
1733 // If our original display was an INLINE type, then we can go ahead
1734 // and determine our static y position now.
1735 bool needToSetStaticY = o->hasStaticY();
1736 if (o->hasStaticY() && isInlineType) {
1737 o->setStaticY(height());
1738 needToSetStaticY = false;
1739 }
1740
1741 bool needToCreateLineBox = needToSetStaticX || needToSetStaticY;
1742 RenderObject* c = o->container();
1743 if (c->isRenderInline() && (!needToSetStaticX || !needToSetStaticY))
1744 needToCreateLineBox = true;
1745
1746 // If we're ignoring spaces, we have to stop and include this object and
1747 // then start ignoring spaces again.
1748 if (needToCreateLineBox) {
1749 trailingSpaceObject = 0;
1750 ignoreStart.obj = o;
1751 ignoreStart.pos = 0;
1752 if (ignoringSpaces) {
1753 addMidpoint(ignoreStart); // Stop ignoring spaces.
1754 addMidpoint(ignoreStart); // Start ignoring again.
1755 }
1756
1757 }
1758 }
1759 } else if (o->isRenderInline()) {
1760 // Right now, we should only encounter empty inlines here.
1761 ASSERT(!o->firstChild());
1762
1763 RenderBox* flowBox = toRenderBox(o);
1764
1765 // Now that some inline flows have line boxes, if we are already ignoring spaces, we need
1766 // to make sure that we stop to include this object and then start ignoring spaces again.
1767 // If this object is at the start of the line, we need to behave like list markers and
1768 // start ignoring spaces.
1769 if (inlineFlowRequiresLineBox(flowBox)) {
1770 isLineEmpty = false;
1771 if (ignoringSpaces) {
1772 trailingSpaceObject = 0;
1773 addMidpoint(InlineIterator(0, o, 0)); // Stop ignoring spaces.
1774 addMidpoint(InlineIterator(0, o, 0)); // Start ignoring again.
1775 } else if (style()->collapseWhiteSpace() && resolver.position().obj == o
1776 && shouldSkipWhitespaceAfterStartObject(this, o)) {
1777 // Like with list markers, we start ignoring spaces to make sure that any
1778 // additional spaces we see will be discarded.
1779 currentCharacterIsSpace = true;
1780 currentCharacterIsWS = true;
1781 ignoringSpaces = true;
1782 }
1783 }
1784
1785 tmpW += flowBox->marginLeft() + flowBox->borderLeft() + flowBox->paddingLeft() +
1786 flowBox->marginRight() + flowBox->borderRight() + flowBox->paddingRight();
1787 } else if (o->isReplaced()) {
1788 RenderBox* replacedBox = toRenderBox(o);
1789
1790 // Break on replaced elements if either has normal white-space.
1791 if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) {
1792 w += tmpW;
1793 tmpW = 0;
1794 lBreak.obj = o;
1795 lBreak.pos = 0;
1796 lBreak.nextBreakablePosition = -1;
1797 }
1798
1799 if (ignoringSpaces)
1800 addMidpoint(InlineIterator(0, o, 0));
1801
1802 isLineEmpty = false;
1803 ignoringSpaces = false;
1804 currentCharacterIsSpace = false;
1805 currentCharacterIsWS = false;
1806 trailingSpaceObject = 0;
1807
1808 // Optimize for a common case. If we can't find whitespace after the list
1809 // item, then this is all moot. -dwh
1810 if (o->isListMarker() && !static_cast<RenderListMarker*>(o)->isInside()) {
1811 if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o)) {
1812 // Like with inline flows, we start ignoring spaces to make sure that any
1813 // additional spaces we see will be discarded.
1814 currentCharacterIsSpace = true;
1815 currentCharacterIsWS = true;
1816 ignoringSpaces = true;
1817 }
1818 } else
1819 tmpW += replacedBox->width() + replacedBox->marginLeft() + replacedBox->marginRight() + inlineWidth(o);
1820 } else if (o->isText()) {
1821 if (!pos)
1822 appliedStartWidth = false;
1823
1824 RenderText* t = toRenderText(o);
1825
1826 int strlen = t->textLength();
1827 int len = strlen - pos;
1828 const UChar* str = t->characters();
1829
1830 const Font& f = t->style(m_firstLine)->font();
1831
1832 int lastSpace = pos;
1833 int wordSpacing = o->style()->wordSpacing();
1834 int lastSpaceWordSpacing = 0;
1835
1836 int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true);
1837 int charWidth = 0;
1838 bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE;
1839 // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
1840 // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
1841 bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE);
1842 bool midWordBreak = false;
1843 bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap;
1844
1845 if (t->isWordBreak()) {
1846 w += tmpW;
1847 tmpW = 0;
1848 lBreak.obj = o;
1849 lBreak.pos = 0;
1850 lBreak.nextBreakablePosition = -1;
1851 ASSERT(!len);
1852 }
1853
1854 while (len) {
1855 bool previousCharacterIsSpace = currentCharacterIsSpace;
1856 bool previousCharacterIsWS = currentCharacterIsWS;
1857 UChar c = str[pos];
1858 currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n'));
1859
1860 if (!collapseWhiteSpace || !currentCharacterIsSpace)
1861 isLineEmpty = false;
1862
1863 // Check for soft hyphens. Go ahead and ignore them.
1864 if (c == softHyphen) {
1865 if (!ignoringSpaces) {
1866 // Ignore soft hyphens
1867 InlineIterator beforeSoftHyphen;
1868 if (pos)
1869 beforeSoftHyphen = InlineIterator(0, o, pos - 1);
1870 else
1871 beforeSoftHyphen = InlineIterator(0, last, last->isText() ? toRenderText(last)->textLength() - 1 : 0);
1872 // Two consecutive soft hyphens. Avoid overlapping midpoints.
1873 if (sNumMidpoints && smidpoints->at(sNumMidpoints - 1).obj == o && smidpoints->at(sNumMidpoints - 1).pos == pos)
1874 sNumMidpoints--;
1875 else
1876 addMidpoint(beforeSoftHyphen);
1877
1878 // Add the width up to but not including the hyphen.
1879 tmpW += t->width(lastSpace, pos - lastSpace, f, w + tmpW) + lastSpaceWordSpacing;
1880
1881 // For wrapping text only, include the hyphen. We need to ensure it will fit
1882 // on the line if it shows when we break.
1883 if (autoWrap)
1884 tmpW += t->width(pos, 1, f, w + tmpW);
1885
1886 InlineIterator afterSoftHyphen(0, o, pos);
1887 afterSoftHyphen.increment();
1888 addMidpoint(afterSoftHyphen);
1889 }
1890
1891 pos++;
1892 len--;
1893 lastSpaceWordSpacing = 0;
1894 lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice.
1895 continue;
1896 }
1897
1898 bool applyWordSpacing = false;
1899
1900 currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
1901
1902 if ((breakAll || breakWords) && !midWordBreak) {
1903 wrapW += charWidth;
1904 charWidth = t->width(pos, 1, f, w + wrapW);
1905 midWordBreak = w + wrapW + charWidth > width;
1906 }
1907
1908 bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP));
1909
1910 if (betweenWords || midWordBreak) {
1911 bool stoppedIgnoringSpaces = false;
1912 if (ignoringSpaces) {
1913 if (!currentCharacterIsSpace) {
1914 // Stop ignoring spaces and begin at this
1915 // new point.
1916 ignoringSpaces = false;
1917 lastSpaceWordSpacing = 0;
1918 lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
1919 addMidpoint(InlineIterator(0, o, pos));
1920 stoppedIgnoringSpaces = true;
1921 } else {
1922 // Just keep ignoring these spaces.
1923 pos++;
1924 len--;
1925 continue;
1926 }
1927 }
1928
1929 int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
1930 tmpW += additionalTmpW;
1931 if (!appliedStartWidth) {
1932 tmpW += inlineWidth(o, true, false);
1933 appliedStartWidth = true;
1934 }
1935
1936 applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace;
1937
1938 if (!w && autoWrap && tmpW > width)
1939 fitBelowFloats(tmpW, width);
1940
1941 if (autoWrap || breakWords) {
1942 // If we break only after white-space, consider the current character
1943 // as candidate width for this line.
1944 bool lineWasTooWide = false;
1945 if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) {
1946 int charWidth = t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0);
1947 // Check if line is too big even without the extra space
1948 // at the end of the line. If it is not, do nothing.
1949 // If the line needs the extra whitespace to be too long,
1950 // then move the line break to the space and skip all
1951 // additional whitespace.
1952 if (w + tmpW + charWidth > width) {
1953 lineWasTooWide = true;
1954 lBreak.obj = o;
1955 lBreak.pos = pos;
1956 lBreak.nextBreakablePosition = nextBreakable;
1957 skipTrailingWhitespace(lBreak);
1958 }
1959 }
1960 if (lineWasTooWide || w + tmpW > width) {
1961 if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') {
1962 if (!stoppedIgnoringSpaces && pos > 0) {
1963 // We need to stop right before the newline and then start up again.
1964 addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1965 addMidpoint(InlineIterator(0, o, pos)); // Start
1966 }
1967 lBreak.increment();
1968 previousLineBrokeCleanly = true;
1969 }
1970 goto end; // Didn't fit. Jump to the end.
1971 } else {
1972 if (!betweenWords || (midWordBreak && !autoWrap))
1973 tmpW -= additionalTmpW;
1974 if (pos > 0 && str[pos-1] == softHyphen)
1975 // Subtract the width of the soft hyphen out since we fit on a line.
1976 tmpW -= t->width(pos-1, 1, f, w+tmpW);
1977 }
1978 }
1979
1980 if (c == '\n' && preserveNewline) {
1981 if (!stoppedIgnoringSpaces && pos > 0) {
1982 // We need to stop right before the newline and then start up again.
1983 addMidpoint(InlineIterator(0, o, pos - 1)); // Stop
1984 addMidpoint(InlineIterator(0, o, pos)); // Start
1985 }
1986 lBreak.obj = o;
1987 lBreak.pos = pos;
1988 lBreak.nextBreakablePosition = nextBreakable;
1989 lBreak.increment();
1990 previousLineBrokeCleanly = true;
1991 return lBreak;
1992 }
1993
1994 if (autoWrap && betweenWords) {
1995 w += tmpW;
1996 wrapW = 0;
1997 tmpW = 0;
1998 lBreak.obj = o;
1999 lBreak.pos = pos;
2000 lBreak.nextBreakablePosition = nextBreakable;
2001 // Auto-wrapping text should not wrap in the middle of a word once it has had an
2002 // opportunity to break after a word.
2003 breakWords = false;
2004 }
2005
2006 if (midWordBreak) {
2007 // Remember this as a breakable position in case
2008 // adding the end width forces a break.
2009 lBreak.obj = o;
2010 lBreak.pos = pos;
2011 lBreak.nextBreakablePosition = nextBreakable;
2012 midWordBreak &= (breakWords || breakAll);
2013 }
2014
2015 if (betweenWords) {
2016 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
2017 lastSpace = pos;
2018 }
2019
2020 if (!ignoringSpaces && o->style()->collapseWhiteSpace()) {
2021 // If we encounter a newline, or if we encounter a
2022 // second space, we need to go ahead and break up this
2023 // run and enter a mode where we start collapsing spaces.
2024 if (currentCharacterIsSpace && previousCharacterIsSpace) {
2025 ignoringSpaces = true;
2026
2027 // We just entered a mode where we are ignoring
2028 // spaces. Create a midpoint to terminate the run
2029 // before the second space.
2030 addMidpoint(ignoreStart);
2031 }
2032 }
2033 } else if (ignoringSpaces) {
2034 // Stop ignoring spaces and begin at this
2035 // new point.
2036 ignoringSpaces = false;
2037 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
2038 lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
2039 addMidpoint(InlineIterator(0, o, pos));
2040 }
2041
2042 if (currentCharacterIsSpace && !previousCharacterIsSpace) {
2043 ignoreStart.obj = o;
2044 ignoreStart.pos = pos;
2045 }
2046
2047 if (!currentCharacterIsWS && previousCharacterIsWS) {
2048 if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) {
2049 lBreak.obj = o;
2050 lBreak.pos = pos;
2051 lBreak.nextBreakablePosition = nextBreakable;
2052 }
2053 }
2054
2055 if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces)
2056 trailingSpaceObject = o;
2057 else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace)
2058 trailingSpaceObject = 0;
2059
2060 pos++;
2061 len--;
2062 atStart = false;
2063 }
2064
2065 // IMPORTANT: pos is > length here!
2066 if (!ignoringSpaces)
2067 tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing;
2068 tmpW += inlineWidth(o, !appliedStartWidth, true);
2069 } else
2070 ASSERT_NOT_REACHED();
2071
2072 RenderObject* next = bidiNext(this, o);
2073 bool checkForBreak = autoWrap;
2074 if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP)
2075 checkForBreak = true;
2076 else if (next && o->isText() && next->isText() && !next->isBR()) {
2077 if (autoWrap || (next->style()->autoWrap())) {
2078 if (currentCharacterIsSpace)
2079 checkForBreak = true;
2080 else {
2081 checkForBreak = false;
2082 RenderText* nextText = toRenderText(next);
2083 if (nextText->textLength()) {
2084 UChar c = nextText->characters()[0];
2085 if (c == ' ' || c == '\t' || (c == '\n' && !shouldPreserveNewline(next)))
2086 // If the next item on the line is text, and if we did not end with
2087 // a space, then the next text run continues our word (and so it needs to
2088 // keep adding to |tmpW|. Just update and continue.
2089 checkForBreak = true;
2090 } else if (nextText->isWordBreak())
2091 checkForBreak = true;
2092 bool willFitOnLine = w + tmpW <= width;
2093 if (!willFitOnLine && !w) {
2094 fitBelowFloats(tmpW, width);
2095 willFitOnLine = tmpW <= width;
2096 }
2097 bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine;
2098 if (canPlaceOnLine && checkForBreak) {
2099 w += tmpW;
2100 tmpW = 0;
2101 lBreak.obj = next;
2102 lBreak.pos = 0;
2103 lBreak.nextBreakablePosition = -1;
2104 }
2105 }
2106 }
2107 }
2108
2109 if (checkForBreak && (w + tmpW > width)) {
2110 // if we have floats, try to get below them.
2111 if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace())
2112 trailingSpaceObject = 0;
2113
2114 if (w)
2115 goto end;
2116
2117 fitBelowFloats(tmpW, width);
2118
2119 // |width| may have been adjusted because we got shoved down past a float (thus
2120 // giving us more room), so we need to retest, and only jump to
2121 // the end label if we still don't fit on the line. -dwh
2122 if (w + tmpW > width)
2123 goto end;
2124 }
2125
2126 if (!o->isFloatingOrPositioned()) {
2127 last = o;
2128 if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || static_cast<RenderListMarker*>(last)->isInside())) {
2129 w += tmpW;
2130 tmpW = 0;
2131 lBreak.obj = next;
2132 lBreak.pos = 0;
2133 lBreak.nextBreakablePosition = -1;
2134 }
2135 }
2136
2137 o = next;
2138 nextBreakable = -1;
2139
2140 // Clear out our character space bool, since inline <pre>s don't collapse whitespace
2141 // with adjacent inline normal/nowrap spans.
2142 if (!collapseWhiteSpace)
2143 currentCharacterIsSpace = false;
2144
2145 pos = 0;
2146 atStart = false;
2147 }
2148
2149
2150 if (w + tmpW <= width || lastWS == NOWRAP) {
2151 lBreak.obj = 0;
2152 lBreak.pos = 0;
2153 lBreak.nextBreakablePosition = -1;
2154 }
2155
2156 end:
2157
2158 if (lBreak == resolver.position() && !lBreak.obj->isBR()) {
2159 // we just add as much as possible
2160 if (style()->whiteSpace() == PRE) {
2161 // FIXME: Don't really understand this case.
2162 if (pos != 0) {
2163 lBreak.obj = o;
2164 lBreak.pos = pos - 1;
2165 } else {
2166 lBreak.obj = last;
2167 lBreak.pos = last->isText() ? last->length() : 0;
2168 lBreak.nextBreakablePosition = -1;
2169 }
2170 } else if (lBreak.obj) {
2171 if (last != o && !last->isListMarker()) {
2172 // better to break between object boundaries than in the middle of a word (except for list markers)
2173 lBreak.obj = o;
2174 lBreak.pos = 0;
2175 lBreak.nextBreakablePosition = -1;
2176 } else {
2177 // Don't ever break in the middle of a word if we can help it.
2178 // There's no room at all. We just have to be on this line,
2179 // even though we'll spill out.
2180 lBreak.obj = o;
2181 lBreak.pos = pos;
2182 lBreak.nextBreakablePosition = -1;
2183 }
2184 }
2185 }
2186
2187 // make sure we consume at least one char/object.
2188 if (lBreak == resolver.position())
2189 lBreak.increment();
2190
2191 // Sanity check our midpoints.
2192 checkMidpoints(lBreak);
2193
2194 if (trailingSpaceObject) {
2195 // This object is either going to be part of the last midpoint, or it is going
2196 // to be the actual endpoint. In both cases we just decrease our pos by 1 level to
2197 // exclude the space, allowing it to - in effect - collapse into the newline.
2198 if (sNumMidpoints%2==1) {
2199 InlineIterator* midpoints = smidpoints->data();
2200 midpoints[sNumMidpoints-1].pos--;
2201 }
2202 //else if (lBreak.pos > 0)
2203 // lBreak.pos--;
2204 else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
2205 // Add a new end midpoint that stops right at the very end.
2206 RenderText* text = toRenderText(trailingSpaceObject);
2207 unsigned length = text->textLength();
2208 unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
2209 InlineIterator endMid(0, trailingSpaceObject, pos);
2210 addMidpoint(endMid);
2211 }
2212 }
2213
2214 // We might have made lBreak an iterator that points past the end
2215 // of the object. Do this adjustment to make it point to the start
2216 // of the next object instead to avoid confusing the rest of the
2217 // code.
2218 if (lBreak.pos > 0) {
2219 lBreak.pos--;
2220 lBreak.increment();
2221 }
2222
2223 if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) {
2224 // For soft hyphens on line breaks, we have to chop out the midpoints that made us
2225 // ignore the hyphen so that it will render at the end of the line.
2226 UChar c = toRenderText(lBreak.obj)->characters()[lBreak.pos-1];
2227 if (c == softHyphen)
2228 chopMidpointsAt(lBreak.obj, lBreak.pos-2);
2229 }
2230
2231 return lBreak;
2232 }
2233
checkLinesForOverflow()2234 void RenderBlock::checkLinesForOverflow()
2235 {
2236 m_overflowWidth = width();
2237 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2238 m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft);
2239 m_overflowTop = min(curr->topOverflow(), m_overflowTop);
2240 m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth);
2241 m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight);
2242 }
2243 }
2244
deleteEllipsisLineBoxes()2245 void RenderBlock::deleteEllipsisLineBoxes()
2246 {
2247 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
2248 curr->clearTruncation();
2249 }
2250
checkLinesForTextOverflow()2251 void RenderBlock::checkLinesForTextOverflow()
2252 {
2253 // Determine the width of the ellipsis using the current font.
2254 // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
2255 TextRun ellipsisRun(&horizontalEllipsis, 1);
2256 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
2257 const Font& firstLineFont = firstLineStyle()->font();
2258 const Font& font = style()->font();
2259 int firstLineEllipsisWidth = firstLineFont.width(ellipsisRun);
2260 int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(ellipsisRun);
2261
2262 // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
2263 // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
2264 // check the left edge of the line box to see if it is less
2265 // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
2266 bool ltr = style()->direction() == LTR;
2267 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
2268 int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos());
2269 int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos();
2270 if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) {
2271 // This line spills out of our box in the appropriate direction. Now we need to see if the line
2272 // can be truncated. In order for truncation to be possible, the line must have sufficient space to
2273 // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
2274 // space.
2275 int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
2276 if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
2277 curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width);
2278 }
2279 }
2280 }
2281
2282 }
2283