ladybird/Userland/Libraries/LibWeb/CSS/Display.h
Sam Atkins f3124c492b LibWeb: Add display: math
This is a `<display-inside>` keyword added by the MathML spec, and has
the rough meaning of "display in the default way". It enables the
standard layout rules for each MathML element (and is ignored for
anything that isn't a MathML element).

I believe we'll need an actual MathML formatting context to do the
layout correctly, but we can at least support a couple of elements that
behave the same as HTML ones.
2023-09-11 17:03:22 +01:00

218 lines
7.6 KiB
C++

/*
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Assertions.h>
#include <AK/String.h>
#include <LibWeb/CSS/Enums.h>
namespace Web::CSS {
class Display {
public:
Display() = default;
~Display() = default;
String to_string() const;
bool operator==(Display const& other) const
{
if (m_type != other.m_type)
return false;
switch (m_type) {
case Type::Box:
return m_value.box == other.m_value.box;
case Type::Internal:
return m_value.internal == other.m_value.internal;
case Type::OutsideAndInside:
return m_value.outside_inside.outside == other.m_value.outside_inside.outside
&& m_value.outside_inside.inside == other.m_value.outside_inside.inside
&& m_value.outside_inside.list_item == other.m_value.outside_inside.list_item;
}
VERIFY_NOT_REACHED();
}
enum class ListItem {
No,
Yes,
};
enum class Type {
OutsideAndInside,
Internal,
Box,
};
bool is_internal() const { return m_type == Type::Internal; }
DisplayInternal internal() const
{
VERIFY(is_internal());
return m_value.internal;
}
bool is_table_column() const { return is_internal() && internal() == DisplayInternal::TableColumn; }
bool is_table_row_group() const { return is_internal() && internal() == DisplayInternal::TableRowGroup; }
bool is_table_header_group() const { return is_internal() && internal() == DisplayInternal::TableHeaderGroup; }
bool is_table_footer_group() const { return is_internal() && internal() == DisplayInternal::TableFooterGroup; }
bool is_table_row() const { return is_internal() && internal() == DisplayInternal::TableRow; }
bool is_table_cell() const { return is_internal() && internal() == DisplayInternal::TableCell; }
bool is_table_column_group() const { return is_internal() && internal() == DisplayInternal::TableColumnGroup; }
bool is_table_caption() const { return is_internal() && internal() == DisplayInternal::TableCaption; }
bool is_none() const { return m_type == Type::Box && m_value.box == DisplayBox::None; }
bool is_contents() const { return m_type == Type::Box && m_value.box == DisplayBox::Contents; }
Type type() const { return m_type; }
bool is_outside_and_inside() const { return m_type == Type::OutsideAndInside; }
DisplayOutside outside() const
{
VERIFY(is_outside_and_inside());
return m_value.outside_inside.outside;
}
bool is_block_outside() const { return is_outside_and_inside() && outside() == DisplayOutside::Block; }
bool is_inline_outside() const { return is_outside_and_inside() && outside() == DisplayOutside::Inline; }
bool is_inline_block() const { return is_inline_outside() && is_flow_root_inside(); }
ListItem list_item() const
{
VERIFY(is_outside_and_inside());
return m_value.outside_inside.list_item;
}
bool is_list_item() const { return is_outside_and_inside() && list_item() == ListItem::Yes; }
DisplayInside inside() const
{
VERIFY(is_outside_and_inside());
return m_value.outside_inside.inside;
}
bool is_flow_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Flow; }
bool is_flow_root_inside() const { return is_outside_and_inside() && inside() == DisplayInside::FlowRoot; }
bool is_table_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Table; }
bool is_flex_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Flex; }
bool is_grid_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Grid; }
bool is_ruby_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Ruby; }
bool is_math_inside() const { return is_outside_and_inside() && inside() == DisplayInside::Math; }
enum class Short {
None,
Contents,
Block,
Flow,
FlowRoot,
Inline,
InlineBlock,
RunIn,
ListItem,
InlineListItem,
Flex,
InlineFlex,
Grid,
InlineGrid,
Ruby,
Table,
InlineTable,
Math,
};
static Display from_short(Short short_)
{
switch (short_) {
case Short::None:
return Display { DisplayBox::None };
case Short::Contents:
return Display { DisplayBox::Contents };
case Short::Block:
return Display { DisplayOutside::Block, DisplayInside::Flow };
case Short::Inline:
return Display { DisplayOutside::Inline, DisplayInside::Flow };
case Short::Flow:
return Display { DisplayOutside::Block, DisplayInside::Flow };
case Short::FlowRoot:
return Display { DisplayOutside::Block, DisplayInside::FlowRoot };
case Short::InlineBlock:
return Display { DisplayOutside::Inline, DisplayInside::FlowRoot };
case Short::RunIn:
return Display { DisplayOutside::RunIn, DisplayInside::Flow };
case Short::ListItem:
return Display { DisplayOutside::Block, DisplayInside::Flow, ListItem::Yes };
case Short::InlineListItem:
return Display { DisplayOutside::Inline, DisplayInside::Flow, ListItem::Yes };
case Short::Flex:
return Display { DisplayOutside::Block, DisplayInside::Flex };
case Short::InlineFlex:
return Display { DisplayOutside::Inline, DisplayInside::Flex };
case Short::Grid:
return Display { DisplayOutside::Block, DisplayInside::Grid };
case Short::InlineGrid:
return Display { DisplayOutside::Inline, DisplayInside::Grid };
case Short::Ruby:
return Display { DisplayOutside::Inline, DisplayInside::Ruby };
case Short::Table:
return Display { DisplayOutside::Block, DisplayInside::Table };
case Short::InlineTable:
return Display { DisplayOutside::Inline, DisplayInside::Table };
case Short::Math:
// NOTE: The spec ( https://w3c.github.io/mathml-core/#new-display-math-value ) does not
// mention what the outside value for `display: math` should be.
// The UA stylesheet does `* { display: block math; }` so let's go with that.
return Display { DisplayOutside::Block, DisplayInside::Math };
}
VERIFY_NOT_REACHED();
}
Display(DisplayOutside outside, DisplayInside inside)
: m_type(Type::OutsideAndInside)
{
m_value.outside_inside = {
.outside = outside,
.inside = inside,
.list_item = ListItem::No,
};
}
Display(DisplayOutside outside, DisplayInside inside, ListItem list_item)
: m_type(Type::OutsideAndInside)
{
m_value.outside_inside = {
.outside = outside,
.inside = inside,
.list_item = list_item,
};
}
explicit Display(DisplayInternal internal)
: m_type(Type::Internal)
{
m_value.internal = internal;
}
explicit Display(DisplayBox box)
: m_type(Type::Box)
{
m_value.box = box;
}
private:
Type m_type {};
union {
struct {
DisplayOutside outside;
DisplayInside inside;
ListItem list_item;
} outside_inside;
DisplayInternal internal;
DisplayBox box;
} m_value {};
};
}