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 
RootInlineBox(RenderBlock * block)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 
destroy(RenderArena * arena)61 void RootInlineBox::destroy(RenderArena* arena)
62 {
63     detachEllipsisBox(arena);
64     InlineFlowBox::destroy(arena);
65 }
66 
detachEllipsisBox(RenderArena * arena)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 
rendererLineBoxes() const77 RenderLineBoxList* RootInlineBox::rendererLineBoxes() const
78 {
79     return block()->lineBoxes();
80 }
81 
clearTruncation()82 void RootInlineBox::clearTruncation()
83 {
84     if (hasEllipsisBox()) {
85         detachEllipsisBox(renderer()->renderArena());
86         InlineFlowBox::clearTruncation();
87     }
88 }
89 
lineCanAccommodateEllipsis(bool ltr,int blockEdge,int lineBoxEdge,int ellipsisWidth)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 
placeEllipsis(const AtomicString & ellipsisStr,bool ltr,float blockLeftEdge,float blockRightEdge,float ellipsisWidth,InlineBox * markupBox)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 
placeEllipsisBox(bool ltr,float blockLeftEdge,float blockRightEdge,float ellipsisWidth,bool & foundBox)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 
paintEllipsisBox(PaintInfo & paintInfo,int tx,int ty,int lineTop,int lineBottom) const136 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 
addHighlightOverflow()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 
paintCustomHighlight(PaintInfo & paintInfo,int tx,int ty,const AtomicString & highlightType)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 
paint(PaintInfo & paintInfo,int tx,int ty,int lineTop,int lineBottom)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 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,int lineTop,int lineBottom)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 
adjustPosition(float dx,float dy)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 
childRemoved(InlineBox * box)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 
alignBoxesInBlockDirection(int heightOfBlock,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,VerticalPositionCache & verticalPositionCache)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 
beforeAnnotationsAdjustment() const276 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 
lineSelectionGap(RenderBlock * rootBlock,const IntPoint & rootBlockPhysicalPosition,const IntSize & offsetFromRootBlock,int selTop,int selHeight,const PaintInfo * paintInfo)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 
selectionState()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 
firstSelectedBox()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 
lastSelectedBox()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 
selectionTop() const399 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 
selectionBottom() const425 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 
block() const451 RenderBlock* RootInlineBox::block() const
452 {
453     return toRenderBlock(renderer());
454 }
455 
isEditableLeaf(InlineBox * leaf)456 static bool isEditableLeaf(InlineBox* leaf)
457 {
458     return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->rendererIsEditable();
459 }
460 
closestLeafChildForLogicalLeftPosition(int leftPosition,bool onlyEditableLeaves)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 
lineBreakBidiStatus() const493 BidiStatus RootInlineBox::lineBreakBidiStatus() const
494 {
495     return BidiStatus(m_lineBreakBidiStatusEor, m_lineBreakBidiStatusLastStrong, m_lineBreakBidiStatusLast, m_lineBreakContext);
496 }
497 
setLineBreakInfo(RenderObject * obj,unsigned breakPos,const BidiStatus & status)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 
ellipsisBox() const508 EllipsisBox* RootInlineBox::ellipsisBox() const
509 {
510     if (!hasEllipsisBox())
511         return 0;
512     return gEllipsisBoxMap->get(this);
513 }
514 
removeLineBoxFromRenderObject()515 void RootInlineBox::removeLineBoxFromRenderObject()
516 {
517     block()->lineBoxes()->removeLineBox(this);
518 }
519 
extractLineBoxFromRenderObject()520 void RootInlineBox::extractLineBoxFromRenderObject()
521 {
522     block()->lineBoxes()->extractLineBox(this);
523 }
524 
attachLineBoxToRenderObject()525 void RootInlineBox::attachLineBoxToRenderObject()
526 {
527     block()->lineBoxes()->attachLineBox(this);
528 }
529 
paddedLayoutOverflowRect(int endPadding) const530 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 
setAscentAndDescent(int & ascent,int & descent,int newAscent,int newDescent,bool & ascentDescentSet)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 
ascentAndDescentForBox(InlineBox * box,GlyphOverflowAndFallbackFontsMap & textBoxDataMap,int & ascent,int & descent,bool & affectsAscent,bool & affectsDescent) const563 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 
verticalPositionForBox(InlineBox * box,VerticalPositionCache & verticalPositionCache)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 
includeLeadingForBox(InlineBox * box) const730 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 
includeFontForBox(InlineBox * box) const739 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 
includeGlyphsForBox(InlineBox * box) const752 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 
includeMarginForBox(InlineBox * box) const765 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 
fitsToGlyphs() const775 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 
includesRootLineBoxFontOrLeading() const782 bool RootInlineBox::includesRootLineBoxFontOrLeading() const
783 {
784     LineBoxContain lineBoxContain = renderer()->style()->lineBoxContain();
785     return (lineBoxContain & LineBoxContainBlock) || (lineBoxContain & LineBoxContainInline) || (lineBoxContain & LineBoxContainFont);
786 }
787 
getLogicalStartBoxWithNode(InlineBox * & startBox) const788 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 
getLogicalEndBoxWithNode(InlineBox * & endBox) const802 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 #ifndef NDEBUG
boxName() const817 const char* RootInlineBox::boxName() const
818 {
819     return "RootInlineBox";
820 }
821 #endif
822 
823 } // namespace WebCore
824