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