FloatingContext.cpp [plain text]
#include "config.h"
#include "FloatingContext.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "DisplayBox.h"
#include "FloatAvoider.h"
#include "FloatBox.h"
#include "LayoutBox.h"
#include "LayoutContainer.h"
#include "LayoutState.h"
#include <wtf/IsoMallocInlines.h>
namespace WebCore {
namespace Layout {
WTF_MAKE_ISO_ALLOCATED_IMPL(FloatingContext);
class Iterator;
class FloatingPair {
public:
bool isEmpty() const { return !m_leftIndex && !m_rightIndex; }
const FloatingState::FloatItem* left() const;
const FloatingState::FloatItem* right() const;
bool intersects(const Display::Box::Rect&) const;
PositionInContextRoot verticalConstraint() const { return m_verticalPosition; }
FloatAvoider::HorizontalConstraints horizontalConstraints() const;
PositionInContextRoot bottom() const;
bool operator==(const FloatingPair&) const;
private:
friend class Iterator;
FloatingPair(const FloatingState::FloatList&);
const FloatingState::FloatList& m_floats;
Optional<unsigned> m_leftIndex;
Optional<unsigned> m_rightIndex;
PositionInContextRoot m_verticalPosition;
};
class Iterator {
public:
Iterator(const FloatingState::FloatList&, Optional<PositionInContextRoot> verticalPosition);
const FloatingPair& operator*() const { return m_current; }
Iterator& operator++();
bool operator==(const Iterator&) const;
bool operator!=(const Iterator&) const;
private:
void set(PositionInContextRoot verticalPosition);
const FloatingState::FloatList& m_floats;
FloatingPair m_current;
};
static Iterator begin(const FloatingState& floatingState, PositionInContextRoot initialVerticalPosition)
{
return Iterator(floatingState.floats(), initialVerticalPosition);
}
static Iterator end(const FloatingState& floatingState)
{
return Iterator(floatingState.floats(), WTF::nullopt);
}
FloatingContext::FloatingContext(FloatingState& floatingState)
: m_floatingState(floatingState)
{
}
Point FloatingContext::positionForFloat(const Box& layoutBox) const
{
ASSERT(layoutBox.isFloatingPositioned());
if (m_floatingState.isEmpty()) {
auto& displayBox = layoutState().displayBoxForLayoutBox(layoutBox);
auto alignWithContainingBlock = [&]() -> Position {
auto& containingBlockDisplayBox = layoutState().displayBoxForLayoutBox(*layoutBox.containingBlock());
if (layoutBox.isLeftFloatingPositioned())
return Position { containingBlockDisplayBox.contentBoxLeft() + displayBox.marginStart() };
return Position { containingBlockDisplayBox.contentBoxRight() - displayBox.marginEnd() - displayBox.width() };
};
return { alignWithContainingBlock(), displayBox.top() };
}
FloatBox floatBox = { layoutBox, m_floatingState, layoutState() };
floatingPosition(floatBox);
return floatBox.rectInContainingBlock().topLeft();
}
Optional<Point> FloatingContext::positionForFloatAvoiding(const Box& layoutBox) const
{
ASSERT(layoutBox.establishesBlockFormattingContext());
ASSERT(!layoutBox.isFloatingPositioned());
ASSERT(!layoutBox.hasFloatClear());
if (m_floatingState.isEmpty())
return { };
FloatAvoider floatAvoider = { layoutBox, m_floatingState, layoutState() };
floatingPosition(floatAvoider);
return { floatAvoider.rectInContainingBlock().topLeft() };
}
Optional<Position> FloatingContext::verticalPositionWithClearance(const Box& layoutBox) const
{
ASSERT(layoutBox.hasFloatClear());
ASSERT(layoutBox.isBlockLevelBox());
if (m_floatingState.isEmpty())
return { };
auto bottom = [&](Optional<PositionInContextRoot> floatBottom) -> Optional<Position> {
if (!floatBottom)
return { };
auto& layoutState = this->layoutState();
auto& displayBox = layoutState.displayBoxForLayoutBox(layoutBox);
auto rootRelativeTop = FormattingContext::mapTopLeftToAncestor(layoutState, layoutBox, downcast<Container>(m_floatingState.root())).y;
auto clearance = *floatBottom - rootRelativeTop;
if (clearance <= 0)
return { };
if (auto* previousInFlowSibling = layoutBox.previousInFlowSibling()) {
auto& previousInFlowDisplayBox = layoutState.displayBoxForLayoutBox(*previousInFlowSibling);
ASSERT(!previousInFlowDisplayBox.marginAfter());
auto collapsedMargin = displayBox.marginBefore();
auto previousVerticalMargin = previousInFlowDisplayBox.verticalMargin();
if (previousVerticalMargin.collapsedValues().after) {
previousVerticalMargin.setCollapsedValues({ previousVerticalMargin.collapsedValues().before, { } });
previousInFlowDisplayBox.setVerticalMargin(previousVerticalMargin);
}
auto verticalMargin = displayBox.verticalMargin();
if (verticalMargin.collapsedValues().before) {
verticalMargin.setCollapsedValues({ { }, verticalMargin.collapsedValues().after });
displayBox.setVerticalMargin(verticalMargin);
}
auto nonCollapsedMargin = previousInFlowDisplayBox.marginAfter() + displayBox.marginBefore();
auto marginOffset = nonCollapsedMargin - collapsedMargin;
rootRelativeTop += marginOffset;
clearance -= marginOffset;
}
rootRelativeTop += clearance;
ASSERT(*floatBottom == rootRelativeTop);
auto containingBlockRootRelativeTop = FormattingContext::mapTopLeftToAncestor(layoutState, *layoutBox.containingBlock(), downcast<Container>(m_floatingState.root())).y;
return Position { rootRelativeTop - containingBlockRootRelativeTop };
};
auto clear = layoutBox.style().clear();
auto& formattingContextRoot = layoutBox.formattingContextRoot();
if (clear == Clear::Left)
return bottom(m_floatingState.leftBottom(formattingContextRoot));
if (clear == Clear::Right)
return bottom(m_floatingState.rightBottom(formattingContextRoot));
if (clear == Clear::Both)
return bottom(m_floatingState.bottom(formattingContextRoot));
ASSERT_NOT_REACHED();
return { };
}
void FloatingContext::floatingPosition(FloatAvoider& floatAvoider) const
{
floatAvoider.resetPosition();
Optional<PositionInContextRoot> bottomMost;
auto end = Layout::end(m_floatingState);
for (auto iterator = begin(m_floatingState, { floatAvoider.rect().top() }); iterator != end; ++iterator) {
ASSERT(!(*iterator).isEmpty());
auto floats = *iterator;
floatAvoider.setHorizontalConstraints(floats.horizontalConstraints());
floatAvoider.setVerticalConstraint(floats.verticalConstraint());
if (!floatAvoider.overflowsContainingBlock() && !floats.intersects(floatAvoider.rect()))
return;
bottomMost = floats.bottom();
}
if (!bottomMost)
return;
floatAvoider.setVerticalConstraint(*bottomMost);
floatAvoider.setHorizontalConstraints({ });
}
FloatingPair::FloatingPair(const FloatingState::FloatList& floats)
: m_floats(floats)
{
}
const FloatingState::FloatItem* FloatingPair::left() const
{
if (!m_leftIndex)
return nullptr;
ASSERT(m_floats[*m_leftIndex].isLeftPositioned());
return &m_floats[*m_leftIndex];
}
const FloatingState::FloatItem* FloatingPair::right() const
{
if (!m_rightIndex)
return nullptr;
ASSERT(!m_floats[*m_rightIndex].isLeftPositioned());
return &m_floats[*m_rightIndex];
}
bool FloatingPair::intersects(const Display::Box::Rect& candidateRect) const
{
auto intersects = [&](const FloatingState::FloatItem* floating, Float floatingType) {
if (!floating)
return false;
auto marginRect = floating->rectWithMargin();
if ((floatingType == Float::Left && candidateRect.left() < marginRect.right())
|| (floatingType == Float::Right && candidateRect.right() > marginRect.left()))
return true;
return marginRect.intersects(candidateRect);
};
if (!m_leftIndex && !m_rightIndex) {
ASSERT_NOT_REACHED();
return false;
}
if (intersects(left(), Float::Left))
return true;
if (intersects(right(), Float::Right))
return true;
return false;
}
bool FloatingPair::operator ==(const FloatingPair& other) const
{
return m_leftIndex == other.m_leftIndex && m_rightIndex == other.m_rightIndex;
}
FloatAvoider::HorizontalConstraints FloatingPair::horizontalConstraints() const
{
Optional<PositionInContextRoot> leftEdge;
Optional<PositionInContextRoot> rightEdge;
if (left())
leftEdge = PositionInContextRoot { left()->rectWithMargin().right() };
if (right())
rightEdge = PositionInContextRoot { right()->rectWithMargin().left() };
return { leftEdge, rightEdge };
}
PositionInContextRoot FloatingPair::bottom() const
{
auto* left = this->left();
auto* right = this->right();
ASSERT(left || right);
auto leftBottom = left ? Optional<PositionInContextRoot>(PositionInContextRoot { left->rectWithMargin().bottom() }) : WTF::nullopt;
auto rightBottom = right ? Optional<PositionInContextRoot>(PositionInContextRoot { right->rectWithMargin().bottom() }) : WTF::nullopt;
if (leftBottom && rightBottom)
return std::max(*leftBottom, *rightBottom);
if (leftBottom)
return *leftBottom;
return *rightBottom;
}
Iterator::Iterator(const FloatingState::FloatList& floats, Optional<PositionInContextRoot> verticalPosition)
: m_floats(floats)
, m_current(floats)
{
if (verticalPosition)
set(*verticalPosition);
}
inline static Optional<unsigned> previousFloatingIndex(Float floatingType, const FloatingState::FloatList& floats, unsigned currentIndex)
{
RELEASE_ASSERT(currentIndex <= floats.size());
while (currentIndex) {
auto& floating = floats[--currentIndex];
if ((floatingType == Float::Left && floating.isLeftPositioned()) || (floatingType == Float::Right && !floating.isLeftPositioned()))
return currentIndex;
}
return { };
}
Iterator& Iterator::operator++()
{
if (m_current.isEmpty()) {
ASSERT_NOT_REACHED();
return *this;
}
auto findPreviousFloatingWithLowerBottom = [&](Float floatingType, unsigned currentIndex) -> Optional<unsigned> {
RELEASE_ASSERT(currentIndex < m_floats.size());
if (!currentIndex)
return { };
auto currentBottom = m_floats[currentIndex].rectWithMargin().bottom();
Optional<unsigned> index = currentIndex;
while (true) {
index = previousFloatingIndex(floatingType, m_floats, *index);
if (!index)
return { };
if (m_floats[*index].rectWithMargin().bottom() > currentBottom)
return index;
}
ASSERT_NOT_REACHED();
return { };
};
auto leftBottom = m_current.left() ? Optional<PositionInContextRoot>(m_current.left()->bottom()) : WTF::nullopt;
auto rightBottom = m_current.right() ? Optional<PositionInContextRoot>(m_current.right()->bottom()) : WTF::nullopt;
auto updateLeft = (leftBottom == rightBottom) || (!rightBottom || (leftBottom && leftBottom < rightBottom));
auto updateRight = (leftBottom == rightBottom) || (!leftBottom || (rightBottom && leftBottom > rightBottom));
if (updateLeft) {
ASSERT(m_current.m_leftIndex);
m_current.m_verticalPosition = *leftBottom;
m_current.m_leftIndex = findPreviousFloatingWithLowerBottom(Float::Left, *m_current.m_leftIndex);
}
if (updateRight) {
ASSERT(m_current.m_rightIndex);
m_current.m_verticalPosition = *rightBottom;
m_current.m_rightIndex = findPreviousFloatingWithLowerBottom(Float::Right, *m_current.m_rightIndex);
}
return *this;
}
void Iterator::set(PositionInContextRoot verticalPosition)
{
m_current.m_verticalPosition = verticalPosition;
if (m_floats.isEmpty()) {
ASSERT_NOT_REACHED();
m_current.m_leftIndex = { };
m_current.m_rightIndex = { };
return;
}
auto findFloatingBelow = [&](Float floatingType) -> Optional<unsigned> {
ASSERT(!m_floats.isEmpty());
auto index = floatingType == Float::Left ? m_current.m_leftIndex : m_current.m_rightIndex;
index = index.valueOr(m_floats.size());
while (true) {
index = previousFloatingIndex(floatingType, m_floats, *index);
if (!index)
return { };
auto bottom = m_floats[*index].rectWithMargin().bottom();
if (bottom > verticalPosition)
return index;
}
return { };
};
m_current.m_leftIndex = findFloatingBelow(Float::Left);
m_current.m_rightIndex = findFloatingBelow(Float::Right);
ASSERT(!m_current.m_leftIndex || (*m_current.m_leftIndex < m_floats.size() && m_floats[*m_current.m_leftIndex].isLeftPositioned()));
ASSERT(!m_current.m_rightIndex || (*m_current.m_rightIndex < m_floats.size() && !m_floats[*m_current.m_rightIndex].isLeftPositioned()));
}
bool Iterator::operator==(const Iterator& other) const
{
return m_current == other.m_current;
}
bool Iterator::operator!=(const Iterator& other) const
{
return !(*this == other);
}
}
}
#endif