blob: 65db5899a9aa7a1091b25a0701b378cc286148d2 [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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.
*/
#include "config.h"
#include "LegacyInlineFlowBox.h"
#include "CSSPropertyNames.h"
#include "Document.h"
#include "FontCascade.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "InlineBoxPainter.h"
#include "LegacyInlineTextBox.h"
#include "LegacyRootInlineBox.h"
#include "RenderBlock.h"
#include "RenderBoxInlines.h"
#include "RenderElementInlines.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderObjectInlines.h"
#include "RenderStyle+GettersInlines.h"
#include "RenderTableCell.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "Settings.h"
#include "Text.h"
#include "TextBoxPainter.h"
#include <math.h>
#include <wtf/TZoneMallocInlines.h>
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(LegacyInlineFlowBox);
struct SameSizeAsLegacyInlineFlowBox : public LegacyInlineBox {
uint32_t bitfields : 23;
void* pointers[5];
};
static_assert(sizeof(LegacyInlineFlowBox) == sizeof(SameSizeAsLegacyInlineFlowBox), "LegacyInlineFlowBox should stay small");
#if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
LegacyInlineFlowBox::~LegacyInlineFlowBox()
{
setHasBadChildList();
}
void LegacyInlineFlowBox::setHasBadChildList()
{
assertNotDeleted();
if (m_hasBadChildList)
return;
for (auto* child = firstChild(); child; child = child->nextOnLine())
child->setHasBadParent();
m_hasBadChildList = true;
}
#endif
static void setHasTextDescendantsOnAncestors(LegacyInlineFlowBox* box)
{
while (box && !box->hasTextDescendants()) {
box->setHasTextDescendants();
box = box->parent();
}
}
void LegacyInlineFlowBox::addToLine(LegacyInlineBox* child)
{
ASSERT(!child->parent());
ASSERT(!child->nextOnLine());
ASSERT(!child->previousOnLine());
checkConsistency();
child->setParent(this);
if (!m_firstChild) {
m_firstChild = child;
m_lastChild = child;
} else {
m_lastChild->setNextOnLine(child);
child->setPreviousOnLine(m_lastChild);
m_lastChild = child;
}
child->setIsFirstLine(isFirstLine());
child->setIsHorizontal(isHorizontal());
if (child->isInlineTextBox()) {
if (child->renderer().parent() == &renderer())
m_hasTextChildren = true;
setHasTextDescendantsOnAncestors(this);
} else if (auto* blockFlow = dynamicDowncast<LegacyInlineFlowBox>(*child)) {
if (blockFlow->hasTextDescendants())
setHasTextDescendantsOnAncestors(this);
}
const RenderStyle& childStyle = child->lineStyle();
if (child->isInlineTextBox()) {
const RenderStyle* childStyle = &child->lineStyle();
bool hasMarkers = false;
if (auto* textBox = dynamicDowncast<LegacyInlineTextBox>(*child))
hasMarkers = textBox->hasMarkers();
if (childStyle->usedLetterSpacing() < 0 || childStyle->hasTextShadow() || !childStyle->textEmphasisStyle().isNone() || childStyle->hasPositiveStrokeWidth() || hasMarkers || !childStyle->textUnderlineOffset().isAuto() || !childStyle->textDecorationThickness().isAuto() || !childStyle->textUnderlinePosition().isAuto())
child->clearKnownToHaveNoOverflow();
} else if (child->boxModelObject()->hasSelfPaintingLayer())
child->clearKnownToHaveNoOverflow();
else if (childStyle.hasOutlineInVisualOverflow())
child->clearKnownToHaveNoOverflow();
if (lineStyle().hasOutlineInVisualOverflow())
clearKnownToHaveNoOverflow();
if (knownToHaveNoOverflow()) {
auto* flowBox = dynamicDowncast<LegacyInlineFlowBox>(*child);
if (flowBox && !flowBox->knownToHaveNoOverflow())
clearKnownToHaveNoOverflow();
}
if (auto* renderInline = dynamicDowncast<RenderInline>(child->renderer()); renderInline && renderInline->hasSelfPaintingLayer())
m_hasSelfPaintInlineBox = true;
checkConsistency();
}
void LegacyInlineFlowBox::removeChild(LegacyInlineBox* child)
{
checkConsistency();
if (!isDirty())
dirtyLineBoxes();
if (child == m_firstChild)
m_firstChild = child->nextOnLine();
if (child == m_lastChild)
m_lastChild = child->previousOnLine();
if (child->nextOnLine())
child->nextOnLine()->setPreviousOnLine(child->previousOnLine());
if (child->previousOnLine())
child->previousOnLine()->setNextOnLine(child->nextOnLine());
child->setParent(nullptr);
checkConsistency();
}
void LegacyInlineFlowBox::deleteLine()
{
LegacyInlineBox* child = firstChild();
LegacyInlineBox* next = nullptr;
while (child) {
ASSERT(this == child->parent());
next = child->nextOnLine();
#ifndef NDEBUG
child->setParent(nullptr);
#endif
child->deleteLine();
child = next;
}
#ifndef NDEBUG
m_firstChild = nullptr;
m_lastChild = nullptr;
#endif
removeLineBoxFromRenderObject();
delete this;
}
void LegacyInlineFlowBox::removeLineBoxFromRenderObject()
{
downcast<RenderInline>(renderer()).legacyLineBoxes().removeLineBox(this);
}
void LegacyInlineFlowBox::adjustPosition(float dx, float dy)
{
LegacyInlineBox::adjustPosition(dx, dy);
for (auto* child = firstChild(); child; child = child->nextOnLine())
child->adjustPosition(dx, dy);
if (m_overflow)
m_overflow->move(LayoutUnit(dx), LayoutUnit(dy));
}
inline void LegacyInlineFlowBox::addTextBoxVisualOverflow(LegacyInlineTextBox& textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, LayoutRect& logicalVisualOverflow)
{
if (textBox.knownToHaveNoOverflow())
return;
const auto& lineStyle = this->lineStyle();
auto writingMode = lineStyle.writingMode();
GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(&textBox);
GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? nullptr : &it->value.second;
bool isFlippedLine = writingMode.isLineInverted();
auto topGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->bottom : glyphOverflow->top) : 0_lu;
auto bottomGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->top : glyphOverflow->bottom) : 0_lu;
auto leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0_lu;
auto rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0_lu;
auto viewportSize = textBox.renderer().frame().view() ? textBox.renderer().frame().view()->size() : IntSize();
LayoutUnit strokeOverflow(std::ceil(lineStyle.usedStrokeWidth(viewportSize) / 2.0f));
auto topGlyphOverflow = -strokeOverflow - topGlyphEdge;
auto bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge;
auto leftGlyphOverflow = -strokeOverflow - leftGlyphEdge;
auto rightGlyphOverflow = strokeOverflow + rightGlyphEdge;
// If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is
// applied to the right, so this is not an issue with left overflow.
rightGlyphOverflow -= std::min(0, (int)lineStyle.fontCascade().letterSpacing());
auto [textShadowLogicalTop, textShadowLogicalBottom] = Style::shadowBlockDirectionExtent(lineStyle.textShadow(), writingMode, lineStyle.usedZoomForLength());
auto childOverflowLogicalTop = std::min<LayoutUnit>(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow);
auto childOverflowLogicalBottom = std::max<LayoutUnit>(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow);
auto [textShadowLogicalLeft, textShadowLogicalRight] = Style::shadowInlineDirectionExtent(lineStyle.textShadow(), writingMode, lineStyle.usedZoomForLength());
auto childOverflowLogicalLeft = std::min<LayoutUnit>(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow);
auto childOverflowLogicalRight = std::max<LayoutUnit>(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow);
auto logicalTopVisualOverflow = std::min(LayoutUnit(textBox.logicalTop() + childOverflowLogicalTop), logicalVisualOverflow.y());
auto logicalBottomVisualOverflow = std::max(LayoutUnit(textBox.logicalBottom() + childOverflowLogicalBottom), logicalVisualOverflow.maxY());
auto logicalLeftVisualOverflow = std::min(LayoutUnit(textBox.logicalLeft() + childOverflowLogicalLeft), logicalVisualOverflow.x());
auto logicalRightVisualOverflow = std::max(LayoutUnit(textBox.logicalRight() + childOverflowLogicalRight), logicalVisualOverflow.maxX());
logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow);
textBox.setLogicalOverflowRect(logicalVisualOverflow);
}
void LegacyInlineFlowBox::computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap& textBoxDataMap)
{
// If we know we have no overflow, we can just bail.
if (knownToHaveNoOverflow())
return;
m_overflow = { };
// Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored.
// Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in
// transforms, relative positioning, etc.
LayoutRect logicalVisualOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom)));
for (auto* child = firstChild(); child; child = child->nextOnLine()) {
if (is<RenderText>(child->renderer())) {
auto& textBox = downcast<LegacyInlineTextBox>(*child);
LayoutRect textBoxOverflow(enclosingLayoutRect(textBox.logicalFrameRect()));
addTextBoxVisualOverflow(textBox, textBoxDataMap, textBoxOverflow);
logicalVisualOverflow.unite(textBoxOverflow);
} else if (is<RenderInline>(child->renderer())) {
auto& flow = downcast<LegacyInlineFlowBox>(*child);
flow.computeOverflow(lineTop, lineBottom, textBoxDataMap);
if (!flow.renderer().hasSelfPaintingLayer())
logicalVisualOverflow.unite(flow.logicalVisualOverflowRect(lineTop, lineBottom));
}
}
setOverflowFromLogicalRects(logicalVisualOverflow, lineTop, lineBottom);
}
void LegacyInlineFlowBox::setVisualOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom)
{
LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom));
if (frameBox.contains(rect) || rect.isEmpty())
return;
if (!m_overflow)
m_overflow = makeUnique<RenderOverflow>(frameBox, frameBox, frameBox);
m_overflow->setVisualOverflow(rect);
}
void LegacyInlineFlowBox::setOverflowFromLogicalRects(const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom)
{
LayoutRect visualOverflow(isHorizontal() ? logicalVisualOverflow : logicalVisualOverflow.transposedRect());
setVisualOverflow(visualOverflow, lineTop, lineBottom);
}
LegacyInlineBox* LegacyInlineFlowBox::firstLeafDescendant() const
{
LegacyInlineBox* leaf = nullptr;
for (auto* child = firstChild(); child && !leaf; child = child->nextOnLine())
leaf = child->isLeaf() ? child : downcast<LegacyInlineFlowBox>(*child).firstLeafDescendant();
return leaf;
}
LegacyInlineBox* LegacyInlineFlowBox::lastLeafDescendant() const
{
LegacyInlineBox* leaf = nullptr;
for (auto* child = lastChild(); child && !leaf; child = child->previousOnLine())
leaf = child->isLeaf() ? child : downcast<LegacyInlineFlowBox>(*child).lastLeafDescendant();
return leaf;
}
RenderObject::HighlightState LegacyInlineFlowBox::selectionState() const
{
return RenderObject::HighlightState::None;
}
#if ENABLE(TREE_DEBUGGING)
ASCIILiteral LegacyInlineFlowBox::boxName() const
{
return "InlineFlowBox"_s;
}
void LegacyInlineFlowBox::outputLineTreeAndMark(WTF::TextStream& stream, const LegacyInlineBox* markedBox, int depth) const
{
LegacyInlineBox::outputLineTreeAndMark(stream, markedBox, depth);
for (const LegacyInlineBox* box = firstChild(); box; box = box->nextOnLine())
box->outputLineTreeAndMark(stream, markedBox, depth + 1);
}
#endif
#ifndef NDEBUG
void LegacyInlineFlowBox::checkConsistency() const
{
assertNotDeleted();
ASSERT_WITH_SECURITY_IMPLICATION(!m_hasBadChildList);
#ifdef CHECK_CONSISTENCY
const LegacyInlineBox* previousChild = nullptr;
for (const LegacyInlineBox* child = firstChild(); child; child = child->nextOnLine()) {
ASSERT(child->parent() == this);
ASSERT(child->previousOnLine() == previousChild);
previousChild = child;
}
ASSERT(previousChild == m_lastChild);
#endif
}
#endif
} // namespace WebCore