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 "LogicalSelectionOffsetCaches.h" 33#include "Page.h" 34#include "PaintInfo.h" 35#include "RenderFlowThread.h" 36#include "RenderView.h" 37#include "VerticalPositionCache.h" 38#include <wtf/NeverDestroyed.h> 39 40#if PLATFORM(IOS) 41#include "Settings.h" 42#endif 43 44namespace WebCore { 45 46struct SameSizeAsRootInlineBox : public InlineFlowBox { 47 unsigned variables[7]; 48 void* pointers[3]; 49}; 50 51COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small); 52 53typedef WTF::HashMap<const RootInlineBox*, std::unique_ptr<EllipsisBox>> EllipsisBoxMap; 54static EllipsisBoxMap* gEllipsisBoxMap = 0; 55 56static ContainingRegionMap& containingRegionMap(RenderBlockFlow& block) 57{ 58 ASSERT(block.flowThreadContainingBlock()); 59 return block.flowThreadContainingBlock()->containingRegionMap(); 60} 61 62RootInlineBox::RootInlineBox(RenderBlockFlow& block) 63 : InlineFlowBox(block) 64 , m_lineBreakPos(0) 65 , m_lineBreakObj(nullptr) 66{ 67 setIsHorizontal(block.isHorizontalWritingMode()); 68} 69 70RootInlineBox::~RootInlineBox() 71{ 72 detachEllipsisBox(); 73 74 if (blockFlow().flowThreadContainingBlock()) 75 containingRegionMap(blockFlow()).remove(this); 76} 77 78void RootInlineBox::detachEllipsisBox() 79{ 80 if (hasEllipsisBox()) { 81 auto box = gEllipsisBoxMap->take(this); 82 box->setParent(nullptr); 83 setHasEllipsisBox(false); 84 } 85} 86 87void RootInlineBox::clearTruncation() 88{ 89 if (hasEllipsisBox()) { 90 detachEllipsisBox(); 91 InlineFlowBox::clearTruncation(); 92 } 93} 94 95bool RootInlineBox::isHyphenated() const 96{ 97 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 98 if (box->isInlineTextBox()) { 99 if (toInlineTextBox(box)->hasHyphen()) 100 return true; 101 } 102 } 103 104 return false; 105} 106 107int RootInlineBox::baselinePosition(FontBaseline baselineType) const 108{ 109 return renderer().baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 110} 111 112LayoutUnit RootInlineBox::lineHeight() const 113{ 114 return renderer().lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); 115} 116 117bool RootInlineBox::lineCanAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth) 118{ 119 // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room. 120 int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge; 121 if (logicalWidth() - delta < ellipsisWidth) 122 return false; 123 124 // Next iterate over all the line boxes on the line. If we find a replaced element that intersects 125 // then we refuse to accommodate the ellipsis. Otherwise we're ok. 126 return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth); 127} 128 129float RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, InlineBox* markupBox) 130{ 131 if (!gEllipsisBoxMap) 132 gEllipsisBoxMap = new EllipsisBoxMap(); 133 134 // Create an ellipsis box. 135 auto newEllipsisBox = std::make_unique<EllipsisBox>(blockFlow(), ellipsisStr, this, ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(), y(), !prevRootBox(), isHorizontal(), markupBox); 136 auto ellipsisBox = newEllipsisBox.get(); 137 138 gEllipsisBoxMap->add(this, WTF::move(newEllipsisBox)); 139 setHasEllipsisBox(true); 140 141 // FIXME: Do we need an RTL version of this? 142 if (ltr && (x() + logicalWidth() + ellipsisWidth) <= blockRightEdge) { 143 ellipsisBox->setX(x() + logicalWidth()); 144 return logicalWidth() + ellipsisWidth; 145 } 146 147 // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) 148 // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being 149 // truncated). 150 bool foundBox = false; 151 float truncatedWidth = 0; 152 float position = placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, truncatedWidth, foundBox); 153 ellipsisBox->setX(position); 154 return truncatedWidth; 155} 156 157float RootInlineBox::placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) 158{ 159 float result = InlineFlowBox::placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, truncatedWidth, foundBox); 160 if (result == -1) { 161 result = ltr ? blockRightEdge - ellipsisWidth : blockLeftEdge; 162 truncatedWidth = blockRightEdge - blockLeftEdge; 163 } 164 return result; 165} 166 167void RootInlineBox::paintEllipsisBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) const 168{ 169 if (hasEllipsisBox() && paintInfo.shouldPaintWithinRoot(renderer()) && renderer().style().visibility() == VISIBLE 170 && paintInfo.phase == PaintPhaseForeground) 171 ellipsisBox()->paint(paintInfo, paintOffset, lineTop, lineBottom); 172} 173 174void RootInlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 175{ 176 RenderNamedFlowFragment* namedFlowFragment = renderer().currentRenderNamedFlowFragment(); 177 178 // Check if we are in the correct region. 179 if (namedFlowFragment) { 180 RenderRegion* region = containingRegion(); 181 if (region && region != reinterpret_cast<RenderRegion*>(namedFlowFragment)) 182 return; 183 } 184 185 InlineFlowBox::paint(paintInfo, paintOffset, lineTop, lineBottom); 186 paintEllipsisBox(paintInfo, paintOffset, lineTop, lineBottom); 187} 188 189bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 190{ 191 if (hasEllipsisBox() && visibleToHitTesting()) { 192 if (ellipsisBox()->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { 193 renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); 194 return true; 195 } 196 } 197 return InlineFlowBox::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom); 198} 199 200void RootInlineBox::adjustPosition(float dx, float dy) 201{ 202 InlineFlowBox::adjustPosition(dx, dy); 203 LayoutUnit blockDirectionDelta = isHorizontal() ? dy : dx; // The block direction delta is a LayoutUnit. 204 m_lineTop += blockDirectionDelta; 205 m_lineBottom += blockDirectionDelta; 206 m_lineTopWithLeading += blockDirectionDelta; 207 m_lineBottomWithLeading += blockDirectionDelta; 208 if (hasEllipsisBox()) 209 ellipsisBox()->adjustPosition(dx, dy); 210} 211 212void 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 223RenderRegion* RootInlineBox::containingRegion() const 224{ 225 ContainingRegionMap& regionMap = containingRegionMap(blockFlow()); 226 bool hasContainingRegion = regionMap.contains(this); 227 RenderRegion* region = hasContainingRegion ? regionMap.get(this) : nullptr; 228 229#ifndef NDEBUG 230 if (hasContainingRegion) { 231 RenderFlowThread* flowThread = blockFlow().flowThreadContainingBlock(); 232 const RenderRegionList& regionList = flowThread->renderRegionList(); 233 ASSERT_WITH_SECURITY_IMPLICATION(regionList.contains(region)); 234 } 235#endif 236 237 return region; 238} 239 240void RootInlineBox::clearContainingRegion() 241{ 242 ASSERT(!isDirty()); 243 244 if (!containingRegionMap(blockFlow()).contains(this)) 245 return; 246 247 containingRegionMap(blockFlow()).remove(this); 248} 249 250void RootInlineBox::setContainingRegion(RenderRegion& region) 251{ 252 ASSERT(!isDirty()); 253 254 containingRegionMap(blockFlow()).set(this, ®ion); 255} 256 257LayoutUnit RootInlineBox::alignBoxesInBlockDirection(LayoutUnit heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache) 258{ 259 // SVG will handle vertical alignment on its own. 260 if (isSVGRootInlineBox()) 261 return 0; 262 263 LayoutUnit maxPositionTop = 0; 264 LayoutUnit maxPositionBottom = 0; 265 int maxAscent = 0; 266 int maxDescent = 0; 267 bool setMaxAscent = false; 268 bool setMaxDescent = false; 269 270 // Figure out if we're in no-quirks mode. 271 bool noQuirksMode = renderer().document().inNoQuirksMode(); 272 273 m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline; 274 275 computeLogicalBoxHeights(*this, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode, 276 textBoxDataMap, baselineType(), verticalPositionCache); 277 278 if (maxAscent + maxDescent < std::max(maxPositionTop, maxPositionBottom)) 279 adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); 280 281 LayoutUnit maxHeight = maxAscent + maxDescent; 282 LayoutUnit lineTop = heightOfBlock; 283 LayoutUnit lineBottom = heightOfBlock; 284 LayoutUnit lineTopIncludingMargins = heightOfBlock; 285 LayoutUnit lineBottomIncludingMargins = heightOfBlock; 286 bool setLineTop = false; 287 bool hasAnnotationsBefore = false; 288 bool hasAnnotationsAfter = false; 289 placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, noQuirksMode, lineTop, lineBottom, setLineTop, 290 lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType()); 291 m_hasAnnotationsBefore = hasAnnotationsBefore; 292 m_hasAnnotationsAfter = hasAnnotationsAfter; 293 294 maxHeight = std::max<LayoutUnit>(0, maxHeight); // FIXME: Is this really necessary? 295 296 setLineTopBottomPositions(lineTop, lineBottom, heightOfBlock, heightOfBlock + maxHeight); 297 setPaginatedLineWidth(blockFlow().availableLogicalWidthForContent(heightOfBlock)); 298 299 LayoutUnit annotationsAdjustment = beforeAnnotationsAdjustment(); 300 if (annotationsAdjustment) { 301 // FIXME: Need to handle pagination here. We might have to move to the next page/column as a result of the 302 // ruby expansion. 303 adjustBlockDirectionPosition(annotationsAdjustment); 304 heightOfBlock += annotationsAdjustment; 305 } 306 307 LayoutUnit gridSnapAdjustment = lineSnapAdjustment(); 308 if (gridSnapAdjustment) { 309 adjustBlockDirectionPosition(gridSnapAdjustment); 310 heightOfBlock += gridSnapAdjustment; 311 } 312 313 return heightOfBlock + maxHeight; 314} 315 316float RootInlineBox::maxLogicalTop() const 317{ 318 float maxLogicalTop = 0; 319 computeMaxLogicalTop(maxLogicalTop); 320 return maxLogicalTop; 321} 322 323LayoutUnit RootInlineBox::beforeAnnotationsAdjustment() const 324{ 325 LayoutUnit result = 0; 326 327 if (!renderer().style().isFlippedLinesWritingMode()) { 328 // Annotations under the previous line may push us down. 329 if (prevRootBox() && prevRootBox()->hasAnnotationsAfter()) 330 result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop()); 331 332 if (!hasAnnotationsBefore()) 333 return result; 334 335 // Annotations over this line may push us further down. 336 LayoutUnit highestAllowedPosition = prevRootBox() ? std::min(prevRootBox()->lineBottom(), lineTop()) + result : blockFlow().borderBefore(); 337 result = computeOverAnnotationAdjustment(highestAllowedPosition); 338 } else { 339 // Annotations under this line may push us up. 340 if (hasAnnotationsBefore()) 341 result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : blockFlow().borderBefore()); 342 343 if (!prevRootBox() || !prevRootBox()->hasAnnotationsAfter()) 344 return result; 345 346 // We have to compute the expansion for annotations over the previous line to see how much we should move. 347 LayoutUnit lowestAllowedPosition = std::max(prevRootBox()->lineBottom(), lineTop()) - result; 348 result = prevRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); 349 } 350 351 return result; 352} 353 354LayoutUnit RootInlineBox::lineSnapAdjustment(LayoutUnit delta) const 355{ 356 // If our block doesn't have snapping turned on, do nothing. 357 // FIXME: Implement bounds snapping. 358 if (blockFlow().style().lineSnap() == LineSnapNone) 359 return 0; 360 361 // Get the current line grid and offset. 362 LayoutState* layoutState = blockFlow().view().layoutState(); 363 RenderBlockFlow* lineGrid = layoutState->lineGrid(); 364 LayoutSize lineGridOffset = layoutState->lineGridOffset(); 365 if (!lineGrid || lineGrid->style().writingMode() != blockFlow().style().writingMode()) 366 return 0; 367 368 // Get the hypothetical line box used to establish the grid. 369 RootInlineBox* lineGridBox = lineGrid->lineGridBox(); 370 if (!lineGridBox) 371 return 0; 372 373 LayoutUnit lineGridBlockOffset = lineGrid->isHorizontalWritingMode() ? lineGridOffset.height() : lineGridOffset.width(); 374 LayoutUnit blockOffset = blockFlow().isHorizontalWritingMode() ? layoutState->layoutOffset().height() : layoutState->layoutOffset().width(); 375 376 // Now determine our position on the grid. Our baseline needs to be adjusted to the nearest baseline multiple 377 // as established by the line box. 378 // FIXME: Need to handle crazy line-box-contain values that cause the root line box to not be considered. I assume 379 // the grid should honor line-box-contain. 380 LayoutUnit gridLineHeight = lineGridBox->lineBottomWithLeading() - lineGridBox->lineTopWithLeading(); 381 if (!gridLineHeight) 382 return 0; 383 384 LayoutUnit lineGridFontAscent = lineGrid->style().fontMetrics().ascent(baselineType()); 385 LayoutUnit lineGridFontHeight = lineGridBox->logicalHeight(); 386 LayoutUnit firstTextTop = lineGridBlockOffset + lineGridBox->logicalTop(); 387 LayoutUnit firstLineTopWithLeading = lineGridBlockOffset + lineGridBox->lineTopWithLeading(); 388 LayoutUnit firstBaselinePosition = firstTextTop + lineGridFontAscent; 389 390 LayoutUnit currentTextTop = blockOffset + logicalTop() + delta; 391 LayoutUnit currentFontAscent = blockFlow().style().fontMetrics().ascent(baselineType()); 392 LayoutUnit currentBaselinePosition = currentTextTop + currentFontAscent; 393 394 LayoutUnit lineGridPaginationOrigin = isHorizontal() ? layoutState->lineGridPaginationOrigin().height() : layoutState->lineGridPaginationOrigin().width(); 395 396 // If we're paginated, see if we're on a page after the first one. If so, the grid resets on subsequent pages. 397 // FIXME: If the grid is an ancestor of the pagination establisher, then this is incorrect. 398 LayoutUnit pageLogicalTop = 0; 399 if (layoutState->isPaginated() && layoutState->pageLogicalHeight()) { 400 pageLogicalTop = blockFlow().pageLogicalTopForOffset(lineTopWithLeading() + delta); 401 if (pageLogicalTop > firstLineTopWithLeading) 402 firstTextTop = pageLogicalTop + lineGridBox->logicalTop() - lineGrid->borderAndPaddingBefore() + lineGridPaginationOrigin; 403 } 404 405 if (blockFlow().style().lineSnap() == LineSnapContain) { 406 // Compute the desired offset from the text-top of a grid line. 407 // Look at our height (logicalHeight()). 408 // Look at the total available height. It's going to be (textBottom - textTop) + (n-1)*(multiple with leading) 409 // where n is number of grid lines required to enclose us. 410 if (logicalHeight() <= lineGridFontHeight) 411 firstTextTop += (lineGridFontHeight - logicalHeight()) / 2; 412 else { 413 LayoutUnit numberOfLinesWithLeading = ceilf(static_cast<float>(logicalHeight() - lineGridFontHeight) / gridLineHeight); 414 LayoutUnit totalHeight = lineGridFontHeight + numberOfLinesWithLeading * gridLineHeight; 415 firstTextTop += (totalHeight - logicalHeight()) / 2; 416 } 417 firstBaselinePosition = firstTextTop + currentFontAscent; 418 } else 419 firstBaselinePosition = firstTextTop + lineGridFontAscent; 420 421 // If we're above the first line, just push to the first line. 422 if (currentBaselinePosition < firstBaselinePosition) 423 return delta + firstBaselinePosition - currentBaselinePosition; 424 425 // Otherwise we're in the middle of the grid somewhere. Just push to the next line. 426 LayoutUnit baselineOffset = currentBaselinePosition - firstBaselinePosition; 427 LayoutUnit remainder = roundToInt(baselineOffset) % roundToInt(gridLineHeight); 428 LayoutUnit result = delta; 429 if (remainder) 430 result += gridLineHeight - remainder; 431 432 // If we aren't paginated we can return the result. 433 if (!layoutState->isPaginated() || !layoutState->pageLogicalHeight() || result == delta) 434 return result; 435 436 // We may end up shifted to a new page. We need to do a re-snap when that happens. 437 LayoutUnit newPageLogicalTop = blockFlow().pageLogicalTopForOffset(lineBottomWithLeading() + result); 438 if (newPageLogicalTop == pageLogicalTop) 439 return result; 440 441 // Put ourselves at the top of the next page to force a snap onto the new grid established by that page. 442 return lineSnapAdjustment(newPageLogicalTop - (blockOffset + lineTopWithLeading())); 443} 444 445GapRects RootInlineBox::lineSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 446 LayoutUnit selTop, LayoutUnit selHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 447{ 448 RenderObject::SelectionState lineState = selectionState(); 449 450 bool leftGap, rightGap; 451 blockFlow().getSelectionGapInfo(lineState, leftGap, rightGap); 452 453 GapRects result; 454 455 InlineBox* firstBox = firstSelectedBox(); 456 InlineBox* lastBox = lastSelectedBox(); 457 if (leftGap) { 458 result.uniteLeft(blockFlow().logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, &firstBox->parent()->renderer(), firstBox->logicalLeft(), 459 selTop, selHeight, cache, paintInfo)); 460 } 461 if (rightGap) { 462 result.uniteRight(blockFlow().logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, &lastBox->parent()->renderer(), lastBox->logicalRight(), 463 selTop, selHeight, cache, paintInfo)); 464 } 465 466 // When dealing with bidi text, a non-contiguous selection region is possible. 467 // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out 468 // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the 469 // selection will look like (underline denotes selection): 470 // |aaa|bbb|AAA| 471 // ___ _ 472 // We can see that the |bbb| run is not part of the selection while the runs around it are. 473 if (firstBox && firstBox != lastBox) { 474 // Now fill in any gaps on the line that occurred between two selected elements. 475 LayoutUnit lastLogicalLeft = firstBox->logicalRight(); 476 bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone; 477 for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { 478 if (box->selectionState() != RenderObject::SelectionNone) { 479 LayoutRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight); 480 logicalRect.move(renderer().isHorizontalWritingMode() ? offsetFromRootBlock : LayoutSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); 481 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); 482 if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) { 483 if (paintInfo && box->parent()->renderer().style().visibility() == VISIBLE) 484 paintInfo->context->fillRect(gapRect, box->parent()->renderer().selectionBackgroundColor(), box->parent()->renderer().style().colorSpace()); 485 // VisibleSelection may be non-contiguous, see comment above. 486 result.uniteCenter(gapRect); 487 } 488 lastLogicalLeft = box->logicalRight(); 489 } 490 if (box == lastBox) 491 break; 492 isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone; 493 } 494 } 495 496 return result; 497} 498 499IntRect RootInlineBox::computeCaretRect(float logicalLeftPosition, unsigned caretWidth, LayoutUnit* extraWidthToEndOfLine) const 500{ 501 int height = selectionHeight(); 502 int top = selectionTop(); 503 504 // Distribute the caret's width to either side of the offset. 505 float left = logicalLeftPosition; 506 int caretWidthLeftOfOffset = caretWidth / 2; 507 left -= caretWidthLeftOfOffset; 508 int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset; 509 left = roundf(left); 510 511 float rootLeft = logicalLeft(); 512 float rootRight = logicalRight(); 513 514 if (extraWidthToEndOfLine) 515 *extraWidthToEndOfLine = (logicalWidth() + rootLeft) - (left + caretWidth); 516 517 const RenderStyle& blockStyle = blockFlow().style(); 518 519 bool rightAligned = false; 520 switch (blockStyle.textAlign()) { 521 case RIGHT: 522 case WEBKIT_RIGHT: 523 rightAligned = true; 524 break; 525 case LEFT: 526 case WEBKIT_LEFT: 527 case CENTER: 528 case WEBKIT_CENTER: 529 break; 530 case JUSTIFY: 531 case TASTART: 532 rightAligned = !blockStyle.isLeftToRightDirection(); 533 break; 534 case TAEND: 535 rightAligned = blockStyle.isLeftToRightDirection(); 536 break; 537 } 538 539 float leftEdge = std::min<float>(0, rootLeft); 540 float rightEdge = std::max<float>(blockFlow().logicalWidth(), rootRight); 541 542 if (rightAligned) { 543 left = std::max(left, leftEdge); 544 left = std::min(left, rootRight - caretWidth); 545 } else { 546 left = std::min(left, rightEdge - caretWidthRightOfOffset); 547 left = std::max(left, rootLeft); 548 } 549 return blockStyle.isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth); 550} 551 552RenderObject::SelectionState RootInlineBox::selectionState() 553{ 554 // Walk over all of the selected boxes. 555 RenderObject::SelectionState state = RenderObject::SelectionNone; 556 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 557 RenderObject::SelectionState boxState = box->selectionState(); 558 if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) || 559 (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart)) 560 state = RenderObject::SelectionBoth; 561 else if (state == RenderObject::SelectionNone || 562 ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) && 563 (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside))) 564 state = boxState; 565 else if (boxState == RenderObject::SelectionNone && state == RenderObject::SelectionStart) { 566 // We are past the end of the selection. 567 state = RenderObject::SelectionBoth; 568 } 569 if (state == RenderObject::SelectionBoth) 570 break; 571 } 572 573 return state; 574} 575 576InlineBox* RootInlineBox::firstSelectedBox() 577{ 578 for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { 579 if (box->selectionState() != RenderObject::SelectionNone) 580 return box; 581 } 582 583 return 0; 584} 585 586InlineBox* RootInlineBox::lastSelectedBox() 587{ 588 for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) { 589 if (box->selectionState() != RenderObject::SelectionNone) 590 return box; 591 } 592 593 return 0; 594} 595 596LayoutUnit RootInlineBox::selectionTop() const 597{ 598 LayoutUnit selectionTop = m_lineTop; 599 600 if (m_hasAnnotationsBefore) 601 selectionTop -= !renderer().style().isFlippedLinesWritingMode() ? computeOverAnnotationAdjustment(m_lineTop) : computeUnderAnnotationAdjustment(m_lineTop); 602 603 if (renderer().style().isFlippedLinesWritingMode()) 604 return selectionTop; 605 606 LayoutUnit prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : blockFlow().borderAndPaddingBefore(); 607 if (prevBottom < selectionTop && blockFlow().containsFloats()) { 608 // This line has actually been moved further down, probably from a large line-height, but possibly because the 609 // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous 610 // line's bottom if the offsets are greater on both sides. 611 LayoutUnit prevLeft = blockFlow().logicalLeftOffsetForLine(prevBottom, false); 612 LayoutUnit prevRight = blockFlow().logicalRightOffsetForLine(prevBottom, false); 613 LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionTop, false); 614 LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionTop, false); 615 if (prevLeft > newLeft || prevRight < newRight) 616 return selectionTop; 617 } 618 619 return prevBottom; 620} 621 622LayoutUnit RootInlineBox::selectionTopAdjustedForPrecedingBlock() const 623{ 624 const RootInlineBox& rootBox = root(); 625 LayoutUnit top = selectionTop(); 626 627 RenderObject::SelectionState blockSelectionState = rootBox.blockFlow().selectionState(); 628 if (blockSelectionState != RenderObject::SelectionInside && blockSelectionState != RenderObject::SelectionEnd) 629 return top; 630 631 LayoutSize offsetToBlockBefore; 632 if (RenderBlock* block = rootBox.blockFlow().blockBeforeWithinSelectionRoot(offsetToBlockBefore)) { 633 if (block->isRenderBlockFlow()) { 634 if (RootInlineBox* lastLine = toRenderBlockFlow(block)->lastRootBox()) { 635 RenderObject::SelectionState lastLineSelectionState = lastLine->selectionState(); 636 if (lastLineSelectionState != RenderObject::SelectionInside && lastLineSelectionState != RenderObject::SelectionStart) 637 return top; 638 639 LayoutUnit lastLineSelectionBottom = lastLine->selectionBottom() + offsetToBlockBefore.height(); 640 top = std::max(top, lastLineSelectionBottom); 641 } 642 } 643 } 644 645 return top; 646} 647 648LayoutUnit RootInlineBox::selectionBottom() const 649{ 650 LayoutUnit selectionBottom = m_lineBottom; 651 652 if (m_hasAnnotationsAfter) 653 selectionBottom += !renderer().style().isFlippedLinesWritingMode() ? computeUnderAnnotationAdjustment(m_lineBottom) : computeOverAnnotationAdjustment(m_lineBottom); 654 655 if (!renderer().style().isFlippedLinesWritingMode() || !nextRootBox()) 656 return selectionBottom; 657 658 LayoutUnit nextTop = nextRootBox()->selectionTop(); 659 if (nextTop > selectionBottom && blockFlow().containsFloats()) { 660 // The next line has actually been moved further over, probably from a large line-height, but possibly because the 661 // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the next 662 // line's top if the offsets are greater on both sides. 663 LayoutUnit nextLeft = blockFlow().logicalLeftOffsetForLine(nextTop, false); 664 LayoutUnit nextRight = blockFlow().logicalRightOffsetForLine(nextTop, false); 665 LayoutUnit newLeft = blockFlow().logicalLeftOffsetForLine(selectionBottom, false); 666 LayoutUnit newRight = blockFlow().logicalRightOffsetForLine(selectionBottom, false); 667 if (nextLeft > newLeft || nextRight < newRight) 668 return selectionBottom; 669 } 670 671 return nextTop; 672} 673 674int RootInlineBox::blockDirectionPointInLine() const 675{ 676 return !blockFlow().style().isFlippedBlocksWritingMode() ? std::max(lineTop(), selectionTop()) : std::min(lineBottom(), selectionBottom()); 677} 678 679RenderBlockFlow& RootInlineBox::blockFlow() const 680{ 681 return toRenderBlockFlow(renderer()); 682} 683 684static bool isEditableLeaf(InlineBox* leaf) 685{ 686 return leaf && leaf->renderer().node() && leaf->renderer().node()->hasEditableStyle(); 687} 688 689InlineBox* RootInlineBox::closestLeafChildForPoint(const IntPoint& pointInContents, bool onlyEditableLeaves) 690{ 691 return closestLeafChildForLogicalLeftPosition(blockFlow().isHorizontalWritingMode() ? pointInContents.x() : pointInContents.y(), onlyEditableLeaves); 692} 693 694InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPosition, bool onlyEditableLeaves) 695{ 696 InlineBox* firstLeaf = firstLeafChild(); 697 InlineBox* lastLeaf = lastLeafChild(); 698 699 if (firstLeaf != lastLeaf) { 700 if (firstLeaf->isLineBreak()) 701 firstLeaf = firstLeaf->nextLeafChildIgnoringLineBreak(); 702 else if (lastLeaf->isLineBreak()) 703 lastLeaf = lastLeaf->prevLeafChildIgnoringLineBreak(); 704 } 705 706 if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) 707 return firstLeaf; 708 709 // Avoid returning a list marker when possible. 710 if (leftPosition <= firstLeaf->logicalLeft() && !firstLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) 711 // The leftPosition coordinate is less or equal to left edge of the firstLeaf. 712 // Return it. 713 return firstLeaf; 714 715 if (leftPosition >= lastLeaf->logicalRight() && !lastLeaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) 716 // The leftPosition coordinate is greater or equal to right edge of the lastLeaf. 717 // Return it. 718 return lastLeaf; 719 720 InlineBox* closestLeaf = 0; 721 for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChildIgnoringLineBreak()) { 722 if (!leaf->renderer().isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { 723 closestLeaf = leaf; 724 if (leftPosition < leaf->logicalRight()) 725 // The x coordinate is less than the right edge of the box. 726 // Return it. 727 return leaf; 728 } 729 } 730 731 return closestLeaf ? closestLeaf : lastLeaf; 732} 733 734BidiStatus RootInlineBox::lineBreakBidiStatus() const 735{ 736 return BidiStatus(static_cast<UCharDirection>(m_lineBreakBidiStatusEor), static_cast<UCharDirection>(m_lineBreakBidiStatusLastStrong), static_cast<UCharDirection>(m_lineBreakBidiStatusLast), m_lineBreakContext); 737} 738 739void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status) 740{ 741 // When setting lineBreakObj, the RenderObject must not be a RenderInline 742 // with no line boxes, otherwise all sorts of invariants are broken later. 743 // This has security implications because if the RenderObject does not 744 // point to at least one line box, then that RenderInline can be deleted 745 // later without resetting the lineBreakObj, leading to use-after-free. 746 ASSERT_WITH_SECURITY_IMPLICATION(!obj || obj->isText() || !(obj->isRenderInline() && obj->isBox() && !toRenderBox(obj)->inlineBoxWrapper())); 747 748 m_lineBreakObj = obj; 749 m_lineBreakPos = breakPos; 750 m_lineBreakBidiStatusEor = status.eor; 751 m_lineBreakBidiStatusLastStrong = status.lastStrong; 752 m_lineBreakBidiStatusLast = status.last; 753 m_lineBreakContext = status.context; 754} 755 756EllipsisBox* RootInlineBox::ellipsisBox() const 757{ 758 if (!hasEllipsisBox()) 759 return 0; 760 return gEllipsisBoxMap->get(this); 761} 762 763void RootInlineBox::removeLineBoxFromRenderObject() 764{ 765 blockFlow().lineBoxes().removeLineBox(this); 766} 767 768void RootInlineBox::extractLineBoxFromRenderObject() 769{ 770 blockFlow().lineBoxes().extractLineBox(this); 771} 772 773void RootInlineBox::attachLineBoxToRenderObject() 774{ 775 blockFlow().lineBoxes().attachLineBox(this); 776} 777 778LayoutRect RootInlineBox::paddedLayoutOverflowRect(LayoutUnit endPadding) const 779{ 780 LayoutRect lineLayoutOverflow = layoutOverflowRect(lineTop(), lineBottom()); 781 if (!endPadding) 782 return lineLayoutOverflow; 783 784 // FIXME: Audit whether to use pixel snapped values when not using integers for layout: https://bugs.webkit.org/show_bug.cgi?id=63656 785 if (isHorizontal()) { 786 if (isLeftToRightDirection()) 787 lineLayoutOverflow.shiftMaxXEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxX(), pixelSnappedLogicalRight() + endPadding)); 788 else 789 lineLayoutOverflow.shiftXEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.x(), pixelSnappedLogicalLeft() - endPadding)); 790 } else { 791 if (isLeftToRightDirection()) 792 lineLayoutOverflow.shiftMaxYEdgeTo(std::max<LayoutUnit>(lineLayoutOverflow.maxY(), pixelSnappedLogicalRight() + endPadding)); 793 else 794 lineLayoutOverflow.shiftYEdgeTo(std::min<LayoutUnit>(lineLayoutOverflow.y(), pixelSnappedLogicalLeft() - endPadding)); 795 } 796 797 return lineLayoutOverflow; 798} 799 800static void setAscentAndDescent(int& ascent, int& descent, int newAscent, int newDescent, bool& ascentDescentSet) 801{ 802 if (!ascentDescentSet) { 803 ascentDescentSet = true; 804 ascent = newAscent; 805 descent = newDescent; 806 } else { 807 ascent = std::max(ascent, newAscent); 808 descent = std::max(descent, newDescent); 809 } 810} 811 812void RootInlineBox::ascentAndDescentForBox(InlineBox& box, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, int& ascent, int& descent, 813 bool& affectsAscent, bool& affectsDescent) const 814{ 815 bool ascentDescentSet = false; 816 817 // Replaced boxes will return 0 for the line-height if line-box-contain says they are 818 // not to be included. 819 if (box.renderer().isReplaced()) { 820 if (lineStyle().lineBoxContain() & LineBoxContainReplaced) { 821 ascent = box.baselinePosition(baselineType()); 822 descent = box.lineHeight() - ascent; 823 824 // Replaced elements always affect both the ascent and descent. 825 affectsAscent = true; 826 affectsDescent = true; 827 } 828 return; 829 } 830 831 Vector<const SimpleFontData*>* usedFonts = nullptr; 832 GlyphOverflow* glyphOverflow = nullptr; 833 if (box.isInlineTextBox()) { 834 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(toInlineTextBox(&box)); 835 usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first; 836 glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second; 837 } 838 839 bool includeLeading = includeLeadingForBox(box); 840 bool includeFont = includeFontForBox(box); 841 842 bool setUsedFont = false; 843 bool setUsedFontWithLeading = false; 844 845 const RenderStyle& boxLineStyle = box.lineStyle(); 846#if PLATFORM(IOS) 847 if (usedFonts && !usedFonts->isEmpty() && (includeFont || (boxLineStyle.lineHeight().isNegative() && includeLeading)) && !box.renderer().document().settings()->alwaysUseBaselineOfPrimaryFont()) { 848#else 849 if (usedFonts && !usedFonts->isEmpty() && (includeFont || (boxLineStyle.lineHeight().isNegative() && includeLeading))) { 850#endif 851 usedFonts->append(boxLineStyle.font().primaryFont()); 852 for (size_t i = 0; i < usedFonts->size(); ++i) { 853 const FontMetrics& fontMetrics = usedFonts->at(i)->fontMetrics(); 854 int usedFontAscent = fontMetrics.ascent(baselineType()); 855 int usedFontDescent = fontMetrics.descent(baselineType()); 856 int halfLeading = (fontMetrics.lineSpacing() - fontMetrics.height()) / 2; 857 int usedFontAscentAndLeading = usedFontAscent + halfLeading; 858 int usedFontDescentAndLeading = fontMetrics.lineSpacing() - usedFontAscentAndLeading; 859 if (includeFont) { 860 setAscentAndDescent(ascent, descent, usedFontAscent, usedFontDescent, ascentDescentSet); 861 setUsedFont = true; 862 } 863 if (includeLeading) { 864 setAscentAndDescent(ascent, descent, usedFontAscentAndLeading, usedFontDescentAndLeading, ascentDescentSet); 865 setUsedFontWithLeading = true; 866 } 867 if (!affectsAscent) 868 affectsAscent = usedFontAscent - box.logicalTop() > 0; 869 if (!affectsDescent) 870 affectsDescent = usedFontDescent + box.logicalTop() > 0; 871 } 872 } 873 874 // If leading is included for the box, then we compute that box. 875 if (includeLeading && !setUsedFontWithLeading) { 876 int ascentWithLeading = box.baselinePosition(baselineType()); 877 int descentWithLeading = box.lineHeight() - ascentWithLeading; 878 setAscentAndDescent(ascent, descent, ascentWithLeading, descentWithLeading, ascentDescentSet); 879 880 // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. 881 // If the top of our font box relative to the root box baseline is above the root box baseline, then 882 // we are contributing to the maxAscent value. Descent is similar. If any part of our font box is below 883 // the root box's baseline, then we contribute to the maxDescent value. 884 affectsAscent = ascentWithLeading - box.logicalTop() > 0; 885 affectsDescent = descentWithLeading + box.logicalTop() > 0; 886 } 887 888 if (includeFontForBox(box) && !setUsedFont) { 889 int fontAscent = boxLineStyle.fontMetrics().ascent(baselineType()); 890 int fontDescent = boxLineStyle.fontMetrics().descent(baselineType()); 891 setAscentAndDescent(ascent, descent, fontAscent, fontDescent, ascentDescentSet); 892 affectsAscent = fontAscent - box.logicalTop() > 0; 893 affectsDescent = fontDescent + box.logicalTop() > 0; 894 } 895 896 if (includeGlyphsForBox(box) && glyphOverflow && glyphOverflow->computeBounds) { 897 setAscentAndDescent(ascent, descent, glyphOverflow->top, glyphOverflow->bottom, ascentDescentSet); 898 affectsAscent = glyphOverflow->top - box.logicalTop() > 0; 899 affectsDescent = glyphOverflow->bottom + box.logicalTop() > 0; 900 glyphOverflow->top = std::min(glyphOverflow->top, std::max(0, glyphOverflow->top - boxLineStyle.fontMetrics().ascent(baselineType()))); 901 glyphOverflow->bottom = std::min(glyphOverflow->bottom, std::max(0, glyphOverflow->bottom - boxLineStyle.fontMetrics().descent(baselineType()))); 902 } 903 904 if (includeMarginForBox(box)) { 905 LayoutUnit ascentWithMargin = boxLineStyle.fontMetrics().ascent(baselineType()); 906 LayoutUnit descentWithMargin = boxLineStyle.fontMetrics().descent(baselineType()); 907 if (box.parent() && !box.renderer().isTextOrLineBreak()) { 908 ascentWithMargin += box.boxModelObject()->borderAndPaddingBefore() + box.boxModelObject()->marginBefore(); 909 descentWithMargin += box.boxModelObject()->borderAndPaddingAfter() + box.boxModelObject()->marginAfter(); 910 } 911 setAscentAndDescent(ascent, descent, ascentWithMargin, descentWithMargin, ascentDescentSet); 912 913 // Treat like a replaced element, since we're using the margin box. 914 affectsAscent = true; 915 affectsDescent = true; 916 } 917} 918 919LayoutUnit RootInlineBox::verticalPositionForBox(InlineBox* box, VerticalPositionCache& verticalPositionCache) 920{ 921 if (box->renderer().isTextOrLineBreak()) 922 return box->parent()->logicalTop(); 923 924 RenderBoxModelObject* renderer = box->boxModelObject(); 925 ASSERT(renderer->isInline()); 926 if (!renderer->isInline()) 927 return 0; 928 929 // This method determines the vertical position for inline elements. 930 bool firstLine = isFirstLine(); 931 if (firstLine && !renderer->document().styleSheetCollection().usesFirstLineRules()) 932 firstLine = false; 933 934 // Check the cache. 935 bool isRenderInline = renderer->isRenderInline(); 936 if (isRenderInline && !firstLine) { 937 LayoutUnit cachedPosition; 938 if (verticalPositionCache.get(renderer, baselineType(), cachedPosition)) 939 return cachedPosition; 940 } 941 942 LayoutUnit verticalPosition = 0; 943 EVerticalAlign verticalAlign = renderer->style().verticalAlign(); 944 if (verticalAlign == TOP || verticalAlign == BOTTOM) 945 return 0; 946 947 RenderElement* parent = renderer->parent(); 948 if (parent->isRenderInline() && parent->style().verticalAlign() != TOP && parent->style().verticalAlign() != BOTTOM) 949 verticalPosition = box->parent()->logicalTop(); 950 951 if (verticalAlign != BASELINE) { 952 const RenderStyle& parentLineStyle = firstLine ? parent->firstLineStyle() : parent->style(); 953 const Font& font = parentLineStyle.font(); 954 const FontMetrics& fontMetrics = font.fontMetrics(); 955 int fontSize = font.pixelSize(); 956 957 LineDirectionMode lineDirection = parent->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; 958 959 if (verticalAlign == SUB) 960 verticalPosition += fontSize / 5 + 1; 961 else if (verticalAlign == SUPER) 962 verticalPosition -= fontSize / 3 + 1; 963 else if (verticalAlign == TEXT_TOP) 964 verticalPosition += renderer->baselinePosition(baselineType(), firstLine, lineDirection) - fontMetrics.ascent(baselineType()); 965 else if (verticalAlign == MIDDLE) 966 verticalPosition = (verticalPosition - LayoutUnit(fontMetrics.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection)).round(); 967 else if (verticalAlign == TEXT_BOTTOM) { 968 verticalPosition += fontMetrics.descent(baselineType()); 969 // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. 970 if (!renderer->isReplaced() || renderer->isInlineBlockOrInlineTable()) 971 verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType(), firstLine, lineDirection)); 972 } else if (verticalAlign == BASELINE_MIDDLE) 973 verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType(), firstLine, lineDirection); 974 else if (verticalAlign == LENGTH) { 975 LayoutUnit lineHeight; 976 //Per http://www.w3.org/TR/CSS21/visudet.html#propdef-vertical-align: 'Percentages: refer to the 'line-height' of the element itself'. 977 if (renderer->style().verticalAlignLength().isPercent()) 978 lineHeight = renderer->style().computedLineHeight(); 979 else 980 lineHeight = renderer->lineHeight(firstLine, lineDirection); 981 verticalPosition -= valueForLength(renderer->style().verticalAlignLength(), lineHeight); 982 } 983 } 984 985 // Store the cached value. 986 if (isRenderInline && !firstLine) 987 verticalPositionCache.set(renderer, baselineType(), verticalPosition); 988 989 return verticalPosition; 990} 991 992bool RootInlineBox::includeLeadingForBox(InlineBox& box) const 993{ 994 if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) 995 return false; 996 997 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 998 return (lineBoxContain & LineBoxContainInline) || (&box == this && (lineBoxContain & LineBoxContainBlock)); 999} 1000 1001bool RootInlineBox::includeFontForBox(InlineBox& box) const 1002{ 1003 if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) 1004 return false; 1005 1006 if (!box.behavesLikeText() && box.isInlineFlowBox() && !toInlineFlowBox(&box)->hasTextChildren()) 1007 return false; 1008 1009 // For now map "glyphs" to "font" in vertical text mode until the bounds returned by glyphs aren't garbage. 1010 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 1011 return (lineBoxContain & LineBoxContainFont) || (!isHorizontal() && (lineBoxContain & LineBoxContainGlyphs)); 1012} 1013 1014bool RootInlineBox::includeGlyphsForBox(InlineBox& box) const 1015{ 1016 if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) 1017 return false; 1018 1019 if (!box.behavesLikeText() && box.isInlineFlowBox() && !toInlineFlowBox(&box)->hasTextChildren()) 1020 return false; 1021 1022 // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. 1023 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 1024 return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); 1025} 1026 1027bool RootInlineBox::includeMarginForBox(InlineBox& box) const 1028{ 1029 if (box.renderer().isReplaced() || (box.renderer().isTextOrLineBreak() && !box.behavesLikeText())) 1030 return false; 1031 1032 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 1033 return lineBoxContain & LineBoxContainInlineBox; 1034} 1035 1036 1037bool RootInlineBox::fitsToGlyphs() const 1038{ 1039 // FIXME: We can't fit to glyphs yet for vertical text, since the bounds returned are garbage. 1040 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 1041 return isHorizontal() && (lineBoxContain & LineBoxContainGlyphs); 1042} 1043 1044bool RootInlineBox::includesRootLineBoxFontOrLeading() const 1045{ 1046 LineBoxContain lineBoxContain = renderer().style().lineBoxContain(); 1047 return (lineBoxContain & LineBoxContainBlock) || (lineBoxContain & LineBoxContainInline) || (lineBoxContain & LineBoxContainFont); 1048} 1049 1050Node* RootInlineBox::getLogicalStartBoxWithNode(InlineBox*& startBox) const 1051{ 1052 Vector<InlineBox*> leafBoxesInLogicalOrder; 1053 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); 1054 for (size_t i = 0; i < leafBoxesInLogicalOrder.size(); ++i) { 1055 if (leafBoxesInLogicalOrder[i]->renderer().node()) { 1056 startBox = leafBoxesInLogicalOrder[i]; 1057 return startBox->renderer().node(); 1058 } 1059 } 1060 startBox = 0; 1061 return 0; 1062} 1063 1064Node* RootInlineBox::getLogicalEndBoxWithNode(InlineBox*& endBox) const 1065{ 1066 Vector<InlineBox*> leafBoxesInLogicalOrder; 1067 collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder); 1068 for (size_t i = leafBoxesInLogicalOrder.size(); i > 0; --i) { 1069 if (leafBoxesInLogicalOrder[i - 1]->renderer().node()) { 1070 endBox = leafBoxesInLogicalOrder[i - 1]; 1071 return endBox->renderer().node(); 1072 } 1073 } 1074 endBox = 0; 1075 return 0; 1076} 1077 1078#ifndef NDEBUG 1079const char* RootInlineBox::boxName() const 1080{ 1081 return "RootInlineBox"; 1082} 1083#endif 1084 1085} // namespace WebCore 1086