2 * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 "core/layout/line/InlineBox.h"
23 #include "core/layout/HitTestLocation.h"
24 #include "core/layout/LayoutBlockFlow.h"
25 #include "core/layout/LayoutObject.h"
26 #include "core/layout/line/InlineFlowBox.h"
27 #include "core/layout/line/RootInlineBox.h"
28 #include "core/paint/BlockPainter.h"
29 #include "core/paint/PaintInfo.h"
30 #include "platform/fonts/FontMetrics.h"
31 #include "wtf/Partitions.h"
39 struct SameSizeAsInlineBox
{
40 virtual ~SameSizeAsInlineBox() { }
50 static_assert(sizeof(InlineBox
) == sizeof(SameSizeAsInlineBox
), "InlineBox should stay small");
54 InlineBox::~InlineBox()
56 if (!m_hasBadParent
&& m_parent
)
57 m_parent
->setHasBadChildList();
62 void InlineBox::destroy()
64 // We do not need to issue invalidations if the page is being destroyed
65 // since these objects will never be repainted.
66 if (!m_layoutObject
.documentBeingDestroyed())
67 m_layoutObject
.invalidateDisplayItemClient(*this);
71 void InlineBox::remove(MarkLineBoxes markLineBoxes
)
74 parent()->removeChild(this, markLineBoxes
);
77 void* InlineBox::operator new(size_t sz
)
79 return partitionAlloc(WTF::Partitions::layoutPartition(), sz
);
82 void InlineBox::operator delete(void* ptr
)
87 const char* InlineBox::boxName() const
92 String
InlineBox::debugName() const
98 void InlineBox::showTreeForThis() const
100 layoutObject().showTreeForThis();
103 void InlineBox::showLineTreeForThis() const
105 layoutObject().containingBlock()->showLineTreeAndMark(this, "*");
108 void InlineBox::showLineTreeAndMark(const InlineBox
* markedBox1
, const char* markedLabel1
, const InlineBox
* markedBox2
, const char* markedLabel2
, const LayoutObject
* obj
, int depth
) const
110 int printedCharacters
= 0;
111 if (this == markedBox1
)
112 printedCharacters
+= fprintf(stderr
, "%s", markedLabel1
);
113 if (this == markedBox2
)
114 printedCharacters
+= fprintf(stderr
, "%s", markedLabel2
);
115 if (&layoutObject() == obj
)
116 printedCharacters
+= fprintf(stderr
, "*");
117 for (; printedCharacters
< depth
* 2; printedCharacters
++)
120 showBox(printedCharacters
);
123 void InlineBox::showBox(int printedCharacters
) const
125 printedCharacters
+= fprintf(stderr
, "%s %p", boxName(), this);
126 for (; printedCharacters
< showTreeCharacterOffset
; printedCharacters
++)
128 fprintf(stderr
, "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i\n",
129 layoutObject().decoratedName().ascii().data(), &layoutObject(),
130 x().toFloat(), y().toFloat(), width().toFloat(), height().toFloat(),
131 baselinePosition(AlphabeticBaseline
), baselinePosition(IdeographicBaseline
));
135 LayoutUnit
InlineBox::logicalHeight() const
137 if (hasVirtualLogicalHeight())
138 return virtualLogicalHeight();
140 if (lineLayoutItem().isText())
141 return m_bitfields
.isText() ? LayoutUnit(lineLayoutItem().style(isFirstLineStyle())->fontMetrics().height()) : LayoutUnit();
142 if (lineLayoutItem().isBox() && parent())
143 return isHorizontal() ? toLayoutBox(layoutObject()).size().height() : toLayoutBox(layoutObject()).size().width();
145 ASSERT(isInlineFlowBox());
146 LineLayoutBoxModel flowObject
= boxModelObject();
147 const FontMetrics
& fontMetrics
= lineLayoutItem().style(isFirstLineStyle())->fontMetrics();
148 LayoutUnit result
= fontMetrics
.height();
150 result
+= flowObject
.borderAndPaddingLogicalHeight();
154 int InlineBox::baselinePosition(FontBaseline baselineType
) const
156 return boxModelObject().baselinePosition(baselineType
, m_bitfields
.firstLine(), isHorizontal() ? HorizontalLine
: VerticalLine
, PositionOnContainingLine
);
159 LayoutUnit
InlineBox::lineHeight() const
161 return boxModelObject().lineHeight(m_bitfields
.firstLine(), isHorizontal() ? HorizontalLine
: VerticalLine
, PositionOnContainingLine
);
164 int InlineBox::caretMinOffset() const
166 return lineLayoutItem().caretMinOffset();
169 int InlineBox::caretMaxOffset() const
171 return lineLayoutItem().caretMaxOffset();
174 void InlineBox::dirtyLineBoxes()
177 for (InlineFlowBox
* curr
= parent(); curr
&& !curr
->isDirty(); curr
= curr
->parent())
181 void InlineBox::deleteLine()
183 if (!m_bitfields
.extracted() && lineLayoutItem().isBox())
184 toLayoutBox(layoutObject()).setInlineBoxWrapper(nullptr);
188 void InlineBox::extractLine()
190 m_bitfields
.setExtracted(true);
191 if (lineLayoutItem().isBox())
192 toLayoutBox(layoutObject()).setInlineBoxWrapper(nullptr);
195 void InlineBox::attachLine()
197 m_bitfields
.setExtracted(false);
198 if (lineLayoutItem().isBox())
199 toLayoutBox(layoutObject()).setInlineBoxWrapper(this);
202 void InlineBox::move(const LayoutSize
& delta
)
204 m_topLeft
.move(delta
);
206 if (lineLayoutItem().isReplaced())
207 toLayoutBox(layoutObject()).move(delta
.width(), delta
.height());
210 void InlineBox::paint(const PaintInfo
& paintInfo
, const LayoutPoint
& paintOffset
, LayoutUnit
/* lineTop */, LayoutUnit
/* lineBottom */)
212 // Text clips are painted only for the direct inline children of the object that has a text clip style on it, not block children.
213 if (paintInfo
.phase
!= PaintPhaseTextClip
)
214 BlockPainter::paintInlineBox(*this, paintInfo
, paintOffset
);
217 bool InlineBox::nodeAtPoint(HitTestResult
& result
, const HitTestLocation
& locationInContainer
, const LayoutPoint
& accumulatedOffset
, LayoutUnit
/* lineTop */, LayoutUnit
/* lineBottom */)
219 // Hit test all phases of replaced elements atomically, as though the replaced element established its
220 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
222 LayoutPoint childPoint
= accumulatedOffset
;
223 if (parent()->lineLayoutItem().hasFlippedBlocksWritingMode()) // Faster than calling containingBlock().
224 childPoint
= layoutObject().containingBlock()->flipForWritingModeForChild(&toLayoutBox(layoutObject()), childPoint
);
226 if (lineLayoutItem().style()->hasBorderRadius()) {
227 LayoutRect borderRect
= logicalFrameRect();
228 borderRect
.moveBy(accumulatedOffset
);
229 FloatRoundedRect border
= lineLayoutItem().style()->getRoundedBorderFor(borderRect
);
230 if (!locationInContainer
.intersects(border
))
234 return lineLayoutItem().hitTest(result
, locationInContainer
, childPoint
);
237 const RootInlineBox
& InlineBox::root() const
240 return m_parent
->root();
241 ASSERT(isRootInlineBox());
242 return static_cast<const RootInlineBox
&>(*this);
245 RootInlineBox
& InlineBox::root()
248 return m_parent
->root();
249 ASSERT(isRootInlineBox());
250 return static_cast<RootInlineBox
&>(*this);
253 bool InlineBox::nextOnLineExists() const
255 if (!m_bitfields
.determinedIfNextOnLineExists()) {
256 m_bitfields
.setDeterminedIfNextOnLineExists(true);
259 m_bitfields
.setNextOnLineExists(false);
260 else if (nextOnLine())
261 m_bitfields
.setNextOnLineExists(true);
263 m_bitfields
.setNextOnLineExists(parent()->nextOnLineExists());
265 return m_bitfields
.nextOnLineExists();
268 InlineBox
* InlineBox::nextLeafChild() const
270 InlineBox
* leaf
= nullptr;
271 for (InlineBox
* box
= nextOnLine(); box
&& !leaf
; box
= box
->nextOnLine())
272 leaf
= box
->isLeaf() ? box
: toInlineFlowBox(box
)->firstLeafChild();
273 if (!leaf
&& parent())
274 leaf
= parent()->nextLeafChild();
278 InlineBox
* InlineBox::prevLeafChild() const
280 InlineBox
* leaf
= nullptr;
281 for (InlineBox
* box
= prevOnLine(); box
&& !leaf
; box
= box
->prevOnLine())
282 leaf
= box
->isLeaf() ? box
: toInlineFlowBox(box
)->lastLeafChild();
283 if (!leaf
&& parent())
284 leaf
= parent()->prevLeafChild();
288 InlineBox
* InlineBox::nextLeafChildIgnoringLineBreak() const
290 InlineBox
* leaf
= nextLeafChild();
291 return (leaf
&& leaf
->isLineBreak()) ? nullptr : leaf
;
294 InlineBox
* InlineBox::prevLeafChildIgnoringLineBreak() const
296 InlineBox
* leaf
= prevLeafChild();
297 return (leaf
&& leaf
->isLineBreak()) ? nullptr : leaf
;
300 SelectionState
InlineBox::selectionState() const
302 return layoutObject().selectionState();
305 bool InlineBox::canAccommodateEllipsis(bool ltr
, int blockEdge
, int ellipsisWidth
) const
307 // Non-replaced elements can always accommodate an ellipsis.
308 if (!lineLayoutItem().isReplaced())
311 IntRect
boxRect(left(), 0, m_logicalWidth
, 10);
312 IntRect
ellipsisRect(ltr
? blockEdge
- ellipsisWidth
: blockEdge
, 0, ellipsisWidth
, 10);
313 return !(boxRect
.intersects(ellipsisRect
));
316 LayoutUnit
InlineBox::placeEllipsisBox(bool, LayoutUnit
, LayoutUnit
, LayoutUnit
, LayoutUnit
& truncatedWidth
, bool&)
318 // Use -1 to mean "we didn't set the position."
319 truncatedWidth
+= logicalWidth();
323 void InlineBox::clearKnownToHaveNoOverflow()
325 m_bitfields
.setKnownToHaveNoOverflow(false);
326 if (parent() && parent()->knownToHaveNoOverflow())
327 parent()->clearKnownToHaveNoOverflow();
330 LayoutPoint
InlineBox::locationIncludingFlipping()
332 return logicalPositionToPhysicalPoint(m_topLeft
, size());
335 LayoutPoint
InlineBox::logicalPositionToPhysicalPoint(const LayoutPoint
& point
, const LayoutSize
& size
)
337 if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode()))
338 return LayoutPoint(point
.x(), point
.y());
340 LayoutBlockFlow
& block
= root().block();
341 if (block
.style()->isHorizontalWritingMode())
342 return LayoutPoint(point
.x(), block
.size().height() - size
.height() - point
.y());
344 return LayoutPoint(block
.size().width() - size
.width() - point
.x(), point
.y());
347 void InlineBox::logicalRectToPhysicalRect(LayoutRect
& current
)
349 if (isHorizontal() && !lineLayoutItem().hasFlippedBlocksWritingMode())
352 if (!isHorizontal()) {
353 current
= current
.transposedRect();
355 current
.setLocation(logicalPositionToPhysicalPoint(current
.location(), current
.size()));
359 void InlineBox::flipForWritingMode(FloatRect
& rect
)
361 if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode()))
363 root().block().flipForWritingMode(rect
);
366 FloatPoint
InlineBox::flipForWritingMode(const FloatPoint
& point
)
368 if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode()))
370 return root().block().flipForWritingMode(point
);
373 void InlineBox::flipForWritingMode(LayoutRect
& rect
)
375 if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode()))
377 root().block().flipForWritingMode(rect
);
380 LayoutPoint
InlineBox::flipForWritingMode(const LayoutPoint
& point
)
382 if (!UNLIKELY(lineLayoutItem().hasFlippedBlocksWritingMode()))
384 return root().block().flipForWritingMode(point
);
391 void showTree(const blink::InlineBox
* b
)
394 b
->showTreeForThis();
396 fprintf(stderr
, "Cannot showTree for (nil) InlineBox.\n");
399 void showLineTree(const blink::InlineBox
* b
)
402 b
->showLineTreeForThis();
404 fprintf(stderr
, "Cannot showLineTree for (nil) InlineBox.\n");