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