#include "config.h"
#include "LayoutBox.h"
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "DisplayBox.h"
#include "LayoutContainerBox.h"
#include "LayoutInitialContainingBlock.h"
#include "LayoutPhase.h"
#include "LayoutState.h"
#include "RenderStyle.h"
#include <wtf/IsoMallocInlines.h>
namespace WebCore {
namespace Layout {
WTF_MAKE_ISO_ALLOCATED_IMPL(Box);
Box::Box(Optional<ElementAttributes> attributes, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
: m_style(WTFMove(style))
, m_elementAttributes(attributes)
, m_baseTypeFlags(baseTypeFlags)
, m_hasRareData(false)
, m_isAnonymous(false)
{
}
Box::~Box()
{
if (UNLIKELY(m_hasRareData))
removeRareData();
}
void Box::updateStyle(const RenderStyle& newStyle)
{
m_style = RenderStyle::clone(newStyle);
}
bool Box::establishesFormattingContext() const
{
ASSERT(!Phase::isInTreeBuilding());
return establishesBlockFormattingContext() || establishesInlineFormattingContext() || establishesTableFormattingContext() || establishesIndependentFormattingContext();
}
bool Box::establishesBlockFormattingContext() const
{
if (is<InitialContainingBlock>(*this))
return true;
if (isTableWrapperBox())
return true;
if (isFloatingPositioned() || isAbsolutelyPositioned()) {
return style().display() == DisplayType::Block;
}
if (isBlockContainerBox() && !isBlockLevelBox())
return true;
if (isBlockLevelBox() && !isOverflowVisible())
return true;
return false;
}
bool Box::establishesInlineFormattingContext() const
{
if (!isBlockContainerBox())
return false;
if (!isContainerBox())
return false;
if (!downcast<ContainerBox>(*this).firstInFlowChild())
return false;
return downcast<ContainerBox>(*this).firstInFlowChild()->isInlineLevelBox();
}
bool Box::establishesTableFormattingContext() const
{
return isTableBox();
}
bool Box::establishesIndependentFormattingContext() const
{
return isAbsolutelyPositioned();
}
bool Box::isRelativelyPositioned() const
{
return m_style.position() == PositionType::Relative;
}
bool Box::isStickyPositioned() const
{
return m_style.position() == PositionType::Sticky;
}
bool Box::isAbsolutelyPositioned() const
{
return m_style.position() == PositionType::Absolute || isFixedPositioned();
}
bool Box::isFixedPositioned() const
{
return m_style.position() == PositionType::Fixed;
}
bool Box::isFloatingPositioned() const
{
if (isOutOfFlowPositioned())
return false;
return m_style.floating() != Float::No;
}
bool Box::isLeftFloatingPositioned() const
{
if (!isFloatingPositioned())
return false;
return m_style.floating() == Float::Left;
}
bool Box::isRightFloatingPositioned() const
{
if (!isFloatingPositioned())
return false;
return m_style.floating() == Float::Right;
}
bool Box::hasFloatClear() const
{
return m_style.clear() != Clear::None;
}
bool Box::isFloatAvoider() const
{
if (isFloatingPositioned() || hasFloatClear())
return true;
return establishesTableFormattingContext() || establishesIndependentFormattingContext() || establishesBlockFormattingContext();
}
const ContainerBox& Box::containingBlock() const
{
ASSERT(!Phase::isInTreeBuilding());
RELEASE_ASSERT(!is<InitialContainingBlock>(*this));
if (!isPositioned() || isInFlowPositioned()) {
auto* ancestor = &parent();
for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
if (ancestor->isBlockContainerBox() || ancestor->establishesFormattingContext())
return *ancestor;
}
return *ancestor;
}
if (isFixedPositioned()) {
auto* ancestor = &parent();
for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
if (ancestor->style().hasTransform())
return *ancestor;
}
return *ancestor;
}
if (isOutOfFlowPositioned()) {
auto* ancestor = &parent();
for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) {
if (ancestor->isPositioned() || ancestor->style().hasTransform())
return *ancestor;
}
return *ancestor;
}
ASSERT_NOT_REACHED();
return initialContainingBlock();
}
const ContainerBox& Box::formattingContextRoot() const
{
ASSERT(!Phase::isInTreeBuilding());
ASSERT(!is<InitialContainingBlock>(*this));
auto& ancestor = isInlineLevelBox() && isInFlowPositioned() ? parent() : containingBlock();
if (ancestor.establishesFormattingContext())
return ancestor;
return ancestor.formattingContextRoot();
}
const InitialContainingBlock& Box::initialContainingBlock() const
{
if (is<InitialContainingBlock>(*this))
return downcast<InitialContainingBlock>(*this);
auto* ancestor = &parent();
for (; !is<InitialContainingBlock>(*ancestor); ancestor = &ancestor->parent()) { }
return downcast<InitialContainingBlock>(*ancestor);
}
bool Box::isInFormattingContextOf(const ContainerBox& formattingContextRoot) const
{
ASSERT(formattingContextRoot.establishesFormattingContext());
ASSERT(!is<InitialContainingBlock>(*this));
auto* ancestor = &containingBlock();
while (ancestor) {
if (ancestor == &formattingContextRoot)
return true;
if (is<InitialContainingBlock>(*ancestor))
return false;
ancestor = &ancestor->containingBlock();
}
ASSERT_NOT_REACHED();
return false;
}
bool Box::isInlineBlockBox() const
{
return m_style.display() == DisplayType::InlineBlock;
}
bool Box::isInlineTableBox() const
{
return m_style.display() == DisplayType::InlineTable;
}
bool Box::isBlockLevelBox() const
{
auto display = m_style.display();
return display == DisplayType::Block || display == DisplayType::ListItem || display == DisplayType::Table;
}
bool Box::isInlineLevelBox() const
{
auto display = m_style.display();
return display == DisplayType::Inline || isInlineBlockBox() || isInlineTableBox();
}
bool Box::isInlineBox() const
{
return m_style.display() == DisplayType::Inline && !isReplacedBox();
}
bool Box::isAtomicInlineLevelBox() const
{
return isInlineLevelBox() && !isInlineBox();
}
bool Box::isBlockContainerBox() const
{
auto display = m_style.display();
return display == DisplayType::Block || display == DisplayType::ListItem || isInlineBlockBox() || isTableCell() || isTableCaption(); }
const Box* Box::nextInFlowSibling() const
{
auto* nextSibling = this->nextSibling();
while (nextSibling && !nextSibling->isInFlow())
nextSibling = nextSibling->nextSibling();
return nextSibling;
}
const Box* Box::nextInFlowOrFloatingSibling() const
{
auto* nextSibling = this->nextSibling();
while (nextSibling && !(nextSibling->isInFlow() || nextSibling->isFloatingPositioned()))
nextSibling = nextSibling->nextSibling();
return nextSibling;
}
const Box* Box::previousInFlowSibling() const
{
auto* previousSibling = this->previousSibling();
while (previousSibling && !previousSibling->isInFlow())
previousSibling = previousSibling->previousSibling();
return previousSibling;
}
const Box* Box::previousInFlowOrFloatingSibling() const
{
auto* previousSibling = this->previousSibling();
while (previousSibling && !(previousSibling->isInFlow() || previousSibling->isFloatingPositioned()))
previousSibling = previousSibling->previousSibling();
return previousSibling;
}
bool Box::isOverflowVisible() const
{
auto isOverflowVisible = m_style.overflowX() == Overflow::Visible || m_style.overflowY() == Overflow::Visible;
if (isBodyBox()) {
auto& documentBox = containingBlock();
if (!documentBox.isDocumentBox())
return isOverflowVisible;
if (!documentBox.isOverflowVisible())
return isOverflowVisible;
return true;
}
if (is<InitialContainingBlock>(*this)) {
auto* documentBox = downcast<ContainerBox>(*this).firstChild();
if (!documentBox || !documentBox->isDocumentBox() || !is<ContainerBox>(documentBox))
return isOverflowVisible;
auto* bodyBox = downcast<ContainerBox>(documentBox)->firstChild();
if (!bodyBox || !bodyBox->isBodyBox())
return isOverflowVisible;
auto& bodyBoxStyle = bodyBox->style();
return bodyBoxStyle.overflowX() == Overflow::Visible || bodyBoxStyle.overflowY() == Overflow::Visible;
}
return isOverflowVisible;
}
bool Box::isPaddingApplicable() const
{
if (isAnonymous())
return false;
if (isTableBox() && style().borderCollapse() == BorderCollapse::Collapse) {
return false;
}
return !isTableHeader()
&& !isTableBody()
&& !isTableFooter()
&& !isTableRow()
&& !isTableColumnGroup()
&& !isTableColumn();
}
void Box::setRowSpan(size_t rowSpan)
{
ensureRareData().tableCellSpan.row = rowSpan;
}
void Box::setColumnSpan(size_t columnSpan)
{
ensureRareData().tableCellSpan.column = columnSpan;
}
size_t Box::rowSpan() const
{
if (!hasRareData())
return 1;
return rareData().tableCellSpan.row;
}
size_t Box::columnSpan() const
{
if (!hasRareData())
return 1;
return rareData().tableCellSpan.column;
}
void Box::setColumnWidth(LayoutUnit columnWidth)
{
ensureRareData().columnWidth = columnWidth;
}
Optional<LayoutUnit> Box::columnWidth() const
{
if (!hasRareData())
return { };
return rareData().columnWidth;
}
void Box::setCachedDisplayBoxForLayoutState(LayoutState& layoutState, std::unique_ptr<Display::Box> box) const
{
ASSERT(!m_cachedLayoutState);
m_cachedLayoutState = makeWeakPtr(layoutState);
m_cachedDisplayBoxForLayoutState = WTFMove(box);
}
Box::RareDataMap& Box::rareDataMap()
{
static NeverDestroyed<RareDataMap> map;
return map;
}
const Box::BoxRareData& Box::rareData() const
{
ASSERT(hasRareData());
return *rareDataMap().get(this);
}
Box::BoxRareData& Box::ensureRareData()
{
setHasRareData(true);
return *rareDataMap().ensure(this, [] { return makeUnique<BoxRareData>(); }).iterator->value;
}
void Box::removeRareData()
{
rareDataMap().remove(this);
setHasRareData(false);
}
}
}
#endif