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