blob: 3aed697a1bfecdc2d8a3acad23704723a79478cf [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef InlineFlowBox_h
#define InlineFlowBox_h
#include "core/layout/OverflowModel.h"
#include "core/layout/api/SelectionState.h"
#include "core/layout/line/InlineBox.h"
#include "core/style/ShadowData.h"
#include <memory>
namespace blink {
class HitTestRequest;
class HitTestResult;
class InlineTextBox;
class LineBoxList;
class SimpleFontData;
class VerticalPositionCache;
struct GlyphOverflow;
typedef HashMap<const InlineTextBox*,
std::pair<Vector<const SimpleFontData*>, GlyphOverflow>>
GlyphOverflowAndFallbackFontsMap;
class InlineFlowBox : public InlineBox {
public:
InlineFlowBox(LineLayoutItem lineLayoutItem)
: InlineBox(lineLayoutItem),
m_firstChild(nullptr),
m_lastChild(nullptr),
m_prevLineBox(nullptr),
m_nextLineBox(nullptr),
m_includeLogicalLeftEdge(false),
m_includeLogicalRightEdge(false),
m_descendantsHaveSameLineHeightAndBaseline(true),
m_baselineType(AlphabeticBaseline),
m_hasAnnotationsBefore(false),
m_hasAnnotationsAfter(false),
m_lineBreakBidiStatusEor(WTF::Unicode::LeftToRight),
m_lineBreakBidiStatusLastStrong(WTF::Unicode::LeftToRight),
m_lineBreakBidiStatusLast(WTF::Unicode::LeftToRight),
m_isFirstAfterPageBreak(false)
#if ENABLE(ASSERT)
,
m_hasBadChildList(false)
#endif
{
// Internet Explorer and Firefox always create a marker for list items, even
// when the list-style-type is none. We do not make a marker in the
// list-style-type: none case, since it is wasteful to do so.
// However, in order to match other browsers we have to pretend like an
// invisible marker exists. The side effect of having an invisible marker
// is that the quirks mode behavior of shrinking lines with no text children
// must not apply. This change also means that gaps will exist between image
// bullet list items. Even when the list bullet is an image, the line is
// still considered to be immune from the quirk.
m_hasTextChildren = lineLayoutItem.style()->display() == EDisplay::ListItem;
m_hasTextDescendants = m_hasTextChildren;
}
#if ENABLE(ASSERT)
~InlineFlowBox() override;
#endif
#ifndef NDEBUG
void showLineTreeAndMark(const InlineBox* = nullptr,
const char* = nullptr,
const InlineBox* = nullptr,
const char* = nullptr,
const LayoutObject* = nullptr,
int = 0) const override;
#endif
const char* boxName() const override;
InlineFlowBox* prevLineBox() const { return m_prevLineBox; }
InlineFlowBox* nextLineBox() const { return m_nextLineBox; }
void setNextLineBox(InlineFlowBox* n) { m_nextLineBox = n; }
void setPreviousLineBox(InlineFlowBox* p) { m_prevLineBox = p; }
InlineBox* firstChild() const {
checkConsistency();
return m_firstChild;
}
InlineBox* lastChild() const {
checkConsistency();
return m_lastChild;
}
bool isLeaf() const final { return false; }
InlineBox* firstLeafChild() const;
InlineBox* lastLeafChild() const;
typedef void (*CustomInlineBoxRangeReverse)(
Vector<InlineBox*>::iterator first,
Vector<InlineBox*>::iterator last);
void collectLeafBoxesInLogicalOrder(
Vector<InlineBox*>&,
CustomInlineBoxRangeReverse customReverseImplementation = 0) const;
DISABLE_CFI_PERF
void setConstructed() final {
InlineBox::setConstructed();
for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
child->setConstructed();
}
void addToLine(InlineBox* child);
void deleteLine() final;
void extractLine() final;
void attachLine() final;
void move(const LayoutSize&) override;
virtual void extractLineBoxFromLayoutObject();
virtual void attachLineBoxToLayoutObject();
virtual void removeLineBoxFromLayoutObject();
void clearTruncation() override;
LayoutRect frameRect() const;
void paint(const PaintInfo&,
const LayoutPoint&,
LayoutUnit lineTop,
LayoutUnit lineBottom) const override;
bool nodeAtPoint(HitTestResult&,
const HitTestLocation& locationInContainer,
const LayoutPoint& accumulatedOffset,
LayoutUnit lineTop,
LayoutUnit lineBottom) override;
bool boxShadowCanBeAppliedToBackground(const FillLayer&) const;
virtual LineBoxList* lineBoxes() const;
// logicalLeft = left in a horizontal line and top in a vertical line.
LayoutUnit marginBorderPaddingLogicalLeft() const {
return marginLogicalLeft() + borderLogicalLeft() + paddingLogicalLeft();
}
LayoutUnit marginBorderPaddingLogicalRight() const {
return marginLogicalRight() + borderLogicalRight() + paddingLogicalRight();
}
LayoutUnit marginLogicalLeft() const {
if (!includeLogicalLeftEdge())
return LayoutUnit();
return isHorizontal() ? boxModelObject().marginLeft()
: boxModelObject().marginTop();
}
LayoutUnit marginLogicalRight() const {
if (!includeLogicalRightEdge())
return LayoutUnit();
return isHorizontal() ? boxModelObject().marginRight()
: boxModelObject().marginBottom();
}
int borderLogicalLeft() const {
if (!includeLogicalLeftEdge())
return 0;
return isHorizontal()
? getLineLayoutItem()
.style(isFirstLineStyle())
->borderLeftWidth()
: getLineLayoutItem()
.style(isFirstLineStyle())
->borderTopWidth();
}
int borderLogicalRight() const {
if (!includeLogicalRightEdge())
return 0;
return isHorizontal()
? getLineLayoutItem()
.style(isFirstLineStyle())
->borderRightWidth()
: getLineLayoutItem()
.style(isFirstLineStyle())
->borderBottomWidth();
}
int paddingLogicalLeft() const {
if (!includeLogicalLeftEdge())
return 0;
return (isHorizontal() ? boxModelObject().paddingLeft()
: boxModelObject().paddingTop())
.toInt();
}
int paddingLogicalRight() const {
if (!includeLogicalRightEdge())
return 0;
return (isHorizontal() ? boxModelObject().paddingRight()
: boxModelObject().paddingBottom())
.toInt();
}
bool includeLogicalLeftEdge() const { return m_includeLogicalLeftEdge; }
bool includeLogicalRightEdge() const { return m_includeLogicalRightEdge; }
void setEdges(bool includeLeft, bool includeRight) {
m_includeLogicalLeftEdge = includeLeft;
m_includeLogicalRightEdge = includeRight;
}
// Helper functions used during line construction and placement.
void determineSpacingForFlowBoxes(
bool lastLine,
bool isLogicallyLastRunWrapped,
LineLayoutItem logicallyLastRunLayoutObject);
LayoutUnit getFlowSpacingLogicalWidth();
LayoutUnit placeBoxesInInlineDirection(LayoutUnit logicalLeft,
bool& needsWordSpacing);
void computeLogicalBoxHeights(RootInlineBox*,
LayoutUnit& maxPositionTop,
LayoutUnit& maxPositionBottom,
int& maxAscent,
int& maxDescent,
bool& setMaxAscent,
bool& setMaxDescent,
bool noQuirksMode,
GlyphOverflowAndFallbackFontsMap&,
FontBaseline,
VerticalPositionCache&);
void adjustMaxAscentAndDescent(int& maxAscent,
int& maxDescent,
int maxPositionTop,
int maxPositionBottom);
void placeBoxesInBlockDirection(LayoutUnit logicalTop,
LayoutUnit maxHeight,
int maxAscent,
bool noQuirksMode,
LayoutUnit& lineTop,
LayoutUnit& lineBottom,
LayoutUnit& selectionBottom,
bool& setLineTop,
LayoutUnit& lineTopIncludingMargins,
LayoutUnit& lineBottomIncludingMargins,
bool& hasAnnotationsBefore,
bool& hasAnnotationsAfter,
FontBaseline);
void flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom);
FontBaseline dominantBaseline() const;
LayoutUnit computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const;
LayoutUnit computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const;
void computeOverflow(LayoutUnit lineTop,
LayoutUnit lineBottom,
GlyphOverflowAndFallbackFontsMap&);
void removeChild(InlineBox* child, MarkLineBoxes);
SelectionState getSelectionState() const override;
bool canAccommodateEllipsis(bool ltr,
int blockEdge,
int ellipsisWidth) const final;
LayoutUnit placeEllipsisBox(bool ltr,
LayoutUnit blockLeftEdge,
LayoutUnit blockRightEdge,
LayoutUnit ellipsisWidth,
LayoutUnit& truncatedWidth,
bool&) override;
bool hasTextChildren() const { return m_hasTextChildren; }
bool hasTextDescendants() const { return m_hasTextDescendants; }
void setHasTextDescendants() { m_hasTextDescendants = true; }
void checkConsistency() const;
void setHasBadChildList();
// Line visual and layout overflow are in the coordinate space of the block.
// This means that they aren't purely physical directions. For horizontal-tb
// and vertical-lr they will match physical directions, but for vertical-rl,
// the left/right respectively are flipped when compared to their physical
// counterparts. For example minX is on the left in vertical-lr, but it is on
// the right in vertical-rl.
LayoutRect layoutOverflowRect(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
return m_overflow ? m_overflow->layoutOverflowRect()
: frameRectIncludingLineHeight(lineTop, lineBottom);
}
LayoutUnit logicalTopLayoutOverflow(LayoutUnit lineTop) const {
if (m_overflow)
return isHorizontal() ? m_overflow->layoutOverflowRect().y()
: m_overflow->layoutOverflowRect().x();
return lineTop;
}
LayoutUnit logicalBottomLayoutOverflow(LayoutUnit lineBottom) const {
if (m_overflow)
return isHorizontal() ? m_overflow->layoutOverflowRect().maxY()
: m_overflow->layoutOverflowRect().maxX();
return lineBottom;
}
LayoutRect logicalLayoutOverflowRect(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
LayoutRect result = layoutOverflowRect(lineTop, lineBottom);
if (!getLineLayoutItem().isHorizontalWritingMode())
result = result.transposedRect();
return result;
}
LayoutRect visualOverflowRect(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
return m_overflow ? m_overflow->visualOverflowRect()
: frameRectIncludingLineHeight(lineTop, lineBottom);
}
LayoutUnit logicalLeftVisualOverflow() const {
return m_overflow ? (isHorizontal() ? m_overflow->visualOverflowRect().x()
: m_overflow->visualOverflowRect().y())
: logicalLeft();
}
LayoutUnit logicalRightVisualOverflow() const {
return m_overflow
? (isHorizontal() ? m_overflow->visualOverflowRect().maxX()
: m_overflow->visualOverflowRect().maxY())
: static_cast<LayoutUnit>(logicalRight().ceil());
}
LayoutUnit logicalTopVisualOverflow(LayoutUnit lineTop) const {
if (m_overflow)
return isHorizontal() ? m_overflow->visualOverflowRect().y()
: m_overflow->visualOverflowRect().x();
return lineTop;
}
LayoutUnit logicalBottomVisualOverflow(LayoutUnit lineBottom) const {
if (m_overflow)
return isHorizontal() ? m_overflow->visualOverflowRect().maxY()
: m_overflow->visualOverflowRect().maxX();
return lineBottom;
}
LayoutRect logicalVisualOverflowRect(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
LayoutRect result = visualOverflowRect(lineTop, lineBottom);
if (!getLineLayoutItem().isHorizontalWritingMode())
result = result.transposedRect();
return result;
}
LayoutRect frameRectIncludingLineHeight(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
if (isHorizontal())
return LayoutRect(m_topLeft.x(), lineTop, width(), lineBottom - lineTop);
return LayoutRect(lineTop, m_topLeft.y(), lineBottom - lineTop, height());
}
LayoutRect logicalFrameRectIncludingLineHeight(LayoutUnit lineTop,
LayoutUnit lineBottom) const {
return LayoutRect(logicalLeft(), lineTop, logicalWidth(),
lineBottom - lineTop);
}
bool descendantsHaveSameLineHeightAndBaseline() const {
return m_descendantsHaveSameLineHeightAndBaseline;
}
void clearDescendantsHaveSameLineHeightAndBaseline() {
m_descendantsHaveSameLineHeightAndBaseline = false;
if (parent() && parent()->descendantsHaveSameLineHeightAndBaseline())
parent()->clearDescendantsHaveSameLineHeightAndBaseline();
}
bool isFirstAfterPageBreak() const { return m_isFirstAfterPageBreak; }
void setIsFirstAfterPageBreak(bool isFirstAfterPageBreak) {
m_isFirstAfterPageBreak = isFirstAfterPageBreak;
}
// Some callers (LayoutListItem) needs to set extra overflow on their line
// box.
void overrideOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow,
const LayoutRect& logicalVisualOverflow,
LayoutUnit lineTop,
LayoutUnit lineBottom) {
// If we are setting an overflow, then we can't pretend not to have an
// overflow.
clearKnownToHaveNoOverflow();
setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow,
lineTop, lineBottom);
}
private:
void placeBoxRangeInInlineDirection(InlineBox* firstChild,
InlineBox* lastChild,
LayoutUnit& logicalLeft,
LayoutUnit& minLogicalLeft,
LayoutUnit& maxLogicalRight,
bool& needsWordSpacing);
void beginPlacingBoxRangesInInlineDirection(LayoutUnit logicalLeft) {
setLogicalLeft(logicalLeft);
}
void endPlacingBoxRangesInInlineDirection(LayoutUnit logicalLeft,
LayoutUnit logicalRight,
LayoutUnit minLogicalLeft,
LayoutUnit maxLogicalRight) {
setLogicalWidth(logicalRight - logicalLeft);
if (knownToHaveNoOverflow() &&
(minLogicalLeft < logicalLeft || maxLogicalRight > logicalRight))
clearKnownToHaveNoOverflow();
}
void addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow);
void addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow);
void addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow);
void addTextBoxVisualOverflow(InlineTextBox*,
GlyphOverflowAndFallbackFontsMap&,
LayoutRect& logicalVisualOverflow);
void addReplacedChildOverflow(const InlineBox*,
LayoutRect& logicalLayoutOverflow,
LayoutRect& logicalVisualOverflow);
void setLayoutOverflow(const LayoutRect&, const LayoutRect&);
void setVisualOverflow(const LayoutRect&, const LayoutRect&);
void setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow,
const LayoutRect& logicalVisualOverflow,
LayoutUnit lineTop,
LayoutUnit lineBottom);
protected:
std::unique_ptr<SimpleOverflowModel> m_overflow;
bool isInlineFlowBox() const final { return true; }
InlineBox* m_firstChild;
InlineBox* m_lastChild;
InlineFlowBox*
m_prevLineBox; // The previous box that also uses our LayoutObject
InlineFlowBox* m_nextLineBox; // The next box that also uses our LayoutObject
// Maximum logicalTop among all children of an InlineFlowBox. Used to
// calculate the offset for TextUnderlinePositionUnder.
void computeMaxLogicalTop(LayoutUnit& maxLogicalTop) const;
private:
unsigned m_includeLogicalLeftEdge : 1;
unsigned m_includeLogicalRightEdge : 1;
unsigned m_hasTextChildren : 1;
unsigned m_hasTextDescendants : 1;
unsigned m_descendantsHaveSameLineHeightAndBaseline : 1;
protected:
// The following members are only used by RootInlineBox but moved here to keep
// the bits packed.
// Whether or not this line uses alphabetic or ideographic baselines by
// default.
unsigned m_baselineType : 1; // FontBaseline
// If the line contains any ruby runs, then this will be true.
unsigned m_hasAnnotationsBefore : 1;
unsigned m_hasAnnotationsAfter : 1;
unsigned m_lineBreakBidiStatusEor : 5; // WTF::Unicode::Direction
unsigned m_lineBreakBidiStatusLastStrong : 5; // WTF::Unicode::Direction
unsigned m_lineBreakBidiStatusLast : 5; // WTF::Unicode::Direction
unsigned m_isFirstAfterPageBreak : 1;
// End of RootInlineBox-specific members.
#if ENABLE(ASSERT)
private:
unsigned m_hasBadChildList : 1;
#endif
};
DEFINE_INLINE_BOX_TYPE_CASTS(InlineFlowBox);
#if !ENABLE(ASSERT)
inline void InlineFlowBox::checkConsistency() const {}
#endif
inline void InlineFlowBox::setHasBadChildList() {
#if ENABLE(ASSERT)
m_hasBadChildList = true;
#endif
}
} // namespace blink
#endif // InlineFlowBox_h