1 /* 2 * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20 #include "config.h" 21 #include "core/rendering/RootInlineBox.h" 22 23 #include "core/dom/Document.h" 24 #include "core/rendering/EllipsisBox.h" 25 #include "core/rendering/HitTestResult.h" 26 #include "core/rendering/InlineTextBox.h" 27 #include "core/rendering/PaintInfo.h" 28 #include "core/rendering/RenderBlockFlow.h" 29 #include "core/rendering/RenderFlowThread.h" 30 #include "core/rendering/RenderInline.h" 31 #include "core/rendering/RenderView.h" 32 #include "core/rendering/VerticalPositionCache.h" 33 #include "platform/text/BidiResolver.h" 34 #include "wtf/unicode/Unicode.h" 35 36 using namespace std; 37 38 namespace WebCore { 39 40 struct SameSizeAsRootInlineBox : public InlineFlowBox { 41 unsigned variables[5]; 42 void* pointers[4]; 43 }; 44 45 COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small); 46 47 typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap; 48 static EllipsisBoxMap* gEllipsisBoxMap = 0; 49 50 RootInlineBox::RootInlineBox(RenderBlockFlow& block) 51 : InlineFlowBox(block) 52 , m_lineBreakPos(0) 53 , m_lineBreakObj(0) 54 , m_lineTop(0) 55 , m_lineBottom(0) 56 , m_lineTopWithLeading(0) 57 , m_lineBottomWithLeading(0) 58 { 59 setIsHorizontal(block.isHorizontalWritingMode()); 60 } 61 62 63 void RootInlineBox::destroy() 64 { 65 detachEllipsisBox(); 66 InlineFlowBox::destroy(); 67 } 68 69 void RootInlineBox::detachEllipsisBox() 70 { 71 if (hasEllipsisBox()) { 72 EllipsisBox* box = gEllipsisBoxMap->take(this); 73 box->setParent(0); 74 box->destroy(); 75 setHasEllipsisBox(false); 76 } 77 } 78 79 RenderLineBoxList* RootInlineBox::rendererLineBoxes() const 80 { 81 return block().lineBoxes(); 82 } 83 84 void RootInlineBox::clearTruncation() 85 { 86 if (hasEllipsisBox()) { 87 detachEllipsisBox(); 88 InlineFlowBox::clearTruncation(); 89 } 90 } 91 92 bool RootInlineBox::isHyphenated() const 93 { 94 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 95 if (box->isInlineTextBox()) { 96 if (toInlineTextBox(box)->hasHyphen()) 97 return true; 98 } 99 } 100 101 return false; 102 } 103 104 int RootInlineBox::baselinePosition(FontBaseline baselineType) const 105 { 106 return boxModelObject()->baselinePosition(baselineType, isFirstLineStyle(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 107 } 108 109 LayoutUnit RootInlineBox::lineHeight() const 110 { 111 return boxModelObject()->lineHeight(isFirstLineStyle(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 112 } 113 114 bool RootInlineBox::lineCanAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth) 115 { 116 // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room. 117 int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge; 118 if (logicalWidth() - delta < ellipsisWidth) 119 return false; 120 121 // Next iterate over all the line boxes on the line. If we find a replaced element that intersects 122 // then we refuse to accommodate the ellipsis. Otherwise we're ok. 123 return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth); 124 } 125 126 float RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, 127 InlineBox* markupBox) 128 { 129 // Create an ellipsis box. 130 EllipsisBox* ellipsisBox = new EllipsisBox(renderer(), ellipsisStr, this, 131 ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(), 132 x(), y(), !prevRootBox(), isHorizontal(), markupBox); 133 134 if (!gEllipsisBoxMap) 135 gEllipsisBoxMap = new EllipsisBoxMap(); 136 gEllipsisBoxMap->add(this, ellipsisBox); 137 setHasEllipsisBox(true); 138 139 // FIXME: Do we need an RTL version of this? 140 if (ltr && (logicalLeft() + logicalWidth() + ellipsisWidth) <= blockRightEdge) { 141 ellipsisBox->setLogicalLeft(logicalLeft() + logicalWidth()); 142 return logicalWidth() + ellipsisWidth; 143 } 144 145 // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) 146 // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being 147 // truncated). 148 bool foundBox = false; 149 float truncatedWidth = 0; 150 float position = placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, truncatedWidth, foundBox); 151 ellipsisBox->setLogicalLeft(position); 152 return truncatedWidth; 153 } 154 155 float RootInlineBox::placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) 156 { 157 float result = InlineFlowBox::placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, truncatedWidth, foundBox); 158 if (result == -1) { 159 result = ltr ? blockRightEdge - ellipsisWidth : blockLeftEdge; 160 truncatedWidth = blockRightEdge - blockLeftEdge; 161 } 162 return result; 163 } 164 165 void RootInlineBox::paintEllipsisBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) const 166 { 167 if (hasEllipsisBox() && paintInfo.shouldPaintWithinRoot(&renderer()) && renderer().style()->visibility() == VISIBLE 168 && paintInfo.phase == PaintPhaseForeground) 169 ellipsisBox()->paint(paintInfo, paintOffset, lineTop, lineBottom); 170 } 171 172 void RootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 173 { 174 InlineFlowBox::paint(paintInfo, paintOffset, lineTop, lineBottom); 175 paintEllipsisBox(paintInfo, paintOffset, lineTop, lineBottom); 176 } 177 178 bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 179 { 180 if (hasEllipsisBox() && visibleToHitTestRequest(request)) { 181 if (ellipsisBox()->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { 182 renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); 183 return true; 184 } 185 } 186 return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom); 187 } 188 189 void RootInlineBox::adjustPosition(float dx, float dy) 190 { 191 InlineFlowBox::adjustPosition(dx, dy); 192 LayoutUnit blockDirectionDelta = isHorizontal() ? dy : dx; // The block direction delta is a LayoutUnit. 193 m_lineTop += blockDirectionDelta; 194 m_lineBottom += blockDirectionDelta; 195 m_lineTopWithLeading += blockDirectionDelta; 196 m_lineBottomWithLeading += blockDirectionDelta; 197 if (hasEllipsisBox()) 198 ellipsisBox()->adjustPosition(dx, dy); 199 } 200 201 void RootInlineBox::childRemoved(InlineBox* box) 202 { 203 if (&box->renderer() == m_lineBreakObj) 204 setLineBreakInfo(0, 0, BidiStatus()); 205 206 for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == &box->renderer(); prev = prev->prevRootBox()) { 207 prev->setLineBreakInfo(0, 0, BidiStatus()); 208 prev->markDirty(); 209 } 210 } 211 212 LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) 213 { 214 // SVG will handle vertical alignment on its own. 215 if (isSVGRootInlineBox()) 216 return 0; 217 218 LayoutUnit maxPositionTop = 0; 219 LayoutUnit maxPositionBottom = 0; 220 int maxAscent = 0; 221 int maxDescent = 0; 222 bool setMaxAscent = false; 223 bool setMaxDescent = false; 224 225 // Figure out if we're in no-quirks mode. 226 bool noQuirksMode = renderer().document().inNoQuirksMode(); 227 228 m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline; 229 230 computeLogicalBoxHeights(this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, 231 textBoxDataMap, baselineType(), verticalPositionCache); 232 233 if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom)) 234 adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); 235 236 LayoutUnit maxHeight = maxAscent + maxDescent; 237 LayoutUnit lineTop = heightOfBlock; 238 LayoutUnit lineBottom = heightOfBlock; 239 LayoutUnit lineTopIncludingMargins = heightOfBlock; 240 LayoutUnit lineBottomIncludingMargins = heightOfBlock; 241 bool setLineTop = false; 242 bool hasAnnotationsBefore = false; 243 bool hasAnnotationsAfter = false; 244 placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, noQuirksMode, lineTop, lineBottom, setLineTop, 245 lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType()); 246 m_hasAnnotationsBefore = hasAnnotationsBefore; 247 m_hasAnnotationsAfter = hasAnnotationsAfter; 248 249 maxHeight = max<LayoutUnit>(0, maxHeight); // FIXME: Is this really necessary? 250 251 setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock, heightOfBlock + maxHeight); 252 if (block().view()->layoutState()->isPaginated()) 253 setPaginatedLineWidth(block().availableLogicalWidthForContent()); 254 255 LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment(); 256 if (annotationsAdjustment) { 257 // FIXME: Need to handle pagination here. We might have to move to the next page/column as a result of the 258 // ruby expansion. 259 adjustBlockDirectionPosition(annotationsAdjustment.toFloat()); 260 heightOfBlock += annotationsAdjustment; 261 } 262 263 return heightOfBlock + maxHeight; 264 } 265 266 float RootInlineBox::maxLogicalTop() const 267 { 268 float maxLogicalTop = 0; 269 computeMaxLogicalTop(maxLogicalTop); 270 return maxLogicalTop; 271 } 272 273 LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const 274 { 275 LayoutUnit result = 0; 276 277 if (!renderer().style()->isFlippedLinesWritingMode()) { 278 // Annotations under the previous line may push us down. 279 if (prevRootBox() && prevRootBox()->hasAnnotationsAfter()) 280 result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop()); 281 282 if (!hasAnnotationsBefore()) 283 return result; 284 285 // Annotations over this line may push us further down. 286 LayoutUnit highestAllowedPosition = prevRootBox() ? min(prevRootBox()->lineBottom(), lineTop()) + result : static_cast<LayoutUnit>(block().borderBefore()); 287 result = computeOverAnnotationAdjustment(highestAllowedPosition); 288 } else { 289 // Annotations under this line may push us up. 290 if (hasAnnotationsBefore()) 291 result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : static_cast<LayoutUnit>(block().borderBefore())); 292 293 if (!prevRootBox() || !prevRootBox()->hasAnnotationsAfter()) 294 return result; 295 296 // We have to compute the expansion for annotations over the previous line to see how much we should move. 297 LayoutUnit lowestAllowedPosition = max(prevRootBox()->lineBottom(), lineTop()) - result; 298 result = prevRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); 299 } 300 301 return result; 302 } 303 304 GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 305 LayoutUnit selTop, LayoutUnit selHeight, const PaintInfo* paintInfo) 306 { 307 RenderObject::SelectionState lineState = selectionState(); 308 309 bool leftGap, rightGap; 310 block().getSelectionGapInfo(lineState, leftGap, rightGap); 311 312 GapRects result; 313 314 InlineBox* firstBox = firstSelectedBox(); 315 InlineBox* lastBox = lastSelectedBox(); 316 if (leftGap) { 317 result.uniteLeft(block().logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, 318 &firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop, selHeight, paintInfo)); 319 } 320 if (rightGap) { 321 result.uniteRight(block().logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, 322 &lastBox->parent()->renderer(), lastBox->logicalRight(), selTop, selHeight, paintInfo)); 323 } 324 325 // When dealing with bidi text, a non-contiguous selection region is possible. 326 // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out 327 // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the 328 // selection will look like (underline denotes selection): 329 // |aaa|bbb|AAA| 330 // ___ _ 331 // We can see that the |bbb| run is not part of the selection while the runs around it are. 332 if (firstBox && firstBox != lastBox) { 333 // Now fill in any gaps on the line that occurred between two selected elements. 334 LayoutUnit lastLogicalLeft = firstBox->logicalRight(); 335 bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone; 336 for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { 337 if (box->selectionState() != RenderObject::SelectionNone) { 338 LayoutRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight); 339 logicalRect.move(renderer().isHorizontalWritingMode() ? offsetFromRootBlock : LayoutSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); 340 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); 341 if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) { 342 if (paintInfo && box->parent()->renderer().style()->visibility() == VISIBLE) 343 paintInfo->context->fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor()); 344 // VisibleSelection may be non-contiguous, see comment above. 345 result.uniteCenter(gapRect); 346 } 347 lastLogicalLeft = box->logicalRight(); 348 } 349 if (box == lastBox) 350 break; 351 isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone; 352 } 353 } 354 355 return result; 356 } 357 358 RenderObject::SelectionState RootInlineBox::selectionState() 359 { 360 // Walk over all of the selected boxes. 361 RenderObject::SelectionState state = RenderObject::SelectionNone; 362 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 363 RenderObject::SelectionState boxState = box->selectionState(); 364 if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) || 365 (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart)) 366 state = RenderObject::SelectionBoth; 367 else if (state == RenderObject::SelectionNone || 368 ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) && 369 (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside))) 370 state = boxState; 371 else if (boxState == RenderObject::SelectionNone && state == RenderObject::SelectionStart) { 372 // We are past the end of the selection. 373 state = RenderObject::SelectionBoth; 374 } 375 if (state == RenderObject::SelectionBoth) 376 break; 377 } 378 379 return state; 380 } 381 382 InlineBox* RootInlineBox::firstSelectedBox() 383 { 384 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 385 if (box->selectionState() != RenderObject::SelectionNone) 386 return box; 387 } 388 389 return 0; 390 } 391 392 InlineBox* RootInlineBox::lastSelectedBox() 393 { 394 for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) { 395 if (box->selectionState() != RenderObject::SelectionNone) 396 return box; 397 } 398 399 return 0; 400 } 401 402 LayoutUnit RootInlineBox::selectionTop() const 403 { 404 LayoutUnit selectionTop = m_lineTop; 405 406 if (m_hasAnnotationsBefore) 407 selectionTop -= !renderer().style()->isFlippedLinesWritingMode() ? computeOverAnnotationAdjustment(m_lineTop) : computeUnderAnnotationAdjustment(m_lineTop); 408 409 if (renderer().style()->isFlippedLinesWritingMode() || !prevRootBox()) 410 return selectionTop; 411 412 LayoutUnit prevBottom = prevRootBox()->selectionBottom(); 413 if (prevBottom < selectionTop && block().containsFloats()) { 414 // This line has actually been moved further down, probably from a large line-height, but possibly because the 415 // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous 416 // line's bottom if the offsets are greater on both sides. 417 LayoutUnit prevLeft = block().logicalLeftOffsetForLine(prevBottom, false); 418 LayoutUnit prevRight = block().logicalRightOffsetForLine(prevBottom, false); 419 LayoutUnit newLeft = block().logicalLeftOffsetForLine(selectionTop, false); 420 LayoutUnit newRight = block().logicalRightOffsetForLine(selectionTop, false); 421 if (prevLeft > newLeft || prevRight < newRight) 422 return selectionTop; 423 } 424 425 return prevBottom; 426 } 427 428 LayoutUnit RootInlineBox::selectionTopAdjustedForPrecedingBlock() const 429 { 430 LayoutUnit top = selectionTop(); 431 432 RenderObject::SelectionState blockSelectionState = root().block().selectionState(); 433 if (blockSelectionState != RenderObject::SelectionInside && blockSelectionState != RenderObject::SelectionEnd) 434 return top; 435 436 LayoutSize offsetToBlockBefore; 437 if (RenderBlock* block = root().block().blockBeforeWithinSelectionRoot(offsetToBlockBefore)) { 438 if (RootInlineBox* lastLine = block->lastRootBox()) { 439 RenderObject::SelectionState lastLineSelectionState = lastLine->selectionState(); 440 if (lastLineSelectionState != RenderObject::SelectionInside && lastLineSelectionState != RenderObject::SelectionStart) 441 return top; 442 443 LayoutUnit lastLineSelectionBottom = lastLine->selectionBottom() + offsetToBlockBefore.height(); 444 top = max(top, lastLineSelectionBottom); 445 } 446 } 447 448 return top; 449 } 450 451 LayoutUnit RootInlineBox::selectionBottom() const 452 { 453 LayoutUnit selectionBottom = m_lineBottom; 454 455 if (m_hasAnnotationsAfter) 456 selectionBottom += !renderer().style()->isFlippedLinesWritingMode() ? computeUnderAnnotationAdjustment(m_lineBottom) : computeOverAnnotationAdjustment(m_lineBottom); 457 458 if (!renderer().style()->isFlippedLinesWritingMode() || !nextRootBox()) 459 return selectionBottom; 460 461 LayoutUnit nextTop = nextRootBox()->selectionTop(); 462 if (nextTop > selectionBottom && block().containsFloats()) { 463 // The next line has actually been moved further over, probably from a large line-height, but possibly because the 464 // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the next 465 // line's top if the offsets are greater on both sides. 466 LayoutUnit nextLeft = block().logicalLeftOffsetForLine(nextTop, false); 467 LayoutUnit nextRight = block().logicalRightOffsetForLine(nextTop, false); 468 LayoutUnit newLeft = block().logicalLeftOffsetForLine(selectionBottom, false); 469 LayoutUnit newRight = block().logicalRightOffsetForLine(selectionBottom, false); 470 if (nextLeft > newLeft || nextRight < newRight) 471 return selectionBottom; 472 } 473 474 return nextTop; 475 } 476 477 int RootInlineBox::blockDirectionPointInLine() const 478 { 479 return !block().style()->isFlippedBlocksWritingMode() ? max(lineTop(), selectionTop()) : min(lineBottom(), selectionBottom()); 480 } 481 482 RenderBlockFlow& RootInlineBox::block() const 483 { 484 return toRenderBlockFlow(renderer()); 485 } 486 487 static bool isEditableLeaf(InlineBox* leaf) 488 { 489 return leaf && leaf->renderer().node() && leaf->renderer().node()->rendererIsEditable(); 490 } 491 492 InlineBox* RootInlineBox::closestLeafChildForPoint(const IntPoint& pointInContents, bool onlyEditableLeaves) 493 { 494 return closestLeafChildForLogicalLeftPosition(block().isHorizontalWritingMode() ? pointInContents.x() : pointInContents.y(), onlyEditableLeaves); 495 } 496 497 InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPosition, bool onlyEditableLeaves) 498 { 499 InlineBox* firstLeaf = firstLeafChild(); 500 InlineBox* lastLeaf = lastLeafChild(); 501 502 if (firstLeaf != lastLeaf) { 503 if (firstLeaf->isLineBreak()) 504 firstLeaf = firstLeaf->nextLeafChildIgnoringLineBreak(); 505 else if (lastLeaf->isLineBreak()) 506 lastLeaf = lastLeaf->prevLeafChildIgnoringLineBreak(); 507 } 508 509 if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) 510 return firstLeaf; 511 512 // Avoid returning a list marker when possible. 513 if (leftPosition <= firstLeaf->logicalLeft() && !firstLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) 514 // The leftPosition coordinate is less or equal to left edge of the firstLeaf. 515 // Return it. 516 return firstLeaf; 517 518 if (leftPosition >= lastLeaf->logicalRight() && !lastLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) 519 // The leftPosition coordinate is greater or equal to right edge of the lastLeaf. 520 // Return it. 521 return lastLeaf; 522 523 InlineBox* closestLeaf = 0; 524 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChildIgnoringLineBreak()) { 525 if (!leaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { 526 closestLeaf = leaf; 527 if (leftPosition < leaf->logicalRight()) 528 // The x coordinate is less than the right edge of the box. 529 // Return it. 530 return leaf; 531 } 532 } 533 534 return closestLeaf ? closestLeaf : lastLeaf; 535 } 536 537 BidiStatus RootInlineBox::lineBreakBidiStatus() const 538 { 539 return BidiStatus(static_cast<WTF::Unicode::Direction>(m_lineBreakBidiStatusEor), static_cast<WTF::Unicode::Direction>(m_lineBreakBidiStatusLastStrong), static_cast<WTF::Unicode::Direction>(m_lineBreakBidiStatusLast), m_lineBreakContext); 540 } 541 542 void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status) 543 { 544 // When setting lineBreakObj, the RenderObject must not be a RenderInline 545 // with no line boxes, otherwise all sorts of invariants are broken later. 546 // This has security implications because if the RenderObject does not 547 // point to at least one line box, then that RenderInline can be deleted 548 // later without resetting the lineBreakObj, leading to use-after-free. 549 ASSERT_WITH_SECURITY_IMPLICATION(!obj || obj->isText() || !(obj->isRenderInline() && obj->isBox() && !toRenderBox(obj)->inlineBoxWrapper())); 550 551 m_lineBreakObj = obj; 552 m_lineBreakPos = breakPos; 553 m_lineBreakBidiStatusEor = status.eor; 554 m_lineBreakBidiStatusLastStrong = status.lastStrong; 555 m_lineBreakBidiStatusLast = status.last; 556 m_lineBreakContext = status.context; 557 } 558 559 EllipsisBox* RootInlineBox::ellipsisBox() const 560 { 561 if (!hasEllipsisBox()) 562 return 0; 563 return gEllipsisBoxMap->get(this); 564 } 565 566 void RootInlineBox::removeLineBoxFromRenderObject() 567 { 568 block().lineBoxes()->removeLineBox(this); 569 } 570 571 void RootInlineBox::extractLineBoxFromRenderObject() 572 { 573 block().lineBoxes()->extractLineBox(this); 574 } 575 576 void RootInlineBox::attachLineBoxToRenderObject() 577 { 578 block().lineBoxes()->attachLineBox(this); 579 } 580 581 LayoutRect RootInlineBox::paddedLayoutOverflowRect(LayoutUnit endPadding) const 582 { 583 LayoutRect lineLayoutOverflow = layoutOverflowRect(lineTop(), lineBottom()); 584 if (!endPadding) 585 return lineLayoutOverflow; 586 587 if (isHorizontal()) { 588 if (isLeftToRightDirection()) 589 lineLayoutOverflow.shiftMaxXEdgeTo(max<LayoutUnit>(lineLayoutOverflow.maxX(), logicalRight() + endPadding)); 590 else 591 lineLayoutOverflow.shiftXEdgeTo(min<LayoutUnit>(lineLayoutOverflow.x(), logicalLeft() - endPadding)); 592 } else { 593 if (isLeftToRightDirection()) 594 lineLayoutOverflow.shiftMaxYEdgeTo(max<LayoutUnit>(lineLayoutOverflow.maxY(), logicalRight() + endPadding)); 595 else 596 lineLayoutOverflow.shiftYEdgeTo(min<LayoutUnit>(lineLayoutOverflow.y(), logicalLeft() - endPadding)); 597 } 598 599 return lineLayoutOverflow; 600 } 601 602 static void setAscentAndDescent(int& ascent, int& descent, int newAscent, int newDescent, bool& ascentDescentSet) 603 { 604 if (!ascentDescentSet) { 605 ascentDescentSet = true; 606 ascent = newAscent; 607 descent = newDescent; 608 } else { 609 ascent = max(ascent, newAscent); 610 descent = max(descent, newDescent); 611 } 612 } 613 614 void RootInlineBox::ascentAndDescentForBox(InlineBox* box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent, 615 bool& affectsAscent, bool& affectsDescent) const 616 { 617 bool ascentDescentSet = false; 618 619 // Replaced boxes will return 0 for the line-height if line-box-contain says they are 620 // not to be included. 621 if (box->renderer().isReplaced()) { 622 if (renderer().style(isFirstLineStyle())->lineBoxContain() & LineBoxContainReplaced) { 623 ascent = box->baselinePosition(baselineType()); 624 descent = box->lineHeight() - ascent; 625 626 // Replaced elements always affect both the ascent and descent. 627 affectsAscent = true; 628 affectsDescent = true; 629 } 630 return; 631 } 632 633 Vector<const SimpleFontData*>* usedFonts = 0; 634 GlyphOverflow* glyphOverflow = 0; 635 if (box->isText()) { 636 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(toInlineTextBox(box)); 637 usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first; 638 glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second; 639 } 640 641 bool includeLeading = includeLeadingForBox(box); 642 bool includeFont = includeFontForBox(box); 643 644 bool setUsedFont = false; 645 bool setUsedFontWithLeading = false; 646 647 if (usedFonts && !usedFonts->isEmpty() && (includeFont || (box->renderer().style(isFirstLineStyle())->lineHeight().isNegative() && includeLeading))) { 648 usedFonts->append(box->renderer().style(isFirstLineStyle())->font().primaryFont()); 649 for (size_t i = 0; i < usedFonts->size(); ++i) { 650 const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics(); 651 int usedFontAscent = fontMetrics.ascent(baselineType()); 652 int usedFontDescent = fontMetrics.descent(baselineType()); 653 int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2; 654 int usedFontAscentAndLeading = usedFontAscent + halfLeading; 655 int usedFontDescentAndLeading = fontMetrics.lineSpacing() - usedFontAscentAndLeading; 656 if (includeFont) { 657 setAscentAndDescent(ascent, descent, usedFontAscent, usedFontDescent, ascentDescentSet); 658 setUsedFont = true; 659 } 660 if (includeLeading) { 661 setAscentAndDescent(ascent, descent, usedFontAscentAndLeading, usedFontDescentAndLeading, ascentDescentSet); 662 setUsedFontWithLeading = true; 663 } 664 if (!affectsAscent) 665 affectsAscent = usedFontAscent - box->logicalTop() > 0; 666 if (!affectsDescent) 667 affectsDescent = usedFontDescent + box->logicalTop() > 0; 668 } 669 } 670 671 // If leading is included for the box, then we compute that box. 672 if (includeLeading && !setUsedFontWithLeading) { 673 int ascentWithLeading = box->baselinePosition(baselineType()); 674 int descentWithLeading = box->lineHeight() - ascentWithLeading; 675 setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading, ascentDescentSet); 676 677 // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. 678 // If the top of our font box relative to the root box baseline is above the root box baseline, then 679 // we are contributing to the maxAscent value. Descent is similar. If any part of our font box is below 680 // the root box's baseline, then we contribute to the maxDescent value. 681 affectsAscent = ascentWithLeading - box->logicalTop() > 0; 682 affectsDescent = descentWithLeading + box->logicalTop() > 0; 683 } 684 685 if (includeFontForBox(box) && !setUsedFont) { 686 int fontAscent = box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType()); 687 int fontDescent = box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType()); 688 setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet); 689 affectsAscent = fontAscent - box->logicalTop() > 0; 690 affectsDescent = fontDescent + box->logicalTop() > 0; 691 } 692 693 if (includeGlyphsForBox(box) && glyphOverflow && glyphOverflow->computeBounds) { 694 setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet); 695 affectsAscent = glyphOverflow->top - box->logicalTop() > 0; 696 affectsDescent = glyphOverflow->bottom + box->logicalTop() > 0; 697 glyphOverflow->top = min(glyphOverflow->top, max(0, glyphOverflow->top - box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType()))); 698 glyphOverflow->bottom = min(glyphOverflow->bottom, max(0, glyphOverflow->bottom - box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType()))); 699 } 700 701 if (includeMarginForBox(box)) { 702 LayoutUnit ascentWithMargin = box->renderer().style(isFirstLineStyle())->fontMetrics().ascent(baselineType()); 703 LayoutUnit descentWithMargin = box->renderer().style(isFirstLineStyle())->fontMetrics().descent(baselineType()); 704 if (box->parent() && !box->renderer().isText()) { 705 ascentWithMargin += box->boxModelObject()->borderBefore() + box->boxModelObject()->paddingBefore() + box->boxModelObject()->marginBefore(); 706 descentWithMargin += box->boxModelObject()->borderAfter() + box->boxModelObject()->paddingAfter() + box->boxModelObject()->marginAfter(); 707 } 708 setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet); 709 710 // Treat like a replaced element, since we're using the margin box. 711 affectsAscent = true; 712 affectsDescent = true; 713 } 714 } 715 716 LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositionCache& verticalPositionCache) 717 { 718 if (box->renderer().isText()) 719 return box->parent()->logicalTop(); 720 721 RenderBoxModelObject* renderer = box->boxModelObject(); 722 ASSERT(renderer->isInline()); 723 if (!renderer->isInline()) 724 return 0; 725 726 // This method determines the vertical position for inline elements. 727 bool firstLine = isFirstLineStyle(); 728 if (firstLine && !renderer->document().styleEngine()->usesFirstLineRules()) 729 firstLine = false; 730 731 // Check the cache. 732 bool isRenderInline = renderer->isRenderInline(); 733 if (isRenderInline && !firstLine) { 734 LayoutUnit verticalPosition = verticalPositionCache.get(renderer, baselineType()); 735 if (verticalPosition != PositionUndefined) 736 return verticalPosition; 737 } 738 739 LayoutUnit verticalPosition = 0; 740 EVerticalAlign verticalAlign = renderer->style()->verticalAlign(); 741 if (verticalAlign == TOP || verticalAlign == BOTTOM) 742 return 0; 743 744 RenderObject* parent = renderer->parent(); 745 if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP && parent->style()->verticalAlign() != BOTTOM) 746 verticalPosition = box->parent()->logicalTop(); 747 748 if (verticalAlign != BASELINE) { 749 const Font& font = parent->style(firstLine)->font(); 750 const FontMetrics& fontMetrics = font.fontMetrics(); 751 int fontSize = font.fontDescription().computedPixelSize(); 752 753 LineDirectionMode lineDirection = parent->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; 754 755 if (verticalAlign == SUB) 756 verticalPosition += fontSize / 5 + 1; 757 else if (verticalAlign == SUPER) 758 verticalPosition -= fontSize / 3 + 1; 759 else if (verticalAlign == TEXT_TOP) 760 verticalPosition += renderer->baselinePosition(baselineType(), firstLine, lineDirection) - fontMetrics.ascent(baselineType()); 761 else if (verticalAlign == MIDDLE) 762 verticalPosition = (verticalPosition - static_cast<LayoutUnit>(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection)).round(); 763 else if (verticalAlign == TEXT_BOTTOM) { 764 verticalPosition += fontMetrics.descent(baselineType()); 765 // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. 766 if (!renderer->isReplaced() || renderer->isInlineBlockOrInlineTable()) 767 verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType(), firstLine, lineDirection)); 768 } else if (verticalAlign == BASELINE_MIDDLE) 769 verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection); 770 else if (verticalAlign == LENGTH) { 771 LayoutUnit lineHeight; 772 //Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align: 'Percentages: refer to the 'line-height' of the element itself'. 773 if (renderer->style()->verticalAlignLength().isPercent()) 774 lineHeight = renderer->style()->computedLineHeight(); 775 else 776 lineHeight = renderer->lineHeight(firstLine, lineDirection); 777 verticalPosition -= valueForLength(renderer->style()->verticalAlignLength(), lineHeight); 778 } 779 } 780 781 // Store the cached value. 782 if (isRenderInline && !firstLine) 783 verticalPositionCache.set(renderer, baselineType(), verticalPosition); 784 785 return verticalPosition; 786 } 787 788 bool RootInlineBox::includeLeadingForBox(InlineBox* box) const 789 { 790 if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText())) 791 return false; 792 793 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 794 return (lineBoxContain & LineBoxContainInline) || (box == this && (lineBoxContain & LineBoxContainBlock)); 795 } 796 797 bool RootInlineBox::includeFontForBox(InlineBox* box) const 798 { 799 if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText())) 800 return false; 801 802 if (!box->isText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren()) 803 return false; 804 805 // For now map "glyphs" to "font" in vertical text mode until the bounds returned by glyphs aren't garbage. 806 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 807 return (lineBoxContain & LineBoxContainFont) || (!isHorizontal() && (lineBoxContain & LineBoxContainGlyphs)); 808 } 809 810 bool RootInlineBox::includeGlyphsForBox(InlineBox* box) const 811 { 812 if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText())) 813 return false; 814 815 if (!box->isText() && box->isInlineFlowBox() && !toInlineFlowBox(box)->hasTextChildren()) 816 return false; 817 818 // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. 819 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 820 return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); 821 } 822 823 bool RootInlineBox::includeMarginForBox(InlineBox* box) const 824 { 825 if (box->renderer().isReplaced() || (box->renderer().isText() && !box->isText())) 826 return false; 827 828 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 829 return lineBoxContain & LineBoxContainInlineBox; 830 } 831 832 833 bool RootInlineBox::fitsToGlyphs() const 834 { 835 // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. 836 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 837 return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); 838 } 839 840 bool RootInlineBox::includesRootLineBoxFontOrLeading() const 841 { 842 LineBoxContain lineBoxContain = renderer().style()->lineBoxContain(); 843 return (lineBoxContain & LineBoxContainBlock) || (lineBoxContain & LineBoxContainInline) || (lineBoxContain & LineBoxContainFont); 844 } 845 846 Node* RootInlineBox::getLogicalStartBoxWithNode(InlineBox*& startBox) const 847 { 848 Vector<InlineBox*> leafBoxesInLogicalOrder; 849 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); 850 for (size_t i = 0; i < leafBoxesInLogicalOrder.size(); ++i) { 851 if (leafBoxesInLogicalOrder[i]->renderer().node()) { 852 startBox = leafBoxesInLogicalOrder[i]; 853 return startBox->renderer().node(); 854 } 855 } 856 startBox = 0; 857 return 0; 858 } 859 860 Node* RootInlineBox::getLogicalEndBoxWithNode(InlineBox*& endBox) const 861 { 862 Vector<InlineBox*> leafBoxesInLogicalOrder; 863 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); 864 for (size_t i = leafBoxesInLogicalOrder.size(); i > 0; --i) { 865 if (leafBoxesInLogicalOrder[i - 1]->renderer().node()) { 866 endBox = leafBoxesInLogicalOrder[i - 1]; 867 return endBox->renderer().node(); 868 } 869 } 870 endBox = 0; 871 return 0; 872 } 873 874 #ifndef NDEBUG 875 const char* RootInlineBox::boxName() const 876 { 877 return "RootInlineBox"; 878 } 879 #endif 880 881 } // namespace WebCore 882