• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  } // namespace WebCore
817