2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
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.
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.
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.
21 #include "InlineFlowBox.h"
23 #include "CSSPropertyNames.h"
25 #include "EllipsisBox.h"
27 #include "GraphicsContext.h"
28 #include "InlineTextBox.h"
29 #include "HitTestResult.h"
30 #include "RenderBlock.h"
31 #include "RenderInline.h"
32 #include "RenderLayer.h"
33 #include "RenderListMarker.h"
34 #include "RenderRubyBase.h"
35 #include "RenderRubyRun.h"
36 #include "RenderRubyText.h"
37 #include "RenderTableCell.h"
38 #include "RenderView.h"
39 #include "RootInlineBox.h"
45 struct SameSizeAsInlineFlowBox : public InlineBox {
47 uint32_t bitfields : 23;
50 COMPILE_ASSERT(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), InlineFlowBox_should_stay_small);
52 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
54 InlineFlowBox::~InlineFlowBox()
59 void InlineFlowBox::setHasBadChildList()
62 if (m_hasBadChildList)
64 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
65 child->setHasBadParent();
66 m_hasBadChildList = true;
71 LayoutUnit InlineFlowBox::getFlowSpacingLogicalWidth()
73 LayoutUnit totWidth = marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight();
74 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
75 if (curr->isInlineFlowBox())
76 totWidth += toInlineFlowBox(curr)->getFlowSpacingLogicalWidth();
81 IntRect InlineFlowBox::roundedFrameRect() const
83 // Begin by snapping the x and y coordinates to the nearest pixel.
84 int snappedX = lroundf(x());
85 int snappedY = lroundf(y());
87 int snappedMaxX = lroundf(x() + width());
88 int snappedMaxY = lroundf(y() + height());
90 return IntRect(snappedX, snappedY, snappedMaxX - snappedX, snappedMaxY - snappedY);
93 static void setHasTextDescendantsOnAncestors(InlineFlowBox* box)
95 while (box && !box->hasTextDescendants()) {
96 box->setHasTextDescendants();
101 void InlineFlowBox::addToLine(InlineBox* child)
103 ASSERT(!child->parent());
104 ASSERT(!child->nextOnLine());
105 ASSERT(!child->prevOnLine());
108 child->setParent(this);
110 m_firstChild = child;
113 m_lastChild->setNextOnLine(child);
114 child->setPrevOnLine(m_lastChild);
117 child->setIsFirstLine(isFirstLine());
118 child->setIsHorizontal(isHorizontal());
119 if (child->behavesLikeText()) {
120 if (child->renderer().parent() == &renderer())
121 m_hasTextChildren = true;
122 setHasTextDescendantsOnAncestors(this);
123 } else if (child->isInlineFlowBox()) {
124 if (toInlineFlowBox(child)->hasTextDescendants())
125 setHasTextDescendantsOnAncestors(this);
128 if (descendantsHaveSameLineHeightAndBaseline() && !child->renderer().isOutOfFlowPositioned()) {
129 const RenderStyle& parentStyle = lineStyle();
130 const RenderStyle& childStyle = child->lineStyle();
131 bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false;
132 if (child->renderer().isReplaced())
133 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
134 else if (child->behavesLikeText()) {
135 if (child->renderer().isLineBreak() || child->renderer().parent() != &renderer()) {
136 if (!parentStyle.font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.font().fontMetrics())
137 || parentStyle.lineHeight() != childStyle.lineHeight()
138 || (parentStyle.verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle.verticalAlign() != BASELINE)
139 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
141 if (childStyle.hasTextCombine() || childStyle.textEmphasisMark() != TextEmphasisMarkNone)
142 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
144 if (child->renderer().isLineBreak()) {
145 // FIXME: This is dumb. We only turn off because current layout test results expect the <br> to be 0-height on the baseline.
146 // Other than making a zillion tests have to regenerate results, there's no reason to ditch the optimization here.
147 shouldClearDescendantsHaveSameLineHeightAndBaseline = child->renderer().isBR();
149 ASSERT(isInlineFlowBox());
150 InlineFlowBox* childFlowBox = toInlineFlowBox(child);
151 // Check the child's bit, and then also check for differences in font, line-height, vertical-align
152 if (!childFlowBox->descendantsHaveSameLineHeightAndBaseline()
153 || !parentStyle.font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.font().fontMetrics())
154 || parentStyle.lineHeight() != childStyle.lineHeight()
155 || (parentStyle.verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle.verticalAlign() != BASELINE
156 || childStyle.hasBorder() || childStyle.hasPadding() || childStyle.hasTextCombine())
157 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
161 if (shouldClearDescendantsHaveSameLineHeightAndBaseline)
162 clearDescendantsHaveSameLineHeightAndBaseline();
165 if (!child->renderer().isOutOfFlowPositioned()) {
166 const RenderStyle& childStyle = child->lineStyle();
167 if (child->behavesLikeText()) {
168 const RenderStyle* childStyle = &child->lineStyle();
169 if (childStyle->letterSpacing() < 0 || childStyle->textShadow() || childStyle->textEmphasisMark() != TextEmphasisMarkNone || childStyle->textStrokeWidth())
170 child->clearKnownToHaveNoOverflow();
171 } else if (child->renderer().isReplaced()) {
172 const RenderBox& box = toRenderBox(child->renderer());
173 if (box.hasRenderOverflow() || box.hasSelfPaintingLayer())
174 child->clearKnownToHaveNoOverflow();
175 } else if (!child->renderer().isLineBreak() && (childStyle.boxShadow() || child->boxModelObject()->hasSelfPaintingLayer()
176 || (child->renderer().isListMarker() && !toRenderListMarker(child->renderer()).isInside())
177 || childStyle.hasBorderImageOutsets()))
178 child->clearKnownToHaveNoOverflow();
180 if (knownToHaveNoOverflow() && child->isInlineFlowBox() && !toInlineFlowBox(child)->knownToHaveNoOverflow())
181 clearKnownToHaveNoOverflow();
187 void InlineFlowBox::removeChild(InlineBox* child)
194 root().childRemoved(child);
196 if (child == m_firstChild)
197 m_firstChild = child->nextOnLine();
198 if (child == m_lastChild)
199 m_lastChild = child->prevOnLine();
200 if (child->nextOnLine())
201 child->nextOnLine()->setPrevOnLine(child->prevOnLine());
202 if (child->prevOnLine())
203 child->prevOnLine()->setNextOnLine(child->nextOnLine());
210 void InlineFlowBox::deleteLine()
212 InlineBox* child = firstChild();
215 ASSERT(this == child->parent());
216 next = child->nextOnLine();
228 removeLineBoxFromRenderObject();
232 void InlineFlowBox::removeLineBoxFromRenderObject()
234 toRenderInline(renderer()).lineBoxes().removeLineBox(this);
237 void InlineFlowBox::extractLine()
240 extractLineBoxFromRenderObject();
241 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
242 child->extractLine();
245 void InlineFlowBox::extractLineBoxFromRenderObject()
247 toRenderInline(renderer()).lineBoxes().extractLineBox(this);
250 void InlineFlowBox::attachLine()
253 attachLineBoxToRenderObject();
254 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
258 void InlineFlowBox::attachLineBoxToRenderObject()
260 toRenderInline(renderer()).lineBoxes().attachLineBox(this);
263 void InlineFlowBox::adjustPosition(float dx, float dy)
265 InlineBox::adjustPosition(dx, dy);
266 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
267 child->adjustPosition(dx, dy);
269 m_overflow->move(dx, dy); // FIXME: Rounding error here since overflow was pixel snapped, but nobody other than list markers passes non-integral values here.
272 static inline bool isLastChildForRenderer(const RenderElement& ancestor, const RenderObject* child)
277 if (child == &ancestor)
280 const RenderObject* curr = child;
281 const RenderElement* parent = curr->parent();
282 while (parent && (!parent->isRenderBlock() || parent->isInline())) {
283 if (parent->lastChild() != curr)
285 if (parent == &ancestor)
289 parent = curr->parent();
295 static bool isAncestorAndWithinBlock(const RenderInline& ancestor, const RenderObject* child)
297 const RenderObject* object = child;
298 while (object && (!object->isRenderBlock() || object->isInline())) {
299 if (object == &ancestor)
301 object = object->parent();
306 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer)
308 // All boxes start off open. They will not apply any margins/border/padding on
310 bool includeLeftEdge = false;
311 bool includeRightEdge = false;
313 // The root inline box never has borders/margins/padding.
315 const auto& inlineFlow = toRenderInline(renderer());
317 bool ltr = renderer().style().isLeftToRightDirection();
319 // Check to see if all initial lines are unconstructed. If so, then
320 // we know the inline began on this line (unless we are a continuation).
321 const auto& lineBoxList = inlineFlow.lineBoxes();
322 if (!lineBoxList.firstLineBox()->isConstructed() && !renderer().isInlineElementContinuation()) {
323 #if ENABLE(CSS_BOX_DECORATION_BREAK)
324 if (renderer().style().boxDecorationBreak() == DCLONE)
325 includeLeftEdge = includeRightEdge = true;
328 if (ltr && lineBoxList.firstLineBox() == this)
329 includeLeftEdge = true;
330 else if (!ltr && lineBoxList.lastLineBox() == this)
331 includeRightEdge = true;
334 if (!lineBoxList.lastLineBox()->isConstructed()) {
335 bool isLastObjectOnLine = !isAncestorAndWithinBlock(inlineFlow, logicallyLastRunRenderer) || (isLastChildForRenderer(renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped);
337 // We include the border under these conditions:
338 // (1) The next line was not created, or it is constructed. We check the previous line for rtl.
339 // (2) The logicallyLastRun is not a descendant of this renderer.
340 // (3) The logicallyLastRun is a descendant of this renderer, but it is the last child of this renderer and it does not wrap to the next line.
341 #if ENABLE(CSS_BOX_DECORATION_BREAK)
342 // (4) The decoration break is set to clone therefore there will be borders on every sides.
343 if (renderer().style().boxDecorationBreak() == DCLONE)
344 includeLeftEdge = includeRightEdge = true;
349 && ((lastLine || isLastObjectOnLine) && !inlineFlow.continuation()))
350 includeRightEdge = true;
352 if ((!prevLineBox() || prevLineBox()->isConstructed())
353 && ((lastLine || isLastObjectOnLine) && !inlineFlow.continuation()))
354 includeLeftEdge = true;
359 setEdges(includeLeftEdge, includeRightEdge);
361 // Recur into our children.
362 for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) {
363 if (currChild->isInlineFlowBox()) {
364 InlineFlowBox* currFlow = toInlineFlowBox(currChild);
365 currFlow->determineSpacingForFlowBoxes(lastLine, isLogicallyLastRunWrapped, logicallyLastRunRenderer);
370 float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing)
372 // Set our x position.
373 beginPlacingBoxRangesInInlineDirection(logicalLeft);
375 float startLogicalLeft = logicalLeft;
376 logicalLeft += borderLogicalLeft() + paddingLogicalLeft();
378 float minLogicalLeft = startLogicalLeft;
379 float maxLogicalRight = logicalLeft;
381 placeBoxRangeInInlineDirection(firstChild(), 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing);
383 logicalLeft += borderLogicalRight() + paddingLogicalRight();
384 endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, minLogicalLeft, maxLogicalRight);
388 float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing)
390 for (InlineBox* curr = firstChild; curr && curr != lastChild; curr = curr->nextOnLine()) {
391 if (curr->renderer().isText()) {
392 InlineTextBox* text = toInlineTextBox(curr);
393 RenderText& rt = text->renderer();
394 if (rt.textLength()) {
395 if (needsWordSpacing && isSpaceOrNewline(rt.characterAt(text->start())))
396 logicalLeft += text->lineStyle().font().wordSpacing();
397 needsWordSpacing = !isSpaceOrNewline(rt.characterAt(text->end()));
399 text->setLogicalLeft(logicalLeft);
400 if (knownToHaveNoOverflow())
401 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
402 logicalLeft += text->logicalWidth();
403 if (knownToHaveNoOverflow())
404 maxLogicalRight = std::max(logicalLeft, maxLogicalRight);
406 if (curr->renderer().isOutOfFlowPositioned()) {
407 if (curr->renderer().parent()->style().isLeftToRightDirection())
408 curr->setLogicalLeft(logicalLeft);
410 // Our offset that we cache needs to be from the edge of the right border box and
411 // not the left border box. We have to subtract |x| from the width of the block
412 // (which can be obtained from the root line box).
413 curr->setLogicalLeft(root().blockFlow().logicalWidth() - logicalLeft);
414 continue; // The positioned object has no effect on the width.
416 if (curr->renderer().isRenderInline()) {
417 InlineFlowBox* flow = toInlineFlowBox(curr);
418 logicalLeft += flow->marginLogicalLeft();
419 if (knownToHaveNoOverflow())
420 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
421 logicalLeft = flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing);
422 if (knownToHaveNoOverflow())
423 maxLogicalRight = std::max(logicalLeft, maxLogicalRight);
424 logicalLeft += flow->marginLogicalRight();
425 } else if (!curr->renderer().isListMarker() || toRenderListMarker(curr->renderer()).isInside()) {
426 // The box can have a different writing-mode than the overall line, so this is a bit complicated.
427 // Just get all the physical margin and overflow values by hand based off |isVertical|.
428 LayoutUnit logicalLeftMargin = isHorizontal() ? curr->boxModelObject()->marginLeft() : curr->boxModelObject()->marginTop();
429 LayoutUnit logicalRightMargin = isHorizontal() ? curr->boxModelObject()->marginRight() : curr->boxModelObject()->marginBottom();
431 logicalLeft += logicalLeftMargin;
432 curr->setLogicalLeft(logicalLeft);
433 if (knownToHaveNoOverflow())
434 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
435 logicalLeft += curr->logicalWidth();
436 if (knownToHaveNoOverflow())
437 maxLogicalRight = std::max(logicalLeft, maxLogicalRight);
438 logicalLeft += logicalRightMargin;
439 // If we encounter any space after this inline block then ensure it is treated as the space between two words.
440 needsWordSpacing = true;
447 bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap& textBoxDataMap) const
452 const RenderStyle& lineStyle = this->lineStyle();
453 if (lineStyle.fontDescription().nonCJKGlyphOrientation() == NonCJKGlyphOrientationUpright
454 || lineStyle.font().primaryFont()->hasVerticalGlyphs())
457 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
458 if (curr->renderer().isOutOfFlowPositioned())
459 continue; // Positioned placeholders don't affect calculations.
461 if (curr->isInlineFlowBox()) {
462 if (toInlineFlowBox(curr)->requiresIdeographicBaseline(textBoxDataMap))
465 if (curr->lineStyle().font().primaryFont()->hasVerticalGlyphs())
468 const Vector<const SimpleFontData*>* usedFonts = 0;
469 if (curr->isInlineTextBox()) {
470 GlyphOverflowAndFallbackFontsMap::const_iterator it = textBoxDataMap.find(toInlineTextBox(curr));
471 usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first;
475 for (size_t i = 0; i < usedFonts->size(); ++i) {
476 if (usedFonts->at(i)->hasVerticalGlyphs())
486 static bool verticalAlignApplies(const RenderObject& renderer)
488 // http://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align - vertical-align
489 // only applies to inline level and table-cell elements
490 return !renderer.isText() || renderer.parent()->isInline() || renderer.parent()->isTableCell();
493 void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom)
495 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
496 // The computed lineheight needs to be extended for the
497 // positioned elements
498 if (curr->renderer().isOutOfFlowPositioned())
499 continue; // Positioned placeholders don't affect calculations.
501 if ((curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) && verticalAlignApplies(curr->renderer())) {
502 int lineHeight = curr->lineHeight();
503 if (curr->verticalAlign() == TOP) {
504 if (maxAscent + maxDescent < lineHeight)
505 maxDescent = lineHeight - maxAscent;
508 if (maxAscent + maxDescent < lineHeight)
509 maxAscent = lineHeight - maxDescent;
512 if (maxAscent + maxDescent >= std::max(maxPositionTop, maxPositionBottom))
516 if (curr->isInlineFlowBox())
517 toInlineFlowBox(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
521 void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox* rootBox, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom,
522 int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent,
523 bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
524 FontBaseline baselineType, VerticalPositionCache& verticalPositionCache)
526 // The primary purpose of this function is to compute the maximal ascent and descent values for
527 // a line. These values are computed based off the block's line-box-contain property, which indicates
528 // what parts of descendant boxes have to fit within the line.
530 // The maxAscent value represents the distance of the highest point of any box (typically including line-height) from
531 // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box
532 // (also typically including line-height) from the root box baseline. These values can be negative.
534 // A secondary purpose of this function is to store the offset of every box's baseline from the root box's
535 // baseline. This information is cached in the logicalTop() of every box. We're effectively just using
536 // the logicalTop() as scratch space.
538 // Because a box can be positioned such that it ends up fully above or fully below the
539 // root line box, we only consider it to affect the maxAscent and maxDescent values if some
540 // part of the box (EXCLUDING leading) is above (for ascent) or below (for descent) the root box's baseline.
541 bool affectsAscent = false;
542 bool affectsDescent = false;
543 bool checkChildren = !descendantsHaveSameLineHeightAndBaseline();
545 if (isRootInlineBox()) {
546 // Examine our root box.
549 rootBox->ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent);
550 if (strictMode || hasTextChildren() || (!checkChildren && hasTextDescendants())) {
551 if (maxAscent < ascent || !setMaxAscent) {
555 if (maxDescent < descent || !setMaxDescent) {
556 maxDescent = descent;
557 setMaxDescent = true;
565 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
566 if (curr->renderer().isOutOfFlowPositioned())
567 continue; // Positioned placeholders don't affect calculations.
569 InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0;
571 bool affectsAscent = false;
572 bool affectsDescent = false;
574 // The verticalPositionForBox function returns the distance between the child box's baseline
575 // and the root box's baseline. The value is negative if the child box's baseline is above the
576 // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline.
577 curr->setLogicalTop(rootBox->verticalPositionForBox(curr, verticalPositionCache));
581 rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent);
583 LayoutUnit boxHeight = ascent + descent;
584 if (curr->verticalAlign() == TOP && verticalAlignApplies(curr->renderer())) {
585 if (maxPositionTop < boxHeight)
586 maxPositionTop = boxHeight;
587 } else if (curr->verticalAlign() == BOTTOM && verticalAlignApplies(curr->renderer())) {
588 if (maxPositionBottom < boxHeight)
589 maxPositionBottom = boxHeight;
590 } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants())
591 || inlineFlowBox->renderer().hasInlineDirectionBordersOrPadding()) {
592 // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values
593 // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height
594 // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it
595 // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and
596 // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative
598 ascent -= curr->logicalTop();
599 descent += curr->logicalTop();
600 if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) {
605 if (affectsDescent && (maxDescent < descent || !setMaxDescent)) {
606 maxDescent = descent;
607 setMaxDescent = true;
612 inlineFlowBox->computeLogicalBoxHeights(rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent,
613 setMaxAscent, setMaxDescent, strictMode, textBoxDataMap,
614 baselineType, verticalPositionCache);
618 void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, bool& setLineTop,
619 LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType)
621 bool isRootBox = isRootInlineBox();
623 const FontMetrics& fontMetrics = lineStyle().fontMetrics();
624 // RootInlineBoxes are always placed on at pixel boundaries in their logical y direction. Not doing
625 // so results in incorrect rendering of text decorations, most notably underlines.
626 setLogicalTop(roundToInt(top + maxAscent - fontMetrics.ascent(baselineType)));
629 LayoutUnit adjustmentForChildrenWithSameLineHeightAndBaseline = 0;
630 if (descendantsHaveSameLineHeightAndBaseline()) {
631 adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop();
633 adjustmentForChildrenWithSameLineHeightAndBaseline += renderer().borderAndPaddingBefore();
636 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
637 if (curr->renderer().isOutOfFlowPositioned())
638 continue; // Positioned placeholders don't affect calculations.
640 if (descendantsHaveSameLineHeightAndBaseline()) {
641 curr->adjustBlockDirectionPosition(adjustmentForChildrenWithSameLineHeightAndBaseline);
645 InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0;
646 bool childAffectsTopBottomPos = true;
648 if (curr->verticalAlign() == TOP && verticalAlignApplies(curr->renderer()))
649 curr->setLogicalTop(top);
650 else if (curr->verticalAlign() == BOTTOM && verticalAlignApplies(curr->renderer()))
651 curr->setLogicalTop(top + maxHeight - curr->lineHeight());
653 if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !inlineFlowBox->renderer().hasInlineDirectionBordersOrPadding()
654 && !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()))
655 childAffectsTopBottomPos = false;
656 LayoutUnit posAdjust = maxAscent - curr->baselinePosition(baselineType);
657 curr->setLogicalTop(curr->logicalTop() + top + posAdjust);
660 LayoutUnit newLogicalTop = curr->logicalTop();
661 LayoutUnit newLogicalTopIncludingMargins = newLogicalTop;
662 LayoutUnit boxHeight = curr->logicalHeight();
663 LayoutUnit boxHeightIncludingMargins = boxHeight;
665 const RenderStyle& childLineStyle = curr->lineStyle();
666 if (curr->behavesLikeText() || curr->isInlineFlowBox()) {
667 const FontMetrics& fontMetrics = childLineStyle.fontMetrics();
668 newLogicalTop += curr->baselinePosition(baselineType) - fontMetrics.ascent(baselineType);
669 if (curr->isInlineFlowBox()) {
670 RenderBoxModelObject& boxObject = toRenderBoxModelObject(curr->renderer());
671 newLogicalTop -= childLineStyle.isHorizontalWritingMode()
672 ? boxObject.borderTop() + boxObject.paddingTop()
673 : boxObject.borderRight() + boxObject.paddingRight();
675 newLogicalTopIncludingMargins = newLogicalTop;
676 } else if (!curr->renderer().isBR()) {
677 const RenderBox& box = toRenderBox(curr->renderer());
678 newLogicalTopIncludingMargins = newLogicalTop;
679 LayoutUnit overSideMargin = curr->isHorizontal() ? box.marginTop() : box.marginRight();
680 LayoutUnit underSideMargin = curr->isHorizontal() ? box.marginBottom() : box.marginLeft();
681 newLogicalTop += overSideMargin;
682 boxHeightIncludingMargins += overSideMargin + underSideMargin;
685 curr->setLogicalTop(newLogicalTop);
687 if (childAffectsTopBottomPos) {
688 if (curr->renderer().isRubyRun()) {
689 // Treat the leading on the first and last lines of ruby runs as not being part of the overall lineTop/lineBottom.
690 // Really this is a workaround hack for the fact that ruby should have been done as line layout and not done using
692 if (renderer().style().isFlippedLinesWritingMode() == (curr->renderer().style().rubyPosition() == RubyPositionAfter))
693 hasAnnotationsBefore = true;
695 hasAnnotationsAfter = true;
697 RenderRubyRun& rubyRun = toRenderRubyRun(curr->renderer());
698 if (RenderRubyBase* rubyBase = rubyRun.rubyBase()) {
699 LayoutUnit bottomRubyBaseLeading = (curr->logicalHeight() - rubyBase->logicalBottom()) + rubyBase->logicalHeight() - (rubyBase->lastRootBox() ? rubyBase->lastRootBox()->lineBottom() : LayoutUnit());
700 LayoutUnit topRubyBaseLeading = rubyBase->logicalTop() + (rubyBase->firstRootBox() ? rubyBase->firstRootBox()->lineTop() : LayoutUnit());
701 newLogicalTop += !renderer().style().isFlippedLinesWritingMode() ? topRubyBaseLeading : bottomRubyBaseLeading;
702 boxHeight -= (topRubyBaseLeading + bottomRubyBaseLeading);
705 if (curr->isInlineTextBox()) {
706 bool emphasisMarkIsOver;
707 if (toInlineTextBox(curr)->emphasisMarkExistsAndIsAbove(childLineStyle, emphasisMarkIsOver)) {
708 if (emphasisMarkIsOver != childLineStyle.isFlippedLinesWritingMode())
709 hasAnnotationsBefore = true;
711 hasAnnotationsAfter = true;
717 lineTop = newLogicalTop;
718 lineTopIncludingMargins = std::min(lineTop, newLogicalTopIncludingMargins);
720 lineTop = std::min(lineTop, newLogicalTop);
721 lineTopIncludingMargins = std::min(lineTop, std::min(lineTopIncludingMargins, newLogicalTopIncludingMargins));
723 lineBottom = std::max(lineBottom, newLogicalTop + boxHeight);
724 lineBottomIncludingMargins = std::max(lineBottom, std::max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins));
727 // Adjust boxes to use their real box y/height and not the logical height (as dictated by
730 inlineFlowBox->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop,
731 lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType);
735 if (strictMode || hasTextChildren() || (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
738 lineTop = pixelSnappedLogicalTop();
739 lineTopIncludingMargins = lineTop;
741 lineTop = std::min<LayoutUnit>(lineTop, pixelSnappedLogicalTop());
742 lineTopIncludingMargins = std::min(lineTop, lineTopIncludingMargins);
744 lineBottom = std::max<LayoutUnit>(lineBottom, pixelSnappedLogicalBottom());
745 lineBottomIncludingMargins = std::max(lineBottom, lineBottomIncludingMargins);
748 if (renderer().style().isFlippedLinesWritingMode())
749 flipLinesInBlockDirection(lineTopIncludingMargins, lineBottomIncludingMargins);
753 void InlineFlowBox::computeMaxLogicalTop(float& maxLogicalTop) const
755 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
756 if (curr->renderer().isOutOfFlowPositioned())
757 continue; // Positioned placeholders don't affect calculations.
759 if (descendantsHaveSameLineHeightAndBaseline())
762 maxLogicalTop = std::max<float>(maxLogicalTop, curr->y());
763 float localMaxLogicalTop = 0;
764 if (curr->isInlineFlowBox())
765 toInlineFlowBox(curr)->computeMaxLogicalTop(localMaxLogicalTop);
766 maxLogicalTop = std::max<float>(maxLogicalTop, localMaxLogicalTop);
770 void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom)
772 // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop.
773 setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight());
775 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
776 if (curr->renderer().isOutOfFlowPositioned())
777 continue; // Positioned placeholders aren't affected here.
779 if (curr->isInlineFlowBox())
780 toInlineFlowBox(curr)->flipLinesInBlockDirection(lineTop, lineBottom);
782 curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - curr->logicalHeight());
786 inline void InlineFlowBox::addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow)
788 // box-shadow on root line boxes is applying to the block and not to the lines.
792 const RenderStyle& lineStyle = this->lineStyle();
793 if (!lineStyle.boxShadow())
796 LayoutUnit boxShadowLogicalTop;
797 LayoutUnit boxShadowLogicalBottom;
798 lineStyle.getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, boxShadowLogicalBottom);
800 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite shadow that applies, since
801 // the line is "upside down" in terms of block coordinates.
802 LayoutUnit shadowLogicalTop = lineStyle.isFlippedLinesWritingMode() ? -boxShadowLogicalBottom : boxShadowLogicalTop;
803 LayoutUnit shadowLogicalBottom = lineStyle.isFlippedLinesWritingMode() ? -boxShadowLogicalTop : boxShadowLogicalBottom;
805 LayoutUnit logicalTopVisualOverflow = std::min(pixelSnappedLogicalTop() + shadowLogicalTop, logicalVisualOverflow.y());
806 LayoutUnit logicalBottomVisualOverflow = std::max(pixelSnappedLogicalBottom() + shadowLogicalBottom, logicalVisualOverflow.maxY());
808 LayoutUnit boxShadowLogicalLeft;
809 LayoutUnit boxShadowLogicalRight;
810 lineStyle.getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight);
812 LayoutUnit logicalLeftVisualOverflow = std::min(pixelSnappedLogicalLeft() + boxShadowLogicalLeft, logicalVisualOverflow.x());
813 LayoutUnit logicalRightVisualOverflow = std::max(pixelSnappedLogicalRight() + boxShadowLogicalRight, logicalVisualOverflow.maxX());
815 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
816 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
819 inline void InlineFlowBox::addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow)
821 // border-image-outset on root line boxes is applying to the block and not to the lines.
825 const RenderStyle& lineStyle = this->lineStyle();
826 if (!lineStyle.hasBorderImageOutsets())
829 LayoutBoxExtent borderOutsets = lineStyle.borderImageOutsets();
831 LayoutUnit borderOutsetLogicalTop = borderOutsets.logicalTop(lineStyle.writingMode());
832 LayoutUnit borderOutsetLogicalBottom = borderOutsets.logicalBottom(lineStyle.writingMode());
833 LayoutUnit borderOutsetLogicalLeft = borderOutsets.logicalLeft(lineStyle.writingMode());
834 LayoutUnit borderOutsetLogicalRight = borderOutsets.logicalRight(lineStyle.writingMode());
836 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite border that applies, since
837 // the line is "upside down" in terms of block coordinates. vertical-rl and horizontal-bt are the flipped line modes.
838 LayoutUnit outsetLogicalTop = lineStyle.isFlippedLinesWritingMode() ? borderOutsetLogicalBottom : borderOutsetLogicalTop;
839 LayoutUnit outsetLogicalBottom = lineStyle.isFlippedLinesWritingMode() ? borderOutsetLogicalTop : borderOutsetLogicalBottom;
841 LayoutUnit logicalTopVisualOverflow = std::min(pixelSnappedLogicalTop() - outsetLogicalTop, logicalVisualOverflow.y());
842 LayoutUnit logicalBottomVisualOverflow = std::max(pixelSnappedLogicalBottom() + outsetLogicalBottom, logicalVisualOverflow.maxY());
844 LayoutUnit outsetLogicalLeft = includeLogicalLeftEdge() ? borderOutsetLogicalLeft : LayoutUnit();
845 LayoutUnit outsetLogicalRight = includeLogicalRightEdge() ? borderOutsetLogicalRight : LayoutUnit();
847 LayoutUnit logicalLeftVisualOverflow = std::min(pixelSnappedLogicalLeft() - outsetLogicalLeft, logicalVisualOverflow.x());
848 LayoutUnit logicalRightVisualOverflow = std::max(pixelSnappedLogicalRight() + outsetLogicalRight, logicalVisualOverflow.maxX());
850 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
851 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
854 inline void InlineFlowBox::addTextBoxVisualOverflow(InlineTextBox* textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, LayoutRect& logicalVisualOverflow)
856 if (textBox->knownToHaveNoOverflow())
859 const RenderStyle& lineStyle = this->lineStyle();
861 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(textBox);
862 GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second;
863 bool isFlippedLine = lineStyle.isFlippedLinesWritingMode();
865 int topGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->bottom : glyphOverflow->top) : 0;
866 int bottomGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->top : glyphOverflow->bottom) : 0;
867 int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0;
868 int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0;
870 int strokeOverflow = static_cast<int>(ceilf(lineStyle.textStrokeWidth() / 2.0f));
871 int topGlyphOverflow = -strokeOverflow - topGlyphEdge;
872 int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge;
873 int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge;
874 int rightGlyphOverflow = strokeOverflow + rightGlyphEdge;
876 bool emphasisMarkIsAbove;
877 if (lineStyle.textEmphasisMark() != TextEmphasisMarkNone && textBox->emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkIsAbove)) {
878 int emphasisMarkHeight = lineStyle.font().emphasisMarkHeight(lineStyle.textEmphasisMarkString());
879 if (emphasisMarkIsAbove == !lineStyle.isFlippedLinesWritingMode())
880 topGlyphOverflow = std::min(topGlyphOverflow, -emphasisMarkHeight);
882 bottomGlyphOverflow = std::max(bottomGlyphOverflow, emphasisMarkHeight);
885 // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is
886 // applied to the right, so this is not an issue with left overflow.
887 rightGlyphOverflow -= std::min(0, (int)lineStyle.font().letterSpacing());
889 LayoutUnit textShadowLogicalTop;
890 LayoutUnit textShadowLogicalBottom;
891 lineStyle.getTextShadowBlockDirectionExtent(textShadowLogicalTop, textShadowLogicalBottom);
893 LayoutUnit childOverflowLogicalTop = std::min<LayoutUnit>(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow);
894 LayoutUnit childOverflowLogicalBottom = std::max<LayoutUnit>(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow);
896 LayoutUnit textShadowLogicalLeft;
897 LayoutUnit textShadowLogicalRight;
898 lineStyle.getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight);
900 LayoutUnit childOverflowLogicalLeft = std::min<LayoutUnit>(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow);
901 LayoutUnit childOverflowLogicalRight = std::max<LayoutUnit>(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow);
903 LayoutUnit logicalTopVisualOverflow = std::min(textBox->pixelSnappedLogicalTop() + childOverflowLogicalTop, logicalVisualOverflow.y());
904 LayoutUnit logicalBottomVisualOverflow = std::max(textBox->pixelSnappedLogicalBottom() + childOverflowLogicalBottom, logicalVisualOverflow.maxY());
905 LayoutUnit logicalLeftVisualOverflow = std::min(textBox->pixelSnappedLogicalLeft() + childOverflowLogicalLeft, logicalVisualOverflow.x());
906 LayoutUnit logicalRightVisualOverflow = std::max(textBox->pixelSnappedLogicalRight() + childOverflowLogicalRight, logicalVisualOverflow.maxX());
908 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
909 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
911 textBox->setLogicalOverflowRect(logicalVisualOverflow);
914 inline void InlineFlowBox::addReplacedChildOverflow(const InlineBox* inlineBox, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow)
916 const RenderBox& box = toRenderBox(inlineBox->renderer());
918 // Visual overflow only propagates if the box doesn't have a self-painting layer. This rectangle does not include
919 // transforms or relative positioning (since those objects always have self-painting layers), but it does need to be adjusted
920 // for writing-mode differences.
921 if (!box.hasSelfPaintingLayer()) {
922 LayoutRect childLogicalVisualOverflow = box.logicalVisualOverflowRectForPropagation(&renderer().style());
923 childLogicalVisualOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop());
924 logicalVisualOverflow.unite(childLogicalVisualOverflow);
927 // Layout overflow internal to the child box only propagates if the child box doesn't have overflow clip set.
928 // Otherwise the child border box propagates as layout overflow. This rectangle must include transforms and relative positioning
929 // and be adjusted for writing-mode differences.
930 LayoutRect childLogicalLayoutOverflow = box.logicalLayoutOverflowRectForPropagation(&renderer().style());
931 childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop());
932 logicalLayoutOverflow.unite(childLogicalLayoutOverflow);
935 void InlineFlowBox::computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap& textBoxDataMap)
937 // If we know we have no overflow, we can just bail.
938 if (knownToHaveNoOverflow())
944 // Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored.
945 // Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in
946 // transforms, relative positioning, etc.
947 LayoutRect logicalLayoutOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom)));
948 LayoutRect logicalVisualOverflow(logicalLayoutOverflow);
950 addBoxShadowVisualOverflow(logicalVisualOverflow);
951 addBorderOutsetVisualOverflow(logicalVisualOverflow);
953 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
954 if (curr->renderer().isOutOfFlowPositioned())
955 continue; // Positioned placeholders don't affect calculations.
957 if (curr->renderer().isLineBreak())
959 if (curr->renderer().isText()) {
960 InlineTextBox* text = toInlineTextBox(curr);
961 LayoutRect textBoxOverflow(enclosingLayoutRect(text->logicalFrameRect()));
962 addTextBoxVisualOverflow(text, textBoxDataMap, textBoxOverflow);
963 logicalVisualOverflow.unite(textBoxOverflow);
964 } else if (curr->renderer().isRenderInline()) {
965 InlineFlowBox* flow = toInlineFlowBox(curr);
966 flow->computeOverflow(lineTop, lineBottom, textBoxDataMap);
967 if (!flow->renderer().hasSelfPaintingLayer())
968 logicalVisualOverflow.unite(flow->logicalVisualOverflowRect(lineTop, lineBottom));
969 LayoutRect childLayoutOverflow = flow->logicalLayoutOverflowRect(lineTop, lineBottom);
970 childLayoutOverflow.move(flow->renderer().relativePositionLogicalOffset());
971 logicalLayoutOverflow.unite(childLayoutOverflow);
973 addReplacedChildOverflow(curr, logicalLayoutOverflow, logicalVisualOverflow);
976 setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, lineTop, lineBottom);
979 void InlineFlowBox::setLayoutOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom)
981 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom));
982 if (frameBox.contains(rect) || rect.isEmpty())
986 m_overflow = adoptRef(new RenderOverflow(frameBox, frameBox));
988 m_overflow->setLayoutOverflow(rect);
991 void InlineFlowBox::setVisualOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom)
993 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom));
994 if (frameBox.contains(rect) || rect.isEmpty())
998 m_overflow = adoptRef(new RenderOverflow(frameBox, frameBox));
1000 m_overflow->setVisualOverflow(rect);
1003 void InlineFlowBox::setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom)
1005 LayoutRect layoutOverflow(isHorizontal() ? logicalLayoutOverflow : logicalLayoutOverflow.transposedRect());
1006 setLayoutOverflow(layoutOverflow, lineTop, lineBottom);
1008 LayoutRect visualOverflow(isHorizontal() ? logicalVisualOverflow : logicalVisualOverflow.transposedRect());
1009 setVisualOverflow(visualOverflow, lineTop, lineBottom);
1012 bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
1014 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom));
1015 flipForWritingMode(overflowRect);
1016 overflowRect.moveBy(accumulatedOffset);
1017 if (!locationInContainer.intersects(overflowRect))
1020 // Check children first.
1021 // We need to account for culled inline parents of the hit-tested nodes, so that they may also get included in area-based hit-tests.
1022 RenderElement* culledParent = 0;
1023 for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) {
1024 if (curr->renderer().isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) {
1025 RenderElement* newParent = 0;
1026 // Culled parents are only relevant for area-based hit-tests, so ignore it in point-based ones.
1027 if (locationInContainer.isRectBasedTest()) {
1028 newParent = curr->renderer().parent();
1029 if (newParent == &renderer())
1032 // Check the culled parent after all its children have been checked, to do this we wait until
1033 // we are about to test an element with a different parent.
1034 if (newParent != culledParent) {
1035 if (!newParent || !newParent->isDescendantOf(culledParent)) {
1036 while (culledParent && culledParent != &renderer() && culledParent != newParent) {
1037 if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset))
1039 culledParent = culledParent->parent();
1042 culledParent = newParent;
1044 if (curr->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) {
1045 renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
1050 // Check any culled ancestor of the final children tested.
1051 while (culledParent && culledParent != &renderer()) {
1052 if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset))
1054 culledParent = culledParent->parent();
1057 // Now check ourselves. Pixel snap hit testing.
1058 if (!visibleToHitTesting())
1061 // Do not hittest content beyond the ellipsis box.
1062 if (isRootInlineBox() && hasEllipsisBox()) {
1063 const EllipsisBox* ellipsisBox = root().ellipsisBox();
1064 LayoutRect boundsRect(roundedFrameRect());
1067 renderer().style().isLeftToRightDirection() ? boundsRect.shiftXEdgeTo(ellipsisBox->right()) : boundsRect.setWidth(ellipsisBox->left() - left());
1069 boundsRect.shiftYEdgeTo(ellipsisBox->right());
1071 flipForWritingMode(boundsRect);
1072 boundsRect.moveBy(accumulatedOffset);
1073 // We are beyond the ellipsis box.
1074 if (locationInContainer.intersects(boundsRect))
1078 LayoutRect frameRect = roundedFrameRect();
1079 LayoutUnit minX = frameRect.x();
1080 LayoutUnit minY = frameRect.y();
1081 LayoutUnit width = frameRect.width();
1082 LayoutUnit height = frameRect.height();
1084 // Constrain our hit testing to the line top and bottom if necessary.
1085 bool noQuirksMode = renderer().document().inNoQuirksMode();
1086 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
1087 RootInlineBox& rootBox = root();
1088 LayoutUnit& top = isHorizontal() ? minY : minX;
1089 LayoutUnit& logicalHeight = isHorizontal() ? height : width;
1090 LayoutUnit bottom = std::min(rootBox.lineBottom(), top + logicalHeight);
1091 top = std::max(rootBox.lineTop(), top);
1092 logicalHeight = bottom - top;
1095 // Move x/y to our coordinates.
1096 LayoutRect rect(minX, minY, width, height);
1097 flipForWritingMode(rect);
1098 rect.moveBy(accumulatedOffset);
1100 if (locationInContainer.intersects(rect)) {
1101 renderer().updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset))); // Don't add in m_x or m_y here, we want coords in the containing block's space.
1102 if (!result.addNodeToRectBasedTestResult(renderer().element(), request, locationInContainer, rect))
1109 void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
1111 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom));
1112 overflowRect.inflate(renderer().maximalOutlineSize(paintInfo.phase));
1113 flipForWritingMode(overflowRect);
1114 overflowRect.moveBy(paintOffset);
1116 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
1119 if (paintInfo.phase != PaintPhaseChildOutlines) {
1120 if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
1121 // Add ourselves to the paint info struct's list of inlines that need to paint their
1123 if (renderer().style().visibility() == VISIBLE && renderer().hasOutline() && !isRootInlineBox()) {
1124 RenderInline& inlineFlow = toRenderInline(renderer());
1126 RenderBlock* cb = 0;
1127 bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation();
1128 if (containingBlockPaintsContinuationOutline) {
1129 // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
1130 // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
1131 // anonymous blocks. In this case, it is better to bail out and paint it ourself.
1132 RenderBlock* enclosingAnonymousBlock = renderer().containingBlock();
1133 if (!enclosingAnonymousBlock->isAnonymousBlock())
1134 containingBlockPaintsContinuationOutline = false;
1136 cb = enclosingAnonymousBlock->containingBlock();
1137 for (auto box = &renderer(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
1138 if (box->hasSelfPaintingLayer()) {
1139 containingBlockPaintsContinuationOutline = false;
1146 if (containingBlockPaintsContinuationOutline) {
1147 // Add ourselves to the containing block of the entire continuation so that it can
1148 // paint us atomically.
1149 cb->addContinuationWithOutline(toRenderInline(renderer().element()->renderer()));
1150 } else if (!inlineFlow.isInlineElementContinuation())
1151 paintInfo.outlineObjects->add(&inlineFlow);
1153 } else if (paintInfo.phase == PaintPhaseMask) {
1154 paintMask(paintInfo, paintOffset);
1157 // Paint our background, border and box-shadow.
1158 paintBoxDecorations(paintInfo, paintOffset);
1162 if (paintInfo.phase == PaintPhaseMask)
1165 PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;
1166 PaintInfo childInfo(paintInfo);
1167 childInfo.phase = paintPhase;
1168 childInfo.updateSubtreePaintRootForChildren(&renderer());
1170 // Paint our children.
1171 if (paintPhase != PaintPhaseSelfOutline) {
1172 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
1173 if (curr->renderer().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
1174 curr->paint(childInfo, paintOffset, lineTop, lineBottom);
1179 void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, CompositeOperator op)
1183 paintFillLayers(paintInfo, c, fillLayer->next(), rect, op);
1184 paintFillLayer(paintInfo, c, fillLayer, rect, op);
1187 bool InlineFlowBox::boxShadowCanBeAppliedToBackground(const FillLayer& lastBackgroundLayer) const
1189 // The checks here match how paintFillLayer() decides whether to clip (if it does, the shadow
1190 // would be clipped out, so it has to be drawn separately).
1191 StyleImage* image = lastBackgroundLayer.image();
1192 bool hasFillImage = image && image->canRender(&renderer(), renderer().style().effectiveZoom());
1193 return (!hasFillImage && !renderer().style().hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent();
1196 void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, CompositeOperator op)
1198 StyleImage* img = fillLayer->image();
1199 bool hasFillImage = img && img->canRender(&renderer(), renderer().style().effectiveZoom());
1200 if ((!hasFillImage && !renderer().style().hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent())
1201 renderer().paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
1202 #if ENABLE(CSS_BOX_DECORATION_BREAK)
1203 else if (renderer().style().boxDecorationBreak() == DCLONE) {
1204 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1205 paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height()));
1206 renderer().paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
1210 // We have a fill image that spans multiple lines.
1211 // We need to adjust tx and ty by the width of all previous lines.
1212 // Think of background painting on inlines as though you had one long line, a single continuous
1213 // strip. Even though that strip has been broken up across multiple lines, you still paint it
1214 // as though you had one single line. This means each line has to pick up the background where
1215 // the previous line left off.
1216 LayoutUnit logicalOffsetOnLine = 0;
1217 LayoutUnit totalLogicalWidth;
1218 if (renderer().style().direction() == LTR) {
1219 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1220 logicalOffsetOnLine += curr->logicalWidth();
1221 totalLogicalWidth = logicalOffsetOnLine;
1222 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1223 totalLogicalWidth += curr->logicalWidth();
1225 for (InlineFlowBox* curr = nextLineBox(); curr; curr = curr->nextLineBox())
1226 logicalOffsetOnLine += curr->logicalWidth();
1227 totalLogicalWidth = logicalOffsetOnLine;
1228 for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox())
1229 totalLogicalWidth += curr->logicalWidth();
1231 LayoutUnit stripX = rect.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
1232 LayoutUnit stripY = rect.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
1233 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : LayoutUnit(width());
1234 LayoutUnit stripHeight = isHorizontal() ? LayoutUnit(height()) : totalLogicalWidth;
1236 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1237 paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height()));
1238 renderer().paintFillLayerExtended(paintInfo, c, fillLayer, LayoutRect(stripX, stripY, stripWidth, stripHeight), BackgroundBleedNone, this, rect.size(), op);
1242 void InlineFlowBox::paintBoxShadow(const PaintInfo& info, const RenderStyle& style, ShadowStyle shadowStyle, const LayoutRect& paintRect)
1244 if ((!prevLineBox() && !nextLineBox()) || !parent())
1245 renderer().paintBoxShadow(info, paintRect, style, shadowStyle);
1247 // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't
1248 // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines
1249 renderer().paintBoxShadow(info, paintRect, style, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge());
1253 void InlineFlowBox::constrainToLineTopAndBottomIfNeeded(LayoutRect& rect) const
1255 bool noQuirksMode = renderer().document().inNoQuirksMode();
1256 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
1257 const RootInlineBox& rootBox = root();
1258 LayoutUnit logicalTop = isHorizontal() ? rect.y() : rect.x();
1259 LayoutUnit logicalHeight = isHorizontal() ? rect.height() : rect.width();
1260 LayoutUnit bottom = std::min(rootBox.lineBottom(), logicalTop + logicalHeight);
1261 logicalTop = std::max(rootBox.lineTop(), logicalTop);
1262 logicalHeight = bottom - logicalTop;
1263 if (isHorizontal()) {
1264 rect.setY(logicalTop);
1265 rect.setHeight(logicalHeight);
1267 rect.setX(logicalTop);
1268 rect.setWidth(logicalHeight);
1273 static LayoutRect clipRectForNinePieceImageStrip(InlineFlowBox* box, const NinePieceImage& image, const LayoutRect& paintRect)
1275 LayoutRect clipRect(paintRect);
1276 RenderStyle& style = box->renderer().style();
1277 LayoutBoxExtent outsets = style.imageOutsets(image);
1278 if (box->isHorizontal()) {
1279 clipRect.setY(paintRect.y() - outsets.top());
1280 clipRect.setHeight(paintRect.height() + outsets.top() + outsets.bottom());
1281 if (box->includeLogicalLeftEdge()) {
1282 clipRect.setX(paintRect.x() - outsets.left());
1283 clipRect.setWidth(paintRect.width() + outsets.left());
1285 if (box->includeLogicalRightEdge())
1286 clipRect.setWidth(clipRect.width() + outsets.right());
1288 clipRect.setX(paintRect.x() - outsets.left());
1289 clipRect.setWidth(paintRect.width() + outsets.left() + outsets.right());
1290 if (box->includeLogicalLeftEdge()) {
1291 clipRect.setY(paintRect.y() - outsets.top());
1292 clipRect.setHeight(paintRect.height() + outsets.top());
1294 if (box->includeLogicalRightEdge())
1295 clipRect.setHeight(clipRect.height() + outsets.bottom());
1300 void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1302 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground)
1305 // Pixel snap background/border painting.
1306 LayoutRect frameRect = roundedFrameRect();
1308 constrainToLineTopAndBottomIfNeeded(frameRect);
1310 // Move x/y to our coordinates.
1311 LayoutRect localRect(frameRect);
1312 flipForWritingMode(localRect);
1314 // You can use p::first-line to specify a background. If so, the root line boxes for
1315 // a line may actually have to paint a background.
1316 if (parent() && !renderer().hasBoxDecorations())
1318 const RenderStyle& lineStyle = this->lineStyle();
1319 if (!parent() && (!isFirstLine() || &lineStyle == &renderer().style()))
1322 LayoutPoint adjustedPaintoffset = paintOffset + localRect.location();
1323 GraphicsContext* context = paintInfo.context;
1324 LayoutRect paintRect = LayoutRect(adjustedPaintoffset, frameRect.size());
1325 // Shadow comes first and is behind the background and border.
1326 if (!renderer().boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, this))
1327 paintBoxShadow(paintInfo, lineStyle, Normal, paintRect);
1329 Color c = lineStyle.visitedDependentColor(CSSPropertyBackgroundColor);
1330 paintFillLayers(paintInfo, c, lineStyle.backgroundLayers(), paintRect);
1331 paintBoxShadow(paintInfo, lineStyle, Inset, paintRect);
1333 // :first-line cannot be used to put borders on a line. Always paint borders with our
1334 // non-first-line style.
1335 if (!parent() || !renderer().style().hasBorder())
1337 const NinePieceImage& borderImage = renderer().style().borderImage();
1338 StyleImage* borderImageSource = borderImage.image();
1339 bool hasBorderImage = borderImageSource && borderImageSource->canRender(&renderer(), lineStyle.effectiveZoom());
1340 if (hasBorderImage && !borderImageSource->isLoaded())
1341 return; // Don't paint anything while we wait for the image to load.
1343 // The simple case is where we either have no border image or we are the only box for this object. In those
1344 // cases only a single call to draw is required.
1345 if (!hasBorderImage || (!prevLineBox() && !nextLineBox()))
1346 renderer().paintBorder(paintInfo, paintRect, lineStyle, BackgroundBleedNone, includeLogicalLeftEdge(), includeLogicalRightEdge());
1348 // We have a border image that spans multiple lines.
1349 // We need to adjust tx and ty by the width of all previous lines.
1350 // Think of border image painting on inlines as though you had one long line, a single continuous
1351 // strip. Even though that strip has been broken up across multiple lines, you still paint it
1352 // as though you had one single line. This means each line has to pick up the image where
1353 // the previous line left off.
1354 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
1355 // but it isn't even clear how this should work at all.
1356 LayoutUnit logicalOffsetOnLine = 0;
1357 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1358 logicalOffsetOnLine += curr->logicalWidth();
1359 LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
1360 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1361 totalLogicalWidth += curr->logicalWidth();
1362 LayoutUnit stripX = adjustedPaintoffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
1363 LayoutUnit stripY = adjustedPaintoffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
1364 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
1365 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
1367 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, borderImage, paintRect);
1368 GraphicsContextStateSaver stateSaver(*context);
1369 context->clip(clipRect);
1370 renderer().paintBorder(paintInfo, LayoutRect(stripX, stripY, stripWidth, stripHeight), lineStyle);
1374 void InlineFlowBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1376 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
1379 // Pixel snap mask painting.
1380 LayoutRect frameRect = roundedFrameRect();
1382 constrainToLineTopAndBottomIfNeeded(frameRect);
1384 // Move x/y to our coordinates.
1385 LayoutRect localRect(frameRect);
1386 flipForWritingMode(localRect);
1387 LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();
1389 const NinePieceImage& maskNinePieceImage = renderer().style().maskBoxImage();
1390 StyleImage* maskBoxImage = renderer().style().maskBoxImage().image();
1392 // Figure out if we need to push a transparency layer to render our mask.
1393 bool pushTransparencyLayer = false;
1394 bool compositedMask = renderer().hasLayer() && renderer().layer()->hasCompositedMask();
1395 bool flattenCompositingLayers = renderer().view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers;
1396 CompositeOperator compositeOp = CompositeSourceOver;
1397 if (!compositedMask || flattenCompositingLayers) {
1398 if ((maskBoxImage && renderer().style().maskLayers()->hasImage()) || renderer().style().maskLayers()->next())
1399 pushTransparencyLayer = true;
1401 compositeOp = CompositeDestinationIn;
1402 if (pushTransparencyLayer) {
1403 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
1404 paintInfo.context->beginTransparencyLayer(1.0f);
1405 compositeOp = CompositeSourceOver;
1409 LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size());
1410 paintFillLayers(paintInfo, Color(), renderer().style().maskLayers(), paintRect, compositeOp);
1412 bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(&renderer(), renderer().style().effectiveZoom());
1413 if (!hasBoxImage || !maskBoxImage->isLoaded()) {
1414 if (pushTransparencyLayer)
1415 paintInfo.context->endTransparencyLayer();
1416 return; // Don't paint anything while we wait for the image to load.
1419 // The simple case is where we are the only box for this object. In those
1420 // cases only a single call to draw is required.
1421 if (!prevLineBox() && !nextLineBox()) {
1422 renderer().paintNinePieceImage(paintInfo.context, LayoutRect(adjustedPaintOffset, frameRect.size()), renderer().style(), maskNinePieceImage, compositeOp);
1424 // We have a mask image that spans multiple lines.
1425 // We need to adjust _tx and _ty by the width of all previous lines.
1426 LayoutUnit logicalOffsetOnLine = 0;
1427 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1428 logicalOffsetOnLine += curr->logicalWidth();
1429 LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
1430 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1431 totalLogicalWidth += curr->logicalWidth();
1432 LayoutUnit stripX = adjustedPaintOffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit());
1433 LayoutUnit stripY = adjustedPaintOffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine);
1434 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
1435 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
1437 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, maskNinePieceImage, paintRect);
1438 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1439 paintInfo.context->clip(clipRect);
1440 renderer().paintNinePieceImage(paintInfo.context, LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer().style(), maskNinePieceImage, compositeOp);
1443 if (pushTransparencyLayer)
1444 paintInfo.context->endTransparencyLayer();
1447 InlineBox* InlineFlowBox::firstLeafChild() const
1449 InlineBox* leaf = 0;
1450 for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine())
1451 leaf = child->isLeaf() ? child : toInlineFlowBox(child)->firstLeafChild();
1455 InlineBox* InlineFlowBox::lastLeafChild() const
1457 InlineBox* leaf = 0;
1458 for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine())
1459 leaf = child->isLeaf() ? child : toInlineFlowBox(child)->lastLeafChild();
1463 RenderObject::SelectionState InlineFlowBox::selectionState()
1465 return RenderObject::SelectionNone;
1468 bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const
1470 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1471 if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth))
1477 float InlineFlowBox::placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox)
1480 // We iterate over all children, the foundBox variable tells us when we've found the
1481 // box containing the ellipsis. All boxes after that one in the flow are hidden.
1482 // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate
1483 // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis.
1484 InlineBox* box = ltr ? firstChild() : lastChild();
1486 // NOTE: these will cross after foundBox = true.
1487 int visibleLeftEdge = blockLeftEdge;
1488 int visibleRightEdge = blockRightEdge;
1491 int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, truncatedWidth, foundBox);
1492 if (currResult != -1 && result == -1)
1493 result = currResult;
1496 visibleLeftEdge += box->logicalWidth();
1497 box = box->nextOnLine();
1500 visibleRightEdge -= box->logicalWidth();
1501 box = box->prevOnLine();
1507 void InlineFlowBox::clearTruncation()
1509 for (InlineBox *box = firstChild(); box; box = box->nextOnLine())
1510 box->clearTruncation();
1513 LayoutUnit InlineFlowBox::computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const
1515 LayoutUnit result = 0;
1516 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
1517 if (curr->renderer().isOutOfFlowPositioned())
1518 continue; // Positioned placeholders don't affect calculations.
1520 if (curr->isInlineFlowBox())
1521 result = std::max(result, toInlineFlowBox(curr)->computeOverAnnotationAdjustment(allowedPosition));
1523 if (curr->renderer().isReplaced() && curr->renderer().isRubyRun() && curr->renderer().style().rubyPosition() == RubyPositionBefore) {
1524 RenderRubyRun& rubyRun = toRenderRubyRun(curr->renderer());
1525 RenderRubyText* rubyText = rubyRun.rubyText();
1529 if (!rubyRun.style().isFlippedLinesWritingMode()) {
1530 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : LayoutUnit());
1531 if (topOfFirstRubyTextLine >= 0)
1533 topOfFirstRubyTextLine += curr->logicalTop();
1534 result = std::max(result, allowedPosition - topOfFirstRubyTextLine);
1536 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight());
1537 if (bottomOfLastRubyTextLine <= curr->logicalHeight())
1539 bottomOfLastRubyTextLine += curr->logicalTop();
1540 result = std::max(result, bottomOfLastRubyTextLine - allowedPosition);
1544 if (curr->isInlineTextBox()) {
1545 const RenderStyle& childLineStyle = curr->lineStyle();
1546 bool emphasisMarkIsAbove;
1547 if (childLineStyle.textEmphasisMark() != TextEmphasisMarkNone && toInlineTextBox(curr)->emphasisMarkExistsAndIsAbove(childLineStyle, emphasisMarkIsAbove) && emphasisMarkIsAbove) {
1548 if (!childLineStyle.isFlippedLinesWritingMode()) {
1549 int topOfEmphasisMark = curr->logicalTop() - childLineStyle.font().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1550 result = std::max(result, allowedPosition - topOfEmphasisMark);
1552 int bottomOfEmphasisMark = curr->logicalBottom() + childLineStyle.font().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1553 result = std::max(result, bottomOfEmphasisMark - allowedPosition);
1561 LayoutUnit InlineFlowBox::computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const
1563 LayoutUnit result = 0;
1564 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
1565 if (curr->renderer().isOutOfFlowPositioned())
1566 continue; // Positioned placeholders don't affect calculations.
1568 if (curr->isInlineFlowBox())
1569 result = std::max(result, toInlineFlowBox(curr)->computeUnderAnnotationAdjustment(allowedPosition));
1571 if (curr->renderer().isReplaced() && curr->renderer().isRubyRun() && curr->renderer().style().rubyPosition() == RubyPositionAfter) {
1572 RenderRubyRun& rubyRun = toRenderRubyRun(curr->renderer());
1573 RenderRubyText* rubyText = rubyRun.rubyText();
1577 if (rubyRun.style().isFlippedLinesWritingMode()) {
1578 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : LayoutUnit());
1579 if (topOfFirstRubyTextLine >= 0)
1581 topOfFirstRubyTextLine += curr->logicalTop();
1582 result = std::max(result, allowedPosition - topOfFirstRubyTextLine);
1584 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight());
1585 if (bottomOfLastRubyTextLine <= curr->logicalHeight())
1587 bottomOfLastRubyTextLine += curr->logicalTop();
1588 result = std::max(result, bottomOfLastRubyTextLine - allowedPosition);
1592 if (curr->isInlineTextBox()) {
1593 const RenderStyle& childLineStyle = curr->lineStyle();
1594 bool emphasisMarkIsAbove;
1595 toInlineTextBox(curr)->emphasisMarkExistsAndIsAbove(childLineStyle, emphasisMarkIsAbove);
1596 if (childLineStyle.textEmphasisMark() != TextEmphasisMarkNone && !emphasisMarkIsAbove) {
1597 if (!childLineStyle.isFlippedLinesWritingMode()) {
1598 LayoutUnit bottomOfEmphasisMark = curr->logicalBottom() + childLineStyle.font().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1599 result = std::max(result, bottomOfEmphasisMark - allowedPosition);
1601 LayoutUnit topOfEmphasisMark = curr->logicalTop() - childLineStyle.font().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1602 result = std::max(result, allowedPosition - topOfEmphasisMark);
1610 void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const
1612 InlineBox* leaf = firstLeafChild();
1614 // FIXME: The reordering code is a copy of parts from BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, instead of BidiRuns.
1615 // Investigate on how this code could possibly be shared.
1616 unsigned char minLevel = 128;
1617 unsigned char maxLevel = 0;
1619 // First find highest and lowest levels, and initialize leafBoxesInLogicalOrder with the leaf boxes in visual order.
1620 for (; leaf; leaf = leaf->nextLeafChild()) {
1621 minLevel = std::min(minLevel, leaf->bidiLevel());
1622 maxLevel = std::max(maxLevel, leaf->bidiLevel());
1623 leafBoxesInLogicalOrder.append(leaf);
1626 if (renderer().style().rtlOrdering() == VisualOrder)
1629 // Reverse of reordering of the line (L2 according to Bidi spec):
1630 // L2. From the highest level found in the text to the lowest odd level on each line,
1631 // reverse any contiguous sequence of characters that are at that level or higher.
1633 // Reversing the reordering of the line is only done up to the lowest odd level.
1634 if (!(minLevel % 2))
1637 Vector<InlineBox*>::iterator end = leafBoxesInLogicalOrder.end();
1638 while (minLevel <= maxLevel) {
1639 Vector<InlineBox*>::iterator it = leafBoxesInLogicalOrder.begin();
1642 if ((*it)->bidiLevel() >= minLevel)
1646 Vector<InlineBox*>::iterator first = it;
1648 if ((*it)->bidiLevel() < minLevel)
1652 Vector<InlineBox*>::iterator last = it;
1653 if (customReverseImplementation) {
1655 (*customReverseImplementation)(userData, first, last);
1657 std::reverse(first, last);
1665 const char* InlineFlowBox::boxName() const
1667 return "InlineFlowBox";
1670 void InlineFlowBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const
1672 InlineBox::showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth);
1673 for (const InlineBox* box = firstChild(); box; box = box->nextOnLine())
1674 box->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth + 1);
1677 void InlineFlowBox::checkConsistency() const
1680 ASSERT_WITH_SECURITY_IMPLICATION(!m_hasBadChildList);
1681 #ifdef CHECK_CONSISTENCY
1682 const InlineBox* previousChild = nullptr;
1683 for (const InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
1684 ASSERT(child->parent() == this);
1685 ASSERT(child->prevOnLine() == previousChild);
1686 previousChild = child;
1688 ASSERT(previousChild == m_lastChild);
1694 } // namespace WebCore