Home | History | Annotate | Download | only in rendering
      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