1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "InlineFlowBox.h"
22
23#include "CSSPropertyNames.h"
24#include "Document.h"
25#include "EllipsisBox.h"
26#include "FontCascade.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 "RenderLineBreak.h"
34#include "RenderListMarker.h"
35#include "RenderRubyBase.h"
36#include "RenderRubyRun.h"
37#include "RenderRubyText.h"
38#include "RenderTableCell.h"
39#include "RenderTheme.h"
40#include "RenderView.h"
41#include "RootInlineBox.h"
42#include "Settings.h"
43#include "Text.h"
44#include <math.h>
45#include <wtf/IsoMallocInlines.h>
46
47namespace WebCore {
48
49WTF_MAKE_ISO_ALLOCATED_IMPL(InlineFlowBox);
50
51struct SameSizeAsInlineFlowBox : public InlineBox {
52 uint32_t bitfields : 23;
53 void* pointers[5];
54};
55
56COMPILE_ASSERT(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), InlineFlowBox_should_stay_small);
57
58#if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
59
60InlineFlowBox::~InlineFlowBox()
61{
62 setHasBadChildList();
63}
64
65void InlineFlowBox::setHasBadChildList()
66{
67 assertNotDeleted();
68 if (m_hasBadChildList)
69 return;
70 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
71 child->setHasBadParent();
72 m_hasBadChildList = true;
73}
74
75#endif
76
77LayoutUnit InlineFlowBox::getFlowSpacingLogicalWidth()
78{
79 LayoutUnit totalWidth = marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight();
80 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
81 if (is<InlineFlowBox>(*child))
82 totalWidth += downcast<InlineFlowBox>(*child).getFlowSpacingLogicalWidth();
83 }
84 return totalWidth;
85}
86
87static void setHasTextDescendantsOnAncestors(InlineFlowBox* box)
88{
89 while (box && !box->hasTextDescendants()) {
90 box->setHasTextDescendants();
91 box = box->parent();
92 }
93}
94
95void InlineFlowBox::addToLine(InlineBox* child)
96{
97 ASSERT(!child->parent());
98 ASSERT(!child->nextOnLine());
99 ASSERT(!child->prevOnLine());
100 checkConsistency();
101
102 child->setParent(this);
103 if (!m_firstChild) {
104 m_firstChild = child;
105 m_lastChild = child;
106 } else {
107 m_lastChild->setNextOnLine(child);
108 child->setPrevOnLine(m_lastChild);
109 m_lastChild = child;
110 }
111 child->setIsFirstLine(isFirstLine());
112 child->setIsHorizontal(isHorizontal());
113 if (child->behavesLikeText()) {
114 if (child->renderer().parent() == &renderer())
115 m_hasTextChildren = true;
116 setHasTextDescendantsOnAncestors(this);
117 } else if (is<InlineFlowBox>(*child)) {
118 if (downcast<InlineFlowBox>(*child).hasTextDescendants())
119 setHasTextDescendantsOnAncestors(this);
120 }
121 if (descendantsHaveSameLineHeightAndBaseline() && !child->renderer().isOutOfFlowPositioned()) {
122 const RenderStyle& parentStyle = lineStyle();
123 const RenderStyle& childStyle = child->lineStyle();
124 bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false;
125 if (child->renderer().isReplaced())
126 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
127 else if (child->behavesLikeText()) {
128 if (child->renderer().isLineBreak() || child->renderer().parent() != &renderer()) {
129 if (!parentStyle.fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().fontMetrics())
130 || parentStyle.lineHeight() != childStyle.lineHeight()
131 || (parentStyle.verticalAlign() != VerticalAlign::Baseline && !isRootInlineBox()) || childStyle.verticalAlign() != VerticalAlign::Baseline)
132 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
133 }
134 if (childStyle.hasTextCombine() || childStyle.textEmphasisMark() != TextEmphasisMark::None)
135 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
136 } else {
137 if (child->renderer().isLineBreak()) {
138 // FIXME: This is dumb. We only turn off because current layout test results expect the <br> to be 0-height on the baseline.
139 // Other than making a zillion tests have to regenerate results, there's no reason to ditch the optimization here.
140 shouldClearDescendantsHaveSameLineHeightAndBaseline = child->renderer().isBR();
141 } else {
142 auto& childFlowBox = downcast<InlineFlowBox>(*child);
143 // Check the child's bit, and then also check for differences in font, line-height, vertical-align
144 if (!childFlowBox.descendantsHaveSameLineHeightAndBaseline()
145 || !parentStyle.fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().fontMetrics())
146 || parentStyle.lineHeight() != childStyle.lineHeight()
147 || (parentStyle.verticalAlign() != VerticalAlign::Baseline && !isRootInlineBox()) || childStyle.verticalAlign() != VerticalAlign::Baseline
148 || childStyle.hasBorder() || childStyle.hasPadding() || childStyle.hasTextCombine())
149 shouldClearDescendantsHaveSameLineHeightAndBaseline = true;
150 }
151 }
152
153 if (shouldClearDescendantsHaveSameLineHeightAndBaseline)
154 clearDescendantsHaveSameLineHeightAndBaseline();
155 }
156
157 if (!child->renderer().isOutOfFlowPositioned()) {
158 const RenderStyle& childStyle = child->lineStyle();
159 if (child->behavesLikeText()) {
160 const RenderStyle* childStyle = &child->lineStyle();
161 bool hasMarkers = false;
162 if (is<InlineTextBox>(child)) {
163 const auto* textBox = downcast<InlineTextBox>(child);
164 hasMarkers = textBox->hasMarkers();
165 }
166 if (childStyle->letterSpacing() < 0 || childStyle->textShadow() || childStyle->textEmphasisMark() != TextEmphasisMark::None || childStyle->hasPositiveStrokeWidth() || hasMarkers || !childStyle->textUnderlineOffset().isAuto() || !childStyle->textDecorationThickness().isAuto() || childStyle->textUnderlinePosition() != TextUnderlinePosition::Auto)
167 child->clearKnownToHaveNoOverflow();
168 } else if (child->renderer().isReplaced()) {
169 const RenderBox& box = downcast<RenderBox>(child->renderer());
170 if (box.hasRenderOverflow() || box.hasSelfPaintingLayer())
171 child->clearKnownToHaveNoOverflow();
172 } else if (!child->renderer().isLineBreak() && (childStyle.boxShadow() || child->boxModelObject()->hasSelfPaintingLayer()
173 || (is<RenderListMarker>(child->renderer()) && !downcast<RenderListMarker>(child->renderer()).isInside())
174 || childStyle.hasBorderImageOutsets()))
175 child->clearKnownToHaveNoOverflow();
176 else if (childStyle.hasOutlineInVisualOverflow())
177 child->clearKnownToHaveNoOverflow();
178
179 if (knownToHaveNoOverflow() && is<InlineFlowBox>(*child) && !downcast<InlineFlowBox>(*child).knownToHaveNoOverflow())
180 clearKnownToHaveNoOverflow();
181 }
182
183 checkConsistency();
184}
185
186void InlineFlowBox::removeChild(InlineBox* child)
187{
188 checkConsistency();
189
190 if (!isDirty())
191 dirtyLineBoxes();
192
193 root().childRemoved(child);
194
195 if (child == m_firstChild)
196 m_firstChild = child->nextOnLine();
197 if (child == m_lastChild)
198 m_lastChild = child->prevOnLine();
199 if (child->nextOnLine())
200 child->nextOnLine()->setPrevOnLine(child->prevOnLine());
201 if (child->prevOnLine())
202 child->prevOnLine()->setNextOnLine(child->nextOnLine());
203
204 child->setParent(nullptr);
205
206 checkConsistency();
207}
208
209void InlineFlowBox::deleteLine()
210{
211 InlineBox* child = firstChild();
212 InlineBox* next = nullptr;
213 while (child) {
214 ASSERT(this == child->parent());
215 next = child->nextOnLine();
216#ifndef NDEBUG
217 child->setParent(nullptr);
218#endif
219 child->deleteLine();
220 child = next;
221 }
222#ifndef NDEBUG
223 m_firstChild = nullptr;
224 m_lastChild = nullptr;
225#endif
226
227 removeLineBoxFromRenderObject();
228 delete this;
229}
230
231void InlineFlowBox::removeLineBoxFromRenderObject()
232{
233 downcast<RenderInline>(renderer()).lineBoxes().removeLineBox(this);
234}
235
236void InlineFlowBox::extractLine()
237{
238 if (!extracted())
239 extractLineBoxFromRenderObject();
240 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
241 child->extractLine();
242}
243
244void InlineFlowBox::extractLineBoxFromRenderObject()
245{
246 downcast<RenderInline>(renderer()).lineBoxes().extractLineBox(this);
247}
248
249void InlineFlowBox::attachLine()
250{
251 if (extracted())
252 attachLineBoxToRenderObject();
253 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
254 child->attachLine();
255}
256
257void InlineFlowBox::attachLineBoxToRenderObject()
258{
259 downcast<RenderInline>(renderer()).lineBoxes().attachLineBox(this);
260}
261
262void InlineFlowBox::adjustPosition(float dx, float dy)
263{
264 InlineBox::adjustPosition(dx, dy);
265 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
266 child->adjustPosition(dx, dy);
267 if (m_overflow)
268 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.
269}
270
271static inline bool isLastChildForRenderer(const RenderElement& ancestor, const RenderObject* child)
272{
273 if (!child)
274 return false;
275
276 if (child == &ancestor)
277 return true;
278
279 const RenderObject* curr = child;
280 const RenderElement* parent = curr->parent();
281 while (parent && (!parent->isRenderBlock() || parent->isInline())) {
282 if (parent->lastChild() != curr)
283 return false;
284 if (parent == &ancestor)
285 return true;
286
287 curr = parent;
288 parent = curr->parent();
289 }
290
291 return true;
292}
293
294static bool isAncestorAndWithinBlock(const RenderInline& ancestor, const RenderObject* child)
295{
296 const RenderObject* object = child;
297 while (object && (!object->isRenderBlock() || object->isInline())) {
298 if (object == &ancestor)
299 return true;
300 object = object->parent();
301 }
302 return false;
303}
304
305void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer)
306{
307 // All boxes start off open. They will not apply any margins/border/padding on
308 // any side.
309 bool includeLeftEdge = false;
310 bool includeRightEdge = false;
311
312 // The root inline box never has borders/margins/padding.
313 if (parent()) {
314 const auto& inlineFlow = downcast<RenderInline>(renderer());
315
316 bool ltr = renderer().style().isLeftToRightDirection();
317
318 // Check to see if all initial lines are unconstructed. If so, then
319 // we know the inline began on this line (unless we are a continuation).
320 const auto& lineBoxList = inlineFlow.lineBoxes();
321 if (!lineBoxList.firstLineBox()->isConstructed() && !inlineFlow.isContinuation()) {
322#if ENABLE(CSS_BOX_DECORATION_BREAK)
323 if (renderer().style().boxDecorationBreak() == BoxDecorationBreak::Clone)
324 includeLeftEdge = includeRightEdge = true;
325 else
326#endif
327 if (ltr && lineBoxList.firstLineBox() == this)
328 includeLeftEdge = true;
329 else if (!ltr && lineBoxList.lastLineBox() == this)
330 includeRightEdge = true;
331 }
332
333 if (!lineBoxList.lastLineBox()->isConstructed()) {
334 bool isLastObjectOnLine = !isAncestorAndWithinBlock(inlineFlow, logicallyLastRunRenderer) || (isLastChildForRenderer(renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped);
335
336 // We include the border under these conditions:
337 // (1) The next line was not created, or it is constructed. We check the previous line for rtl.
338 // (2) The logicallyLastRun is not a descendant of this renderer.
339 // (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.
340#if ENABLE(CSS_BOX_DECORATION_BREAK)
341 // (4) The decoration break is set to clone therefore there will be borders on every sides.
342 if (renderer().style().boxDecorationBreak() == BoxDecorationBreak::Clone)
343 includeLeftEdge = includeRightEdge = true;
344 else
345#endif
346 if (ltr) {
347 if (!nextLineBox()
348 && ((lastLine || isLastObjectOnLine) && !inlineFlow.continuation()))
349 includeRightEdge = true;
350 } else {
351 if ((!prevLineBox() || prevLineBox()->isConstructed())
352 && ((lastLine || isLastObjectOnLine) && !inlineFlow.continuation()))
353 includeLeftEdge = true;
354 }
355 }
356 }
357
358 setEdges(includeLeftEdge, includeRightEdge);
359
360 // Recur into our children.
361 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
362 if (is<InlineFlowBox>(*child))
363 downcast<InlineFlowBox>(*child).determineSpacingForFlowBoxes(lastLine, isLogicallyLastRunWrapped, logicallyLastRunRenderer);
364 }
365}
366
367float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing)
368{
369 // Set our x position.
370 beginPlacingBoxRangesInInlineDirection(logicalLeft);
371
372 float startLogicalLeft = logicalLeft;
373 logicalLeft += borderLogicalLeft() + paddingLogicalLeft();
374
375 float minLogicalLeft = startLogicalLeft;
376 float maxLogicalRight = logicalLeft;
377
378 placeBoxRangeInInlineDirection(firstChild(), nullptr, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing);
379
380 logicalLeft += borderLogicalRight() + paddingLogicalRight();
381 endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, minLogicalLeft, maxLogicalRight);
382 return logicalLeft;
383}
384
385float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing)
386{
387 float totalExpansion = 0;
388 for (InlineBox* child = firstChild; child && child != lastChild; child = child->nextOnLine()) {
389 if (is<RenderText>(child->renderer())) {
390 auto& textBox = downcast<InlineTextBox>(*child);
391 RenderText& renderText = textBox.renderer();
392 if (renderText.text().length()) {
393 if (needsWordSpacing && isSpaceOrNewline(renderText.characterAt(textBox.start())))
394 logicalLeft += textBox.lineStyle().fontCascade().wordSpacing();
395 needsWordSpacing = !isSpaceOrNewline(renderText.characterAt(textBox.end()));
396 }
397 textBox.setLogicalLeft(logicalLeft);
398 if (knownToHaveNoOverflow())
399 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
400 logicalLeft += textBox.logicalWidth();
401 totalExpansion += textBox.expansion();
402 if (knownToHaveNoOverflow())
403 maxLogicalRight = std::max(logicalLeft, maxLogicalRight);
404 } else {
405 if (child->renderer().isOutOfFlowPositioned()) {
406 if (child->renderer().parent()->style().isLeftToRightDirection())
407 child->setLogicalLeft(logicalLeft);
408 else
409 // Our offset that we cache needs to be from the edge of the right border box and
410 // not the left border box. We have to subtract |x| from the width of the block
411 // (which can be obtained from the root line box).
412 child->setLogicalLeft(root().blockFlow().logicalWidth() - logicalLeft);
413 continue; // The positioned object has no effect on the width.
414 }
415 if (is<RenderInline>(child->renderer())) {
416 auto& flow = downcast<InlineFlowBox>(*child);
417 logicalLeft += flow.marginLogicalLeft();
418 if (knownToHaveNoOverflow())
419 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
420 logicalLeft = flow.placeBoxesInInlineDirection(logicalLeft, needsWordSpacing);
421 totalExpansion += flow.expansion();
422 if (knownToHaveNoOverflow())
423 maxLogicalRight = std::max(logicalLeft, maxLogicalRight);
424 logicalLeft += flow.marginLogicalRight();
425 } else if (!is<RenderListMarker>(child->renderer()) || downcast<RenderListMarker>(child->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() ? child->boxModelObject()->marginLeft() : child->boxModelObject()->marginTop();
429 LayoutUnit logicalRightMargin = isHorizontal() ? child->boxModelObject()->marginRight() : child->boxModelObject()->marginBottom();
430
431 logicalLeft += logicalLeftMargin;
432 child->setLogicalLeft(logicalLeft);
433 if (knownToHaveNoOverflow())
434 minLogicalLeft = std::min(logicalLeft, minLogicalLeft);
435 logicalLeft += child->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;
441 }
442 }
443 }
444 setExpansionWithoutGrowing(totalExpansion);
445 return logicalLeft;
446}
447
448bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap& textBoxDataMap) const
449{
450 if (isHorizontal())
451 return false;
452
453 const RenderStyle& lineStyle = this->lineStyle();
454 if (lineStyle.fontDescription().nonCJKGlyphOrientation() == NonCJKGlyphOrientation::Upright
455 || lineStyle.fontCascade().primaryFont().hasVerticalGlyphs())
456 return true;
457
458 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
459 if (child->renderer().isOutOfFlowPositioned())
460 continue; // Positioned placeholders don't affect calculations.
461
462 if (is<InlineFlowBox>(*child)) {
463 if (downcast<InlineFlowBox>(*child).requiresIdeographicBaseline(textBoxDataMap))
464 return true;
465 } else {
466 if (child->lineStyle().fontCascade().primaryFont().hasVerticalGlyphs())
467 return true;
468
469 const Vector<const Font*>* usedFonts = nullptr;
470 if (is<InlineTextBox>(*child)) {
471 GlyphOverflowAndFallbackFontsMap::const_iterator it = textBoxDataMap.find(downcast<InlineTextBox>(child));
472 usedFonts = it == textBoxDataMap.end() ? nullptr : &it->value.first;
473 }
474
475 if (usedFonts) {
476 for (const Font* font : *usedFonts) {
477 if (font->hasVerticalGlyphs())
478 return true;
479 }
480 }
481 }
482 }
483
484 return false;
485}
486
487static bool verticalAlignApplies(const RenderObject& renderer)
488{
489 // http://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align - vertical-align
490 // only applies to inline level and table-cell elements
491 return !renderer.isText() || renderer.parent()->isInline() || renderer.parent()->isTableCell();
492}
493
494void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom)
495{
496 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
497 // The computed lineheight needs to be extended for the
498 // positioned elements
499 if (child->renderer().isOutOfFlowPositioned())
500 continue; // Positioned placeholders don't affect calculations.
501
502 if ((child->verticalAlign() == VerticalAlign::Top || child->verticalAlign() == VerticalAlign::Bottom) && verticalAlignApplies(child->renderer())) {
503 int lineHeight = child->lineHeight();
504 if (child->verticalAlign() == VerticalAlign::Top) {
505 if (maxAscent + maxDescent < lineHeight)
506 maxDescent = lineHeight - maxAscent;
507 }
508 else {
509 if (maxAscent + maxDescent < lineHeight)
510 maxAscent = lineHeight - maxDescent;
511 }
512
513 if (maxAscent + maxDescent >= std::max(maxPositionTop, maxPositionBottom))
514 break;
515 }
516
517 if (is<InlineFlowBox>(*child))
518 downcast<InlineFlowBox>(*child).adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
519 }
520}
521
522void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox& rootBox, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom,
523 int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent,
524 bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
525 FontBaseline baselineType, VerticalPositionCache& verticalPositionCache)
526{
527 // The primary purpose of this function is to compute the maximal ascent and descent values for
528 // a line. These values are computed based off the block's line-box-contain property, which indicates
529 // what parts of descendant boxes have to fit within the line.
530 //
531 // The maxAscent value represents the distance of the highest point of any box (typically including line-height) from
532 // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box
533 // (also typically including line-height) from the root box baseline. These values can be negative.
534 //
535 // A secondary purpose of this function is to store the offset of every box's baseline from the root box's
536 // baseline. This information is cached in the logicalTop() of every box. We're effectively just using
537 // the logicalTop() as scratch space.
538 //
539 // Because a box can be positioned such that it ends up fully above or fully below the
540 // root line box, we only consider it to affect the maxAscent and maxDescent values if some
541 // part of the box (EXCLUDING leading) is above (for ascent) or below (for descent) the root box's baseline.
542 bool affectsAscent = false;
543 bool affectsDescent = false;
544 bool checkChildren = !descendantsHaveSameLineHeightAndBaseline();
545
546 if (isRootInlineBox()) {
547 // Examine our root box.
548 int ascent = 0;
549 int descent = 0;
550 rootBox.ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent);
551 if (strictMode || hasTextChildren() || (!checkChildren && hasTextDescendants())) {
552 if (maxAscent < ascent || !setMaxAscent) {
553 maxAscent = ascent;
554 setMaxAscent = true;
555 }
556 if (maxDescent < descent || !setMaxDescent) {
557 maxDescent = descent;
558 setMaxDescent = true;
559 }
560 }
561 }
562
563 if (!checkChildren)
564 return;
565
566 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
567 if (child->renderer().isOutOfFlowPositioned())
568 continue; // Positioned placeholders don't affect calculations.
569
570 InlineFlowBox* inlineFlowBox = is<InlineFlowBox>(*child) ? downcast<InlineFlowBox>(child) : nullptr;
571
572 bool affectsAscent = false;
573 bool affectsDescent = false;
574
575 // The verticalPositionForBox function returns the distance between the child box's baseline
576 // and the root box's baseline. The value is negative if the child box's baseline is above the
577 // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline.
578 child->setLogicalTop(rootBox.verticalPositionForBox(child, verticalPositionCache));
579
580 int ascent = 0;
581 int descent = 0;
582 rootBox.ascentAndDescentForBox(*child, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent);
583
584 LayoutUnit boxHeight = ascent + descent;
585 if (child->verticalAlign() == VerticalAlign::Top && verticalAlignApplies(child->renderer())) {
586 if (maxPositionTop < boxHeight)
587 maxPositionTop = boxHeight;
588 } else if (child->verticalAlign() == VerticalAlign::Bottom && verticalAlignApplies(child->renderer())) {
589 if (maxPositionBottom < boxHeight)
590 maxPositionBottom = boxHeight;
591 } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants())
592 || inlineFlowBox->renderer().hasInlineDirectionBordersOrPadding()) {
593 // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values
594 // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height
595 // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it
596 // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and
597 // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative
598 // values.
599 ascent -= child->logicalTop();
600 descent += child->logicalTop();
601 if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) {
602 maxAscent = ascent;
603 setMaxAscent = true;
604 }
605
606 if (affectsDescent && (maxDescent < descent || !setMaxDescent)) {
607 maxDescent = descent;
608 setMaxDescent = true;
609 }
610 }
611
612 if (inlineFlowBox)
613 inlineFlowBox->computeLogicalBoxHeights(rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent,
614 setMaxAscent, setMaxDescent, strictMode, textBoxDataMap,
615 baselineType, verticalPositionCache);
616 }
617}
618
619void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, bool& setLineTop,
620 LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType)
621{
622 bool isRootBox = isRootInlineBox();
623 if (isRootBox) {
624 const FontMetrics& fontMetrics = lineStyle().fontMetrics();
625 // RootInlineBoxes are always placed on at pixel boundaries in their logical y direction. Not doing
626 // so results in incorrect rendering of text decorations, most notably underlines.
627 setLogicalTop(roundToInt(top + maxAscent - fontMetrics.ascent(baselineType)));
628 }
629
630 LayoutUnit adjustmentForChildrenWithSameLineHeightAndBaseline;
631 if (descendantsHaveSameLineHeightAndBaseline()) {
632 adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop();
633 if (parent())
634 adjustmentForChildrenWithSameLineHeightAndBaseline += renderer().borderAndPaddingBefore();
635 }
636
637 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
638 if (child->renderer().isOutOfFlowPositioned())
639 continue; // Positioned placeholders don't affect calculations.
640
641 if (descendantsHaveSameLineHeightAndBaseline()) {
642 child->adjustBlockDirectionPosition(adjustmentForChildrenWithSameLineHeightAndBaseline);
643 continue;
644 }
645
646 InlineFlowBox* inlineFlowBox = is<InlineFlowBox>(*child) ? downcast<InlineFlowBox>(child) : nullptr;
647 bool childAffectsTopBottomPos = true;
648
649 if (child->verticalAlign() == VerticalAlign::Top && verticalAlignApplies(child->renderer()))
650 child->setLogicalTop(top);
651 else if (child->verticalAlign() == VerticalAlign::Bottom && verticalAlignApplies(child->renderer()))
652 child->setLogicalTop(top + maxHeight - child->lineHeight());
653 else {
654 if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !inlineFlowBox->renderer().hasInlineDirectionBordersOrPadding()
655 && !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()))
656 childAffectsTopBottomPos = false;
657 LayoutUnit posAdjust = maxAscent - child->baselinePosition(baselineType);
658 child->setLogicalTop(child->logicalTop() + top + posAdjust);
659 }
660
661 LayoutUnit newLogicalTop = child->logicalTop();
662 LayoutUnit newLogicalTopIncludingMargins = newLogicalTop;
663 LayoutUnit boxHeight = child->logicalHeight();
664 LayoutUnit boxHeightIncludingMargins = boxHeight;
665
666 const RenderStyle& childLineStyle = child->lineStyle();
667 if (child->behavesLikeText() || is<InlineFlowBox>(*child)) {
668 const FontMetrics& fontMetrics = childLineStyle.fontMetrics();
669 newLogicalTop += child->baselinePosition(baselineType) - fontMetrics.ascent(baselineType);
670 if (is<InlineFlowBox>(*child)) {
671 RenderBoxModelObject& boxObject = downcast<InlineFlowBox>(*child).renderer();
672 newLogicalTop -= childLineStyle.isHorizontalWritingMode()
673 ? boxObject.borderTop() + boxObject.paddingTop()
674 : boxObject.borderRight() + boxObject.paddingRight();
675 }
676 newLogicalTopIncludingMargins = newLogicalTop;
677 } else if (!child->renderer().isBR()) {
678 const auto& box = downcast<RenderBox>(child->renderer());
679 newLogicalTopIncludingMargins = newLogicalTop;
680 // We may flip lines in case of verticalLR mode, so we can assume verticalRL for now.
681 LayoutUnit overSideMargin = child->isHorizontal() ? box.marginTop() : box.marginRight();
682 LayoutUnit underSideMargin = child->isHorizontal() ? box.marginBottom() : box.marginLeft();
683 newLogicalTop += overSideMargin;
684 boxHeightIncludingMargins += overSideMargin + underSideMargin;
685 }
686
687 child->setLogicalTop(newLogicalTop);
688
689 if (childAffectsTopBottomPos) {
690 if (is<RenderRubyRun>(child->renderer())) {
691 // Treat the leading on the first and last lines of ruby runs as not being part of the overall lineTop/lineBottom.
692 // Really this is a workaround hack for the fact that ruby should have been done as line layout and not done using
693 // inline-block.
694 if (renderer().style().isFlippedLinesWritingMode() == (child->renderer().style().rubyPosition() == RubyPosition::After))
695 hasAnnotationsBefore = true;
696 else
697 hasAnnotationsAfter = true;
698
699 auto& rubyRun = downcast<RenderRubyRun>(child->renderer());
700 if (RenderRubyBase* rubyBase = rubyRun.rubyBase()) {
701 LayoutUnit bottomRubyBaseLeading = (child->logicalHeight() - rubyBase->logicalBottom()) + rubyBase->logicalHeight() - (rubyBase->lastRootBox() ? rubyBase->lastRootBox()->lineBottom() : 0_lu);
702 LayoutUnit topRubyBaseLeading = rubyBase->logicalTop() + (rubyBase->firstRootBox() ? rubyBase->firstRootBox()->lineTop() : 0_lu);
703 newLogicalTop += !renderer().style().isFlippedLinesWritingMode() ? topRubyBaseLeading : bottomRubyBaseLeading;
704 boxHeight -= (topRubyBaseLeading + bottomRubyBaseLeading);
705 }
706 }
707 if (is<InlineTextBox>(*child)) {
708 if (Optional<bool> markExistsAndIsAbove = downcast<InlineTextBox>(*child).emphasisMarkExistsAndIsAbove(childLineStyle)) {
709 if (*markExistsAndIsAbove != childLineStyle.isFlippedLinesWritingMode())
710 hasAnnotationsBefore = true;
711 else
712 hasAnnotationsAfter = true;
713 }
714 }
715
716 if (!setLineTop) {
717 setLineTop = true;
718 lineTop = newLogicalTop;
719 lineTopIncludingMargins = std::min(lineTop, newLogicalTopIncludingMargins);
720 } else {
721 lineTop = std::min(lineTop, newLogicalTop);
722 lineTopIncludingMargins = std::min(lineTop, std::min(lineTopIncludingMargins, newLogicalTopIncludingMargins));
723 }
724 lineBottom = std::max(lineBottom, newLogicalTop + boxHeight);
725 lineBottomIncludingMargins = std::max(lineBottom, std::max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins));
726 }
727
728 // Adjust boxes to use their real box y/height and not the logical height (as dictated by
729 // line-height).
730 if (inlineFlowBox)
731 inlineFlowBox->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop,
732 lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType);
733 }
734
735 if (isRootBox) {
736 if (strictMode || hasTextChildren() || (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
737 if (!setLineTop) {
738 setLineTop = true;
739 lineTop = logicalTop();
740 lineTopIncludingMargins = lineTop;
741 } else {
742 lineTop = std::min<LayoutUnit>(lineTop, logicalTop());
743 lineTopIncludingMargins = std::min(lineTop, lineTopIncludingMargins);
744 }
745 lineBottom = std::max<LayoutUnit>(lineBottom, logicalBottom());
746 lineBottomIncludingMargins = std::max(lineBottom, lineBottomIncludingMargins);
747 }
748
749 if (renderer().style().isFlippedLinesWritingMode())
750 flipLinesInBlockDirection(lineTopIncludingMargins, lineBottomIncludingMargins);
751 }
752}
753
754void InlineFlowBox::maxLogicalBottomForTextDecorationLine(float& maxLogicalBottom, const RenderElement* decorationRenderer, OptionSet<TextDecoration> textDecoration) const
755{
756 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
757 if (child->renderer().isOutOfFlowPositioned())
758 continue; // Positioned placeholders don't affect calculations.
759
760 if (!(child->lineStyle().textDecorationsInEffect() & textDecoration))
761 continue; // If the text decoration isn't in effect on the child, then it must be outside of |decorationRenderer|'s hierarchy.
762
763 if (decorationRenderer && decorationRenderer->isRenderInline() && !isAncestorAndWithinBlock(downcast<RenderInline>(*decorationRenderer), &child->renderer()))
764 continue;
765
766 if (is<InlineFlowBox>(*child))
767 downcast<InlineFlowBox>(*child).maxLogicalBottomForTextDecorationLine(maxLogicalBottom, decorationRenderer, textDecoration);
768 else {
769 if (child->isInlineTextBox() || child->lineStyle().textDecorationSkip().isEmpty())
770 maxLogicalBottom = std::max<float>(maxLogicalBottom, child->logicalBottom());
771 }
772 }
773}
774
775void InlineFlowBox::minLogicalTopForTextDecorationLine(float& minLogicalTop, const RenderElement* decorationRenderer, OptionSet<TextDecoration> textDecoration) const
776{
777 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
778 if (child->renderer().isOutOfFlowPositioned())
779 continue; // Positioned placeholders don't affect calculations.
780
781 if (!(child->lineStyle().textDecorationsInEffect() & textDecoration))
782 continue; // If the text decoration isn't in effect on the child, then it must be outside of |decorationRenderer|'s hierarchy.
783
784 if (decorationRenderer && decorationRenderer->isRenderInline() && !isAncestorAndWithinBlock(downcast<RenderInline>(*decorationRenderer), &child->renderer()))
785 continue;
786
787 if (is<InlineFlowBox>(*child))
788 downcast<InlineFlowBox>(*child).minLogicalTopForTextDecorationLine(minLogicalTop, decorationRenderer, textDecoration);
789 else {
790 if (child->isInlineTextBox() || child->lineStyle().textDecorationSkip().isEmpty())
791 minLogicalTop = std::min<float>(minLogicalTop, child->logicalTop());
792 }
793 }
794}
795
796void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom)
797{
798 // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop.
799 setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight());
800
801 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
802 if (child->renderer().isOutOfFlowPositioned())
803 continue; // Positioned placeholders aren't affected here.
804
805 if (is<InlineFlowBox>(*child))
806 downcast<InlineFlowBox>(*child).flipLinesInBlockDirection(lineTop, lineBottom);
807 else
808 child->setLogicalTop(lineBottom - (child->logicalTop() - lineTop) - child->logicalHeight());
809 }
810}
811
812inline void InlineFlowBox::addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow)
813{
814 // box-shadow on root line boxes is applying to the block and not to the lines.
815 if (!parent())
816 return;
817
818 const RenderStyle& lineStyle = this->lineStyle();
819 if (!lineStyle.boxShadow())
820 return;
821
822 LayoutUnit boxShadowLogicalTop;
823 LayoutUnit boxShadowLogicalBottom;
824 lineStyle.getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, boxShadowLogicalBottom);
825
826 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite shadow that applies, since
827 // the line is "upside down" in terms of block coordinates.
828 LayoutUnit shadowLogicalTop = lineStyle.isFlippedLinesWritingMode() ? -boxShadowLogicalBottom : boxShadowLogicalTop;
829 LayoutUnit shadowLogicalBottom = lineStyle.isFlippedLinesWritingMode() ? -boxShadowLogicalTop : boxShadowLogicalBottom;
830
831 LayoutUnit logicalTopVisualOverflow = std::min<LayoutUnit>(logicalTop() + shadowLogicalTop, logicalVisualOverflow.y());
832 LayoutUnit logicalBottomVisualOverflow = std::max<LayoutUnit>(logicalBottom() + shadowLogicalBottom, logicalVisualOverflow.maxY());
833
834 LayoutUnit boxShadowLogicalLeft;
835 LayoutUnit boxShadowLogicalRight;
836 lineStyle.getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight);
837
838 LayoutUnit logicalLeftVisualOverflow = std::min<LayoutUnit>(logicalLeft() + boxShadowLogicalLeft, logicalVisualOverflow.x());
839 LayoutUnit logicalRightVisualOverflow = std::max<LayoutUnit>(logicalRight() + boxShadowLogicalRight, logicalVisualOverflow.maxX());
840
841 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
842 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
843}
844
845inline void InlineFlowBox::addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow)
846{
847 // border-image-outset on root line boxes is applying to the block and not to the lines.
848 if (!parent())
849 return;
850
851 const RenderStyle& lineStyle = this->lineStyle();
852 if (!lineStyle.hasBorderImageOutsets())
853 return;
854
855 LayoutBoxExtent borderOutsets = lineStyle.borderImageOutsets();
856
857 LayoutUnit borderOutsetLogicalTop = borderOutsets.before(lineStyle.writingMode());
858 LayoutUnit borderOutsetLogicalBottom = borderOutsets.after(lineStyle.writingMode());
859 LayoutUnit borderOutsetLogicalLeft = borderOutsets.start(lineStyle.writingMode());
860 LayoutUnit borderOutsetLogicalRight = borderOutsets.end(lineStyle.writingMode());
861
862 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite border that applies, since
863 // the line is "upside down" in terms of block coordinates. vertical-rl and horizontal-bt are the flipped line modes.
864 LayoutUnit outsetLogicalTop = lineStyle.isFlippedLinesWritingMode() ? borderOutsetLogicalBottom : borderOutsetLogicalTop;
865 LayoutUnit outsetLogicalBottom = lineStyle.isFlippedLinesWritingMode() ? borderOutsetLogicalTop : borderOutsetLogicalBottom;
866
867 LayoutUnit logicalTopVisualOverflow = std::min<LayoutUnit>(logicalTop() - outsetLogicalTop, logicalVisualOverflow.y());
868 LayoutUnit logicalBottomVisualOverflow = std::max<LayoutUnit>(logicalBottom() + outsetLogicalBottom, logicalVisualOverflow.maxY());
869
870 LayoutUnit outsetLogicalLeft = includeLogicalLeftEdge() ? borderOutsetLogicalLeft : 0_lu;
871 LayoutUnit outsetLogicalRight = includeLogicalRightEdge() ? borderOutsetLogicalRight : 0_lu;
872
873 LayoutUnit logicalLeftVisualOverflow = std::min<LayoutUnit>(logicalLeft() - outsetLogicalLeft, logicalVisualOverflow.x());
874 LayoutUnit logicalRightVisualOverflow = std::max<LayoutUnit>(logicalRight() + outsetLogicalRight, logicalVisualOverflow.maxX());
875
876 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
877 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
878}
879
880inline void InlineFlowBox::addTextBoxVisualOverflow(InlineTextBox& textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, LayoutRect& logicalVisualOverflow)
881{
882 if (textBox.knownToHaveNoOverflow())
883 return;
884
885 const RenderStyle& lineStyle = this->lineStyle();
886
887 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(&textBox);
888 GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? nullptr : &it->value.second;
889 bool isFlippedLine = lineStyle.isFlippedLinesWritingMode();
890
891 int topGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->bottom : glyphOverflow->top) : 0;
892 int bottomGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->top : glyphOverflow->bottom) : 0;
893 int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0;
894 int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0;
895
896 auto viewportSize = textBox.renderer().frame().view() ? textBox.renderer().frame().view()->size() : IntSize();
897 int strokeOverflow = std::ceil(lineStyle.computedStrokeWidth(viewportSize) / 2.0f);
898 int topGlyphOverflow = -strokeOverflow - topGlyphEdge;
899 int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge;
900 int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge;
901 int rightGlyphOverflow = strokeOverflow + rightGlyphEdge;
902
903 if (Optional<bool> markExistsAndIsAbove = textBox.emphasisMarkExistsAndIsAbove(lineStyle)) {
904 int emphasisMarkHeight = lineStyle.fontCascade().emphasisMarkHeight(lineStyle.textEmphasisMarkString());
905 if (*markExistsAndIsAbove == !lineStyle.isFlippedLinesWritingMode())
906 topGlyphOverflow = std::min(topGlyphOverflow, -emphasisMarkHeight);
907 else
908 bottomGlyphOverflow = std::max(bottomGlyphOverflow, emphasisMarkHeight);
909 }
910
911 // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is
912 // applied to the right, so this is not an issue with left overflow.
913 rightGlyphOverflow -= std::min(0, (int)lineStyle.fontCascade().letterSpacing());
914
915 LayoutUnit textShadowLogicalTop;
916 LayoutUnit textShadowLogicalBottom;
917 lineStyle.getTextShadowBlockDirectionExtent(textShadowLogicalTop, textShadowLogicalBottom);
918
919 LayoutUnit childOverflowLogicalTop = std::min<LayoutUnit>(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow);
920 LayoutUnit childOverflowLogicalBottom = std::max<LayoutUnit>(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow);
921
922 LayoutUnit textShadowLogicalLeft;
923 LayoutUnit textShadowLogicalRight;
924 lineStyle.getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight);
925
926 LayoutUnit childOverflowLogicalLeft = std::min<LayoutUnit>(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow);
927 LayoutUnit childOverflowLogicalRight = std::max<LayoutUnit>(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow);
928
929 LayoutUnit logicalTopVisualOverflow = std::min<LayoutUnit>(textBox.logicalTop() + childOverflowLogicalTop, logicalVisualOverflow.y());
930 LayoutUnit logicalBottomVisualOverflow = std::max<LayoutUnit>(textBox.logicalBottom() + childOverflowLogicalBottom, logicalVisualOverflow.maxY());
931 LayoutUnit logicalLeftVisualOverflow = std::min<LayoutUnit>(textBox.logicalLeft() + childOverflowLogicalLeft, logicalVisualOverflow.x());
932 LayoutUnit logicalRightVisualOverflow = std::max<LayoutUnit>(textBox.logicalRight() + childOverflowLogicalRight, logicalVisualOverflow.maxX());
933
934 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
935 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
936
937 auto documentMarkerBounds = textBox.calculateUnionOfAllDocumentMarkerBounds();
938 documentMarkerBounds.move(textBox.logicalLeft(), textBox.logicalTop());
939 logicalVisualOverflow = unionRect(logicalVisualOverflow, LayoutRect(documentMarkerBounds));
940
941 textBox.setLogicalOverflowRect(logicalVisualOverflow);
942}
943
944inline void InlineFlowBox::addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow)
945{
946 const auto& lineStyle = this->lineStyle();
947 if (!lineStyle.hasOutlineInVisualOverflow())
948 return;
949 LayoutUnit outlineSize = lineStyle.outlineSize();
950 LayoutUnit logicalTopVisualOverflow = std::min(LayoutUnit(logicalTop() - outlineSize), logicalVisualOverflow.y());
951 LayoutUnit logicalBottomVisualOverflow = std::max(LayoutUnit(logicalBottom() + outlineSize), logicalVisualOverflow.maxY());
952 LayoutUnit logicalLeftVisualOverflow = std::min(LayoutUnit(logicalLeft() - outlineSize), logicalVisualOverflow.x());
953 LayoutUnit logicalRightVisualOverflow = std::max(LayoutUnit(logicalRight() + outlineSize), logicalVisualOverflow.maxX());
954 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow,
955 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
956}
957
958inline void InlineFlowBox::addReplacedChildOverflow(const InlineBox* inlineBox, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow)
959{
960 const RenderBox& box = downcast<RenderBox>(inlineBox->renderer());
961
962 // Visual overflow only propagates if the box doesn't have a self-painting layer. This rectangle does not include
963 // transforms or relative positioning (since those objects always have self-painting layers), but it does need to be adjusted
964 // for writing-mode differences.
965 if (!box.hasSelfPaintingLayer()) {
966 LayoutRect childLogicalVisualOverflow = box.logicalVisualOverflowRectForPropagation(&renderer().style());
967 childLogicalVisualOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop());
968 logicalVisualOverflow.unite(childLogicalVisualOverflow);
969 }
970
971 // Layout overflow internal to the child box only propagates if the child box doesn't have overflow clip set.
972 // Otherwise the child border box propagates as layout overflow. This rectangle must include transforms and relative positioning
973 // and be adjusted for writing-mode differences.
974 LayoutRect childLogicalLayoutOverflow = box.logicalLayoutOverflowRectForPropagation(&renderer().style());
975 childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop());
976 logicalLayoutOverflow.unite(childLogicalLayoutOverflow);
977}
978
979void InlineFlowBox::computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap& textBoxDataMap)
980{
981 // If we know we have no overflow, we can just bail.
982 if (knownToHaveNoOverflow())
983 return;
984
985 if (m_overflow)
986 m_overflow = nullptr;
987
988 // Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored.
989 // Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in
990 // transforms, relative positioning, etc.
991 LayoutRect logicalLayoutOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom)));
992 LayoutRect logicalVisualOverflow(logicalLayoutOverflow);
993
994 addBoxShadowVisualOverflow(logicalVisualOverflow);
995 addOutlineVisualOverflow(logicalVisualOverflow);
996 addBorderOutsetVisualOverflow(logicalVisualOverflow);
997
998 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
999 if (child->renderer().isOutOfFlowPositioned())
1000 continue; // Positioned placeholders don't affect calculations.
1001
1002 if (is<RenderLineBreak>(child->renderer()))
1003 continue;
1004 if (is<RenderText>(child->renderer())) {
1005 auto& textBox = downcast<InlineTextBox>(*child);
1006 LayoutRect textBoxOverflow(enclosingLayoutRect(textBox.logicalFrameRect()));
1007 addTextBoxVisualOverflow(textBox, textBoxDataMap, textBoxOverflow);
1008 logicalVisualOverflow.unite(textBoxOverflow);
1009 } else if (is<RenderInline>(child->renderer())) {
1010 auto& flow = downcast<InlineFlowBox>(*child);
1011 flow.computeOverflow(lineTop, lineBottom, textBoxDataMap);
1012 if (!flow.renderer().hasSelfPaintingLayer())
1013 logicalVisualOverflow.unite(flow.logicalVisualOverflowRect(lineTop, lineBottom));
1014 LayoutRect childLayoutOverflow = flow.logicalLayoutOverflowRect(lineTop, lineBottom);
1015 childLayoutOverflow.move(flow.renderer().relativePositionLogicalOffset());
1016 logicalLayoutOverflow.unite(childLayoutOverflow);
1017 } else
1018 addReplacedChildOverflow(child, logicalLayoutOverflow, logicalVisualOverflow);
1019 }
1020
1021 setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, lineTop, lineBottom);
1022}
1023
1024void InlineFlowBox::setLayoutOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom)
1025{
1026 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom));
1027 if (frameBox.contains(rect) || rect.isEmpty())
1028 return;
1029
1030 if (!m_overflow)
1031 m_overflow = adoptRef(new RenderOverflow(frameBox, frameBox));
1032
1033 m_overflow->setLayoutOverflow(rect);
1034}
1035
1036void InlineFlowBox::setVisualOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom)
1037{
1038 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom));
1039 if (frameBox.contains(rect) || rect.isEmpty())
1040 return;
1041
1042 if (!m_overflow)
1043 m_overflow = adoptRef(new RenderOverflow(frameBox, frameBox));
1044
1045 m_overflow->setVisualOverflow(rect);
1046}
1047
1048void InlineFlowBox::setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom)
1049{
1050 LayoutRect layoutOverflow(isHorizontal() ? logicalLayoutOverflow : logicalLayoutOverflow.transposedRect());
1051 setLayoutOverflow(layoutOverflow, lineTop, lineBottom);
1052
1053 LayoutRect visualOverflow(isHorizontal() ? logicalVisualOverflow : logicalVisualOverflow.transposedRect());
1054 setVisualOverflow(visualOverflow, lineTop, lineBottom);
1055}
1056
1057bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction hitTestAction)
1058{
1059 if (hitTestAction != HitTestForeground)
1060 return false;
1061
1062 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom));
1063 flipForWritingMode(overflowRect);
1064 overflowRect.moveBy(accumulatedOffset);
1065 if (!locationInContainer.intersects(overflowRect))
1066 return false;
1067
1068 // Check children first.
1069 // 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.
1070 RenderElement* culledParent = nullptr;
1071 for (InlineBox* child = lastChild(); child; child = child->prevOnLine()) {
1072 if (is<RenderText>(child->renderer()) || !child->boxModelObject()->hasSelfPaintingLayer()) {
1073 RenderElement* newParent = nullptr;
1074 // Culled parents are only relevant for area-based hit-tests, so ignore it in point-based ones.
1075 if (locationInContainer.isRectBasedTest()) {
1076 newParent = child->renderer().parent();
1077 if (newParent == &renderer())
1078 newParent = nullptr;
1079 }
1080 // Check the culled parent after all its children have been checked, to do this we wait until
1081 // we are about to test an element with a different parent.
1082 if (newParent != culledParent) {
1083 if (!newParent || !newParent->isDescendantOf(culledParent)) {
1084 while (culledParent && culledParent != &renderer() && culledParent != newParent) {
1085 if (is<RenderInline>(*culledParent) && downcast<RenderInline>(*culledParent).hitTestCulledInline(request, result, locationInContainer, accumulatedOffset))
1086 return true;
1087 culledParent = culledParent->parent();
1088 }
1089 }
1090 culledParent = newParent;
1091 }
1092 if (child->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom, hitTestAction)) {
1093 renderer().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset));
1094 return true;
1095 }
1096 }
1097 }
1098 // Check any culled ancestor of the final children tested.
1099 while (culledParent && culledParent != &renderer()) {
1100 if (is<RenderInline>(*culledParent) && downcast<RenderInline>(*culledParent).hitTestCulledInline(request, result, locationInContainer, accumulatedOffset))
1101 return true;
1102 culledParent = culledParent->parent();
1103 }
1104
1105 // Now check ourselves. Pixel snap hit testing.
1106 if (!visibleToHitTesting())
1107 return false;
1108
1109 // Do not hittest content beyond the ellipsis box.
1110 if (isRootInlineBox() && hasEllipsisBox()) {
1111 const EllipsisBox* ellipsisBox = root().ellipsisBox();
1112 FloatRect boundsRect(frameRect());
1113
1114 if (isHorizontal())
1115 renderer().style().isLeftToRightDirection() ? boundsRect.shiftXEdgeTo(ellipsisBox->right()) : boundsRect.setWidth(ellipsisBox->left() - left());
1116 else
1117 boundsRect.shiftYEdgeTo(ellipsisBox->right());
1118
1119 flipForWritingMode(boundsRect);
1120 boundsRect.moveBy(accumulatedOffset);
1121 // We are beyond the ellipsis box.
1122 if (locationInContainer.intersects(boundsRect))
1123 return false;
1124 }
1125
1126 // Constrain our hit testing to the line top and bottom if necessary.
1127 bool noQuirksMode = renderer().document().inNoQuirksMode();
1128 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
1129 RootInlineBox& rootBox = root();
1130 LayoutUnit top = isHorizontal() ? y() : x();
1131 LayoutUnit logicalHeight = isHorizontal() ? height() : width();
1132 LayoutUnit bottom = std::min(rootBox.lineBottom(), top + logicalHeight);
1133 top = std::max(rootBox.lineTop(), top);
1134 logicalHeight = bottom - top;
1135 }
1136
1137 // Move x/y to our coordinates.
1138 FloatRect rect(frameRect());
1139 flipForWritingMode(rect);
1140 rect.moveBy(accumulatedOffset);
1141
1142 if (locationInContainer.intersects(rect)) {
1143 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.
1144 if (result.addNodeToListBasedTestResult(renderer().element(), request, locationInContainer, rect) == HitTestProgress::Stop)
1145 return true;
1146 }
1147
1148 return false;
1149}
1150
1151void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
1152{
1153 if (paintInfo.phase != PaintPhase::Foreground && paintInfo.phase != PaintPhase::Selection && paintInfo.phase != PaintPhase::Outline && paintInfo.phase != PaintPhase::SelfOutline && paintInfo.phase != PaintPhase::ChildOutlines && paintInfo.phase != PaintPhase::TextClip && paintInfo.phase != PaintPhase::Mask && paintInfo.phase != PaintPhase::EventRegion)
1154 return;
1155
1156 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom));
1157 flipForWritingMode(overflowRect);
1158 overflowRect.moveBy(paintOffset);
1159
1160 if (!paintInfo.rect.intersects(snappedIntRect(overflowRect)))
1161 return;
1162
1163 if (paintInfo.phase != PaintPhase::ChildOutlines) {
1164 if (paintInfo.phase == PaintPhase::Outline || paintInfo.phase == PaintPhase::SelfOutline) {
1165 // Add ourselves to the paint info struct's list of inlines that need to paint their
1166 // outlines.
1167 if (renderer().style().visibility() == Visibility::Visible && renderer().hasOutline() && !isRootInlineBox()) {
1168 RenderInline& inlineFlow = downcast<RenderInline>(renderer());
1169
1170 RenderBlock* containingBlock = nullptr;
1171 bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isContinuation();
1172 if (containingBlockPaintsContinuationOutline) {
1173 // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
1174 // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
1175 // anonymous blocks. In this case, it is better to bail out and paint it ourself.
1176 RenderBlock* enclosingAnonymousBlock = renderer().containingBlock();
1177 if (!enclosingAnonymousBlock->isAnonymousBlock())
1178 containingBlockPaintsContinuationOutline = false;
1179 else {
1180 containingBlock = enclosingAnonymousBlock->containingBlock();
1181 for (auto* box = &renderer(); box != containingBlock; box = &box->parent()->enclosingBoxModelObject()) {
1182 if (box->hasSelfPaintingLayer()) {
1183 containingBlockPaintsContinuationOutline = false;
1184 break;
1185 }
1186 }
1187 }
1188 }
1189
1190 if (containingBlockPaintsContinuationOutline) {
1191 // Add ourselves to the containing block of the entire continuation so that it can
1192 // paint us atomically.
1193 containingBlock->addContinuationWithOutline(downcast<RenderInline>(renderer().element()->renderer()));
1194 } else if (!inlineFlow.isContinuation())
1195 paintInfo.outlineObjects->add(&inlineFlow);
1196 }
1197 } else if (paintInfo.phase == PaintPhase::Mask)
1198 paintMask(paintInfo, paintOffset);
1199 else {
1200 // Paint our background, border and box-shadow.
1201 paintBoxDecorations(paintInfo, paintOffset);
1202 }
1203 }
1204
1205 if (paintInfo.phase == PaintPhase::Mask)
1206 return;
1207
1208 PaintPhase paintPhase = paintInfo.phase == PaintPhase::ChildOutlines ? PaintPhase::Outline : paintInfo.phase;
1209 PaintInfo childInfo(paintInfo);
1210 childInfo.phase = paintPhase;
1211 childInfo.updateSubtreePaintRootForChildren(&renderer());
1212
1213 // Paint our children.
1214 if (paintPhase != PaintPhase::SelfOutline) {
1215 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
1216 if (curr->renderer().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
1217 curr->paint(childInfo, paintOffset, lineTop, lineBottom);
1218 }
1219 }
1220}
1221
1222void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& color, const FillLayer& fillLayer, const LayoutRect& rect, CompositeOperator op)
1223{
1224 Vector<const FillLayer*, 8> layers;
1225 for (auto* layer = &fillLayer; layer; layer = layer->next())
1226 layers.append(layer);
1227 layers.reverse();
1228 for (auto* layer : layers)
1229 paintFillLayer(paintInfo, color, *layer, rect, op);
1230}
1231
1232bool InlineFlowBox::boxShadowCanBeAppliedToBackground(const FillLayer& lastBackgroundLayer) const
1233{
1234 // The checks here match how paintFillLayer() decides whether to clip (if it does, the shadow
1235 // would be clipped out, so it has to be drawn separately).
1236 StyleImage* image = lastBackgroundLayer.image();
1237 bool hasFillImage = image && image->canRender(&renderer(), renderer().style().effectiveZoom());
1238 return (!hasFillImage && !renderer().style().hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent();
1239}
1240
1241void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& color, const FillLayer& fillLayer, const LayoutRect& rect, CompositeOperator op)
1242{
1243 auto* image = fillLayer.image();
1244 bool hasFillImage = image && image->canRender(&renderer(), renderer().style().effectiveZoom());
1245 if ((!hasFillImage && !renderer().style().hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent())
1246 renderer().paintFillLayerExtended(paintInfo, color, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
1247#if ENABLE(CSS_BOX_DECORATION_BREAK)
1248 else if (renderer().style().boxDecorationBreak() == BoxDecorationBreak::Clone) {
1249 GraphicsContextStateSaver stateSaver(paintInfo.context());
1250 paintInfo.context().clip(LayoutRect(rect.x(), rect.y(), width(), height()));
1251 renderer().paintFillLayerExtended(paintInfo, color, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op);
1252 }
1253#endif
1254 else {
1255 // We have a fill image that spans multiple lines.
1256 // We need to adjust tx and ty by the width of all previous lines.
1257 // Think of background painting on inlines as though you had one long line, a single continuous
1258 // strip. Even though that strip has been broken up across multiple lines, you still paint it
1259 // as though you had one single line. This means each line has to pick up the background where
1260 // the previous line left off.
1261 LayoutUnit logicalOffsetOnLine;
1262 LayoutUnit totalLogicalWidth;
1263 if (renderer().style().direction() == TextDirection::LTR) {
1264 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1265 logicalOffsetOnLine += curr->logicalWidth();
1266 totalLogicalWidth = logicalOffsetOnLine;
1267 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1268 totalLogicalWidth += curr->logicalWidth();
1269 } else {
1270 for (InlineFlowBox* curr = nextLineBox(); curr; curr = curr->nextLineBox())
1271 logicalOffsetOnLine += curr->logicalWidth();
1272 totalLogicalWidth = logicalOffsetOnLine;
1273 for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox())
1274 totalLogicalWidth += curr->logicalWidth();
1275 }
1276 LayoutUnit stripX = rect.x() - (isHorizontal() ? logicalOffsetOnLine : 0_lu);
1277 LayoutUnit stripY = rect.y() - (isHorizontal() ? 0_lu : logicalOffsetOnLine);
1278 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : LayoutUnit(width());
1279 LayoutUnit stripHeight = isHorizontal() ? LayoutUnit(height()) : totalLogicalWidth;
1280
1281 GraphicsContextStateSaver stateSaver(paintInfo.context());
1282 paintInfo.context().clip(LayoutRect(rect.x(), rect.y(), width(), height()));
1283 renderer().paintFillLayerExtended(paintInfo, color, fillLayer, LayoutRect(stripX, stripY, stripWidth, stripHeight), BackgroundBleedNone, this, rect.size(), op);
1284 }
1285}
1286
1287void InlineFlowBox::paintBoxShadow(const PaintInfo& info, const RenderStyle& style, ShadowStyle shadowStyle, const LayoutRect& paintRect)
1288{
1289 if ((!prevLineBox() && !nextLineBox()) || !parent())
1290 renderer().paintBoxShadow(info, paintRect, style, shadowStyle);
1291 else {
1292 // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't
1293 // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines
1294 renderer().paintBoxShadow(info, paintRect, style, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge());
1295 }
1296}
1297
1298void InlineFlowBox::constrainToLineTopAndBottomIfNeeded(LayoutRect& rect) const
1299{
1300 bool noQuirksMode = renderer().document().inNoQuirksMode();
1301 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) {
1302 const RootInlineBox& rootBox = root();
1303 LayoutUnit logicalTop = isHorizontal() ? rect.y() : rect.x();
1304 LayoutUnit logicalHeight = isHorizontal() ? rect.height() : rect.width();
1305 LayoutUnit bottom = std::min(rootBox.lineBottom(), logicalTop + logicalHeight);
1306 logicalTop = std::max(rootBox.lineTop(), logicalTop);
1307 logicalHeight = bottom - logicalTop;
1308 if (isHorizontal()) {
1309 rect.setY(logicalTop);
1310 rect.setHeight(logicalHeight);
1311 } else {
1312 rect.setX(logicalTop);
1313 rect.setWidth(logicalHeight);
1314 }
1315 }
1316}
1317
1318static LayoutRect clipRectForNinePieceImageStrip(InlineFlowBox* box, const NinePieceImage& image, const LayoutRect& paintRect)
1319{
1320 LayoutRect clipRect(paintRect);
1321 auto& style = box->renderer().style();
1322 LayoutBoxExtent outsets = style.imageOutsets(image);
1323 if (box->isHorizontal()) {
1324 clipRect.setY(paintRect.y() - outsets.top());
1325 clipRect.setHeight(paintRect.height() + outsets.top() + outsets.bottom());
1326 if (box->includeLogicalLeftEdge()) {
1327 clipRect.setX(paintRect.x() - outsets.left());
1328 clipRect.setWidth(paintRect.width() + outsets.left());
1329 }
1330 if (box->includeLogicalRightEdge())
1331 clipRect.setWidth(clipRect.width() + outsets.right());
1332 } else {
1333 clipRect.setX(paintRect.x() - outsets.left());
1334 clipRect.setWidth(paintRect.width() + outsets.left() + outsets.right());
1335 if (box->includeLogicalLeftEdge()) {
1336 clipRect.setY(paintRect.y() - outsets.top());
1337 clipRect.setHeight(paintRect.height() + outsets.top());
1338 }
1339 if (box->includeLogicalRightEdge())
1340 clipRect.setHeight(clipRect.height() + outsets.bottom());
1341 }
1342 return clipRect;
1343}
1344
1345void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1346{
1347 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != Visibility::Visible || paintInfo.phase != PaintPhase::Foreground)
1348 return;
1349
1350 LayoutRect frameRect(this->frameRect());
1351 constrainToLineTopAndBottomIfNeeded(frameRect);
1352
1353 // Move x/y to our coordinates.
1354 LayoutRect localRect(frameRect);
1355 flipForWritingMode(localRect);
1356
1357 // You can use p::first-line to specify a background. If so, the root line boxes for
1358 // a line may actually have to paint a background.
1359 if (parent() && !renderer().hasVisibleBoxDecorations())
1360 return;
1361 const RenderStyle& lineStyle = this->lineStyle();
1362 if (!parent() && (!isFirstLine() || &lineStyle == &renderer().style()))
1363 return;
1364
1365 LayoutPoint adjustedPaintoffset = paintOffset + localRect.location();
1366 GraphicsContext& context = paintInfo.context();
1367 LayoutRect paintRect = LayoutRect(adjustedPaintoffset, frameRect.size());
1368 // Shadow comes first and is behind the background and border.
1369 if (!renderer().boxShadowShouldBeAppliedToBackground(adjustedPaintoffset, BackgroundBleedNone, this))
1370 paintBoxShadow(paintInfo, lineStyle, Normal, paintRect);
1371
1372 Color color = lineStyle.visitedDependentColor(CSSPropertyBackgroundColor);
1373
1374 CompositeOperator compositeOp = CompositeSourceOver;
1375 if (renderer().document().settings().punchOutWhiteBackgroundsInDarkMode() && Color::isWhiteColor(color) && renderer().useDarkAppearance())
1376 compositeOp = CompositeDestinationOut;
1377
1378 color = lineStyle.colorByApplyingColorFilter(color);
1379
1380 paintFillLayers(paintInfo, color, lineStyle.backgroundLayers(), paintRect, compositeOp);
1381 paintBoxShadow(paintInfo, lineStyle, Inset, paintRect);
1382
1383 // :first-line cannot be used to put borders on a line. Always paint borders with our
1384 // non-first-line style.
1385 if (!parent() || !renderer().style().hasVisibleBorderDecoration())
1386 return;
1387 const NinePieceImage& borderImage = renderer().style().borderImage();
1388 StyleImage* borderImageSource = borderImage.image();
1389 bool hasBorderImage = borderImageSource && borderImageSource->canRender(&renderer(), lineStyle.effectiveZoom());
1390 if (hasBorderImage && !borderImageSource->isLoaded())
1391 return; // Don't paint anything while we wait for the image to load.
1392
1393 // The simple case is where we either have no border image or we are the only box for this object. In those
1394 // cases only a single call to draw is required.
1395 if (!hasBorderImage || (!prevLineBox() && !nextLineBox()))
1396 renderer().paintBorder(paintInfo, paintRect, lineStyle, BackgroundBleedNone, includeLogicalLeftEdge(), includeLogicalRightEdge());
1397 else {
1398 // We have a border image that spans multiple lines.
1399 // We need to adjust tx and ty by the width of all previous lines.
1400 // Think of border image painting on inlines as though you had one long line, a single continuous
1401 // strip. Even though that strip has been broken up across multiple lines, you still paint it
1402 // as though you had one single line. This means each line has to pick up the image where
1403 // the previous line left off.
1404 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
1405 // but it isn't even clear how this should work at all.
1406 LayoutUnit logicalOffsetOnLine;
1407 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1408 logicalOffsetOnLine += curr->logicalWidth();
1409 LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
1410 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1411 totalLogicalWidth += curr->logicalWidth();
1412 LayoutUnit stripX = adjustedPaintoffset.x() - (isHorizontal() ? logicalOffsetOnLine : 0_lu);
1413 LayoutUnit stripY = adjustedPaintoffset.y() - (isHorizontal() ? 0_lu : logicalOffsetOnLine);
1414 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
1415 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
1416
1417 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, borderImage, paintRect);
1418 GraphicsContextStateSaver stateSaver(context);
1419 context.clip(clipRect);
1420 renderer().paintBorder(paintInfo, LayoutRect(stripX, stripY, stripWidth, stripHeight), lineStyle);
1421 }
1422}
1423
1424void InlineFlowBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1425{
1426 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer().style().visibility() != Visibility::Visible || paintInfo.phase != PaintPhase::Mask)
1427 return;
1428
1429 LayoutRect frameRect(this->frameRect());
1430 constrainToLineTopAndBottomIfNeeded(frameRect);
1431
1432 // Move x/y to our coordinates.
1433 LayoutRect localRect(frameRect);
1434 flipForWritingMode(localRect);
1435 LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();
1436
1437 const NinePieceImage& maskNinePieceImage = renderer().style().maskBoxImage();
1438 StyleImage* maskBoxImage = renderer().style().maskBoxImage().image();
1439
1440 // Figure out if we need to push a transparency layer to render our mask.
1441 bool pushTransparencyLayer = false;
1442 bool compositedMask = renderer().hasLayer() && renderer().layer()->hasCompositedMask();
1443 bool flattenCompositingLayers = renderer().view().frameView().paintBehavior().contains(PaintBehavior::FlattenCompositingLayers);
1444 CompositeOperator compositeOp = CompositeSourceOver;
1445 if (!compositedMask || flattenCompositingLayers) {
1446 if ((maskBoxImage && renderer().style().maskLayers().hasImage()) || renderer().style().maskLayers().next())
1447 pushTransparencyLayer = true;
1448
1449 compositeOp = CompositeDestinationIn;
1450 if (pushTransparencyLayer) {
1451 paintInfo.context().setCompositeOperation(CompositeDestinationIn);
1452 paintInfo.context().beginTransparencyLayer(1.0f);
1453 compositeOp = CompositeSourceOver;
1454 }
1455 }
1456
1457 LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size());
1458 paintFillLayers(paintInfo, Color(), renderer().style().maskLayers(), paintRect, compositeOp);
1459
1460 bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(&renderer(), renderer().style().effectiveZoom());
1461 if (!hasBoxImage || !maskBoxImage->isLoaded()) {
1462 if (pushTransparencyLayer)
1463 paintInfo.context().endTransparencyLayer();
1464 return; // Don't paint anything while we wait for the image to load.
1465 }
1466
1467 // The simple case is where we are the only box for this object. In those
1468 // cases only a single call to draw is required.
1469 if (!prevLineBox() && !nextLineBox()) {
1470 renderer().paintNinePieceImage(paintInfo.context(), LayoutRect(adjustedPaintOffset, frameRect.size()), renderer().style(), maskNinePieceImage, compositeOp);
1471 } else {
1472 // We have a mask image that spans multiple lines.
1473 // We need to adjust _tx and _ty by the width of all previous lines.
1474 LayoutUnit logicalOffsetOnLine;
1475 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
1476 logicalOffsetOnLine += curr->logicalWidth();
1477 LayoutUnit totalLogicalWidth = logicalOffsetOnLine;
1478 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox())
1479 totalLogicalWidth += curr->logicalWidth();
1480 LayoutUnit stripX = adjustedPaintOffset.x() - (isHorizontal() ? logicalOffsetOnLine : 0_lu);
1481 LayoutUnit stripY = adjustedPaintOffset.y() - (isHorizontal() ? 0_lu : logicalOffsetOnLine);
1482 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width();
1483 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth;
1484
1485 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, maskNinePieceImage, paintRect);
1486 GraphicsContextStateSaver stateSaver(paintInfo.context());
1487 paintInfo.context().clip(clipRect);
1488 renderer().paintNinePieceImage(paintInfo.context(), LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer().style(), maskNinePieceImage, compositeOp);
1489 }
1490
1491 if (pushTransparencyLayer)
1492 paintInfo.context().endTransparencyLayer();
1493}
1494
1495InlineBox* InlineFlowBox::firstLeafChild() const
1496{
1497 InlineBox* leaf = nullptr;
1498 for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine())
1499 leaf = child->isLeaf() ? child : downcast<InlineFlowBox>(*child).firstLeafChild();
1500 return leaf;
1501}
1502
1503InlineBox* InlineFlowBox::lastLeafChild() const
1504{
1505 InlineBox* leaf = nullptr;
1506 for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine())
1507 leaf = child->isLeaf() ? child : downcast<InlineFlowBox>(*child).lastLeafChild();
1508 return leaf;
1509}
1510
1511RenderObject::SelectionState InlineFlowBox::selectionState()
1512{
1513 return RenderObject::SelectionNone;
1514}
1515
1516bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const
1517{
1518 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1519 if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth))
1520 return false;
1521 }
1522 return true;
1523}
1524
1525float InlineFlowBox::placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox)
1526{
1527 float result = -1;
1528 // We iterate over all children, the foundBox variable tells us when we've found the
1529 // box containing the ellipsis. All boxes after that one in the flow are hidden.
1530 // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate
1531 // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis.
1532 InlineBox* box = ltr ? firstChild() : lastChild();
1533
1534 // NOTE: these will cross after foundBox = true.
1535 int visibleLeftEdge = blockLeftEdge;
1536 int visibleRightEdge = blockRightEdge;
1537
1538 while (box) {
1539 int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, truncatedWidth, foundBox);
1540 if (currResult != -1 && result == -1)
1541 result = currResult;
1542
1543 if (ltr) {
1544 visibleLeftEdge += box->logicalWidth();
1545 box = box->nextOnLine();
1546 }
1547 else {
1548 visibleRightEdge -= box->logicalWidth();
1549 box = box->prevOnLine();
1550 }
1551 }
1552 return result;
1553}
1554
1555void InlineFlowBox::clearTruncation()
1556{
1557 for (InlineBox *box = firstChild(); box; box = box->nextOnLine())
1558 box->clearTruncation();
1559}
1560
1561LayoutUnit InlineFlowBox::computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const
1562{
1563 LayoutUnit result;
1564 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
1565 if (child->renderer().isOutOfFlowPositioned())
1566 continue; // Positioned placeholders don't affect calculations.
1567
1568 if (is<InlineFlowBox>(*child))
1569 result = std::max(result, downcast<InlineFlowBox>(*child).computeOverAnnotationAdjustment(allowedPosition));
1570
1571 if (child->renderer().isReplaced() && is<RenderRubyRun>(child->renderer()) && child->renderer().style().rubyPosition() == RubyPosition::Before) {
1572 auto& rubyRun = downcast<RenderRubyRun>(child->renderer());
1573 RenderRubyText* rubyText = rubyRun.rubyText();
1574 if (!rubyText)
1575 continue;
1576
1577 if (!rubyRun.style().isFlippedLinesWritingMode()) {
1578 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : 0_lu);
1579 if (topOfFirstRubyTextLine >= 0)
1580 continue;
1581 topOfFirstRubyTextLine += child->logicalTop();
1582 result = std::max(result, allowedPosition - topOfFirstRubyTextLine);
1583 } else {
1584 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight());
1585 if (bottomOfLastRubyTextLine <= child->logicalHeight())
1586 continue;
1587 bottomOfLastRubyTextLine += child->logicalTop();
1588 result = std::max(result, bottomOfLastRubyTextLine - allowedPosition);
1589 }
1590 }
1591
1592 if (is<InlineTextBox>(*child)) {
1593 const RenderStyle& childLineStyle = child->lineStyle();
1594 Optional<bool> markExistsAndIsAbove = downcast<InlineTextBox>(*child).emphasisMarkExistsAndIsAbove(childLineStyle);
1595 if (markExistsAndIsAbove && *markExistsAndIsAbove) {
1596 if (!childLineStyle.isFlippedLinesWritingMode()) {
1597 int topOfEmphasisMark = child->logicalTop() - childLineStyle.fontCascade().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1598 result = std::max(result, allowedPosition - topOfEmphasisMark);
1599 } else {
1600 int bottomOfEmphasisMark = child->logicalBottom() + childLineStyle.fontCascade().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1601 result = std::max(result, bottomOfEmphasisMark - allowedPosition);
1602 }
1603 }
1604 }
1605 }
1606 return result;
1607}
1608
1609LayoutUnit InlineFlowBox::computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const
1610{
1611 LayoutUnit result;
1612 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
1613 if (child->renderer().isOutOfFlowPositioned())
1614 continue; // Positioned placeholders don't affect calculations.
1615
1616 if (is<InlineFlowBox>(*child))
1617 result = std::max(result, downcast<InlineFlowBox>(*child).computeUnderAnnotationAdjustment(allowedPosition));
1618
1619 if (child->renderer().isReplaced() && is<RenderRubyRun>(child->renderer()) && child->renderer().style().rubyPosition() == RubyPosition::After) {
1620 auto& rubyRun = downcast<RenderRubyRun>(child->renderer());
1621 RenderRubyText* rubyText = rubyRun.rubyText();
1622 if (!rubyText)
1623 continue;
1624
1625 if (rubyRun.style().isFlippedLinesWritingMode()) {
1626 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : 0_lu);
1627 if (topOfFirstRubyTextLine >= 0)
1628 continue;
1629 topOfFirstRubyTextLine += child->logicalTop();
1630 result = std::max(result, allowedPosition - topOfFirstRubyTextLine);
1631 } else {
1632 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight());
1633 if (bottomOfLastRubyTextLine <= child->logicalHeight())
1634 continue;
1635 bottomOfLastRubyTextLine += child->logicalTop();
1636 result = std::max(result, bottomOfLastRubyTextLine - allowedPosition);
1637 }
1638 }
1639
1640 if (is<InlineTextBox>(*child)) {
1641 const RenderStyle& childLineStyle = child->lineStyle();
1642 Optional<bool> markExistsAndIsAbove = downcast<InlineTextBox>(*child).emphasisMarkExistsAndIsAbove(childLineStyle);
1643 if (markExistsAndIsAbove && !*markExistsAndIsAbove) {
1644 if (!childLineStyle.isFlippedLinesWritingMode()) {
1645 LayoutUnit bottomOfEmphasisMark = child->logicalBottom() + childLineStyle.fontCascade().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1646 result = std::max(result, bottomOfEmphasisMark - allowedPosition);
1647 } else {
1648 LayoutUnit topOfEmphasisMark = child->logicalTop() - childLineStyle.fontCascade().emphasisMarkHeight(childLineStyle.textEmphasisMarkString());
1649 result = std::max(result, allowedPosition - topOfEmphasisMark);
1650 }
1651 }
1652 }
1653 }
1654 return result;
1655}
1656
1657void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const
1658{
1659 InlineBox* leaf = firstLeafChild();
1660
1661 // FIXME: The reordering code is a copy of parts from BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, instead of BidiRuns.
1662 // Investigate on how this code could possibly be shared.
1663 unsigned char minLevel = 128;
1664 unsigned char maxLevel = 0;
1665
1666 // First find highest and lowest levels, and initialize leafBoxesInLogicalOrder with the leaf boxes in visual order.
1667 for (; leaf; leaf = leaf->nextLeafChild()) {
1668 minLevel = std::min(minLevel, leaf->bidiLevel());
1669 maxLevel = std::max(maxLevel, leaf->bidiLevel());
1670 leafBoxesInLogicalOrder.append(leaf);
1671 }
1672
1673 if (renderer().style().rtlOrdering() == Order::Visual)
1674 return;
1675
1676 // Reverse of reordering of the line (L2 according to Bidi spec):
1677 // L2. From the highest level found in the text to the lowest odd level on each line,
1678 // reverse any contiguous sequence of characters that are at that level or higher.
1679
1680 // Reversing the reordering of the line is only done up to the lowest odd level.
1681 if (!(minLevel % 2))
1682 ++minLevel;
1683
1684 Vector<InlineBox*>::iterator end = leafBoxesInLogicalOrder.end();
1685 while (minLevel <= maxLevel) {
1686 Vector<InlineBox*>::iterator it = leafBoxesInLogicalOrder.begin();
1687 while (it != end) {
1688 while (it != end) {
1689 if ((*it)->bidiLevel() >= minLevel)
1690 break;
1691 ++it;
1692 }
1693 Vector<InlineBox*>::iterator first = it;
1694 while (it != end) {
1695 if ((*it)->bidiLevel() < minLevel)
1696 break;
1697 ++it;
1698 }
1699 Vector<InlineBox*>::iterator last = it;
1700 if (customReverseImplementation) {
1701 ASSERT(userData);
1702 (*customReverseImplementation)(userData, first, last);
1703 } else
1704 std::reverse(first, last);
1705 }
1706 ++minLevel;
1707 }
1708}
1709
1710void InlineFlowBox::computeReplacedAndTextLineTopAndBottom(LayoutUnit& lineTop, LayoutUnit& lineBottom) const
1711{
1712 for (const auto* box = firstChild(); box; box = box->nextOnLine()) {
1713 if (is<InlineFlowBox>(*box))
1714 downcast<InlineFlowBox>(*box).computeReplacedAndTextLineTopAndBottom(lineTop, lineBottom);
1715 else {
1716 if (box->logicalTop() < lineTop)
1717 lineTop = box->logicalTop();
1718 if (box->logicalBottom() > lineBottom)
1719 lineBottom = box->logicalBottom();
1720 }
1721 }
1722}
1723
1724#if ENABLE(TREE_DEBUGGING)
1725
1726const char* InlineFlowBox::boxName() const
1727{
1728 return "InlineFlowBox";
1729}
1730
1731void InlineFlowBox::outputLineTreeAndMark(WTF::TextStream& stream, const InlineBox* markedBox, int depth) const
1732{
1733 InlineBox::outputLineTreeAndMark(stream, markedBox, depth);
1734 for (const InlineBox* box = firstChild(); box; box = box->nextOnLine())
1735 box->outputLineTreeAndMark(stream, markedBox, depth + 1);
1736}
1737
1738#endif
1739
1740#ifndef NDEBUG
1741
1742void InlineFlowBox::checkConsistency() const
1743{
1744 assertNotDeleted();
1745 ASSERT_WITH_SECURITY_IMPLICATION(!m_hasBadChildList);
1746#ifdef CHECK_CONSISTENCY
1747 const InlineBox* previousChild = nullptr;
1748 for (const InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
1749 ASSERT(child->parent() == this);
1750 ASSERT(child->prevOnLine() == previousChild);
1751 previousChild = child;
1752 }
1753 ASSERT(previousChild == m_lastChild);
1754#endif
1755}
1756
1757#endif
1758
1759} // namespace WebCore
1760