diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 4724cac0f3..3a1f8ed6cb 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -78,6 +78,7 @@ set(SOURCES CSS/StyleValues/ColorStyleValue.cpp CSS/StyleValues/ConicGradientStyleValue.cpp CSS/StyleValues/ContentStyleValue.cpp + CSS/StyleValues/DisplayStyleValue.cpp CSS/StyleValues/EdgeStyleValue.cpp CSS/StyleValues/FilterValueListStyleValue.cpp CSS/StyleValues/FlexFlowStyleValue.cpp diff --git a/Userland/Libraries/LibWeb/CSS/Display.h b/Userland/Libraries/LibWeb/CSS/Display.h index ff65f75778..4d94fedd84 100644 --- a/Userland/Libraries/LibWeb/CSS/Display.h +++ b/Userland/Libraries/LibWeb/CSS/Display.h @@ -138,6 +138,7 @@ public: None, Contents, Block, + Flow, FlowRoot, Inline, InlineBlock, @@ -149,7 +150,6 @@ public: Grid, InlineGrid, Ruby, - BlockRuby, Table, InlineTable, }; @@ -163,10 +163,12 @@ public: return Display { Box::Contents }; case Short::Block: return Display { Outside::Block, Inside::Flow }; - case Short::FlowRoot: - return Display { Outside::Block, Inside::FlowRoot }; case Short::Inline: return Display { Outside::Inline, Inside::Flow }; + case Short::Flow: + return Display { Outside::Block, Inside::Flow }; + case Short::FlowRoot: + return Display { Outside::Block, Inside::FlowRoot }; case Short::InlineBlock: return Display { Outside::Inline, Inside::FlowRoot }; case Short::RunIn: @@ -185,8 +187,6 @@ public: return Display { Outside::Inline, Inside::Grid }; case Short::Ruby: return Display { Outside::Inline, Inside::Ruby }; - case Short::BlockRuby: - return Display { Outside::Block, Inside::Ruby }; case Short::Table: return Display { Outside::Block, Inside::Table }; case Short::InlineTable: diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 66e03fdf98..4dceda0c61 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -4941,6 +4942,164 @@ RefPtr Parser::parse_content_value(Vector const& com return ContentStyleValue::create(StyleValueList::create(move(content_values), StyleValueList::Separator::Space), move(alt_text)); } +// https://www.w3.org/TR/css-display-3/#the-display-properties +RefPtr Parser::parse_display_value(Vector const& component_values) +{ + auto parse_inside = [](ValueID identifier) -> Optional { + switch (identifier) { + case ValueID::Flow: + return Display::Inside::Flow; + case ValueID::FlowRoot: + return Display::Inside::FlowRoot; + case ValueID::Table: + return Display::Inside::Table; + case ValueID::Flex: + return Display::Inside::Flex; + case ValueID::Grid: + return Display::Inside::Grid; + case ValueID::Ruby: + return Display::Inside::Ruby; + default: + return {}; + } + }; + auto parse_outside = [](ValueID identifier) -> Optional { + switch (identifier) { + case ValueID::Block: + return Display::Outside::Block; + case ValueID::Inline: + return Display::Outside::Inline; + case ValueID::RunIn: + return Display::Outside::RunIn; + default: + return {}; + } + }; + + auto parse_single_component_display = [&](Vector const& component_values) -> Optional { + if (auto identifier = parse_identifier_value(component_values.first())) { + switch (identifier->to_identifier()) { + + // display-outside + case ValueID::Block: + return Display::from_short(Display::Short::Block); + case ValueID::Inline: + return Display::from_short(Display::Short::Inline); + case ValueID::RunIn: + return Display::from_short(Display::Short::RunIn); + + // display-inside + case ValueID::Flow: + return Display::from_short(Display::Short::Flow); + case ValueID::FlowRoot: + return Display::from_short(Display::Short::FlowRoot); + case ValueID::Table: + return Display::from_short(Display::Short::Table); + case ValueID::Flex: + return Display::from_short(Display::Short::Flex); + case ValueID::Grid: + return Display::from_short(Display::Short::Grid); + case ValueID::Ruby: + return Display::from_short(Display::Short::Ruby); + + // display-listitem + case ValueID::ListItem: + return Display::from_short(Display::Short::ListItem); + + // display-internal + case ValueID::TableRowGroup: + return Display { Display::Internal::TableRowGroup }; + case ValueID::TableHeaderGroup: + return Display { Display::Internal::TableHeaderGroup }; + case ValueID::TableFooterGroup: + return Display { Display::Internal::TableFooterGroup }; + case ValueID::TableRow: + return Display { Display::Internal::TableRow }; + case ValueID::TableCell: + return Display { Display::Internal::TableCell }; + case ValueID::TableColumnGroup: + return Display { Display::Internal::TableColumnGroup }; + case ValueID::TableColumn: + return Display { Display::Internal::TableColumn }; + case ValueID::TableCaption: + return Display { Display::Internal::TableCaption }; + case ValueID::RubyBase: + return Display { Display::Internal::RubyBase }; + case ValueID::RubyText: + return Display { Display::Internal::RubyText }; + case ValueID::RubyBaseContainer: + return Display { Display::Internal::RubyBaseContainer }; + case ValueID::RubyTextContainer: + return Display { Display::Internal::RubyTextContainer }; + + // display-box + case ValueID::Contents: + // FIXME this should be Display::Short::Contents but contents is currently not implemented + return Display::from_short(Display::Short::Flow); + case ValueID::None: + return Display::from_short(Display::Short::None); + + // display-legacy + case ValueID::InlineBlock: + return Display::from_short(Display::Short::InlineBlock); + case ValueID::InlineTable: + return Display::from_short(Display::Short::InlineTable); + case ValueID::InlineFlex: + return Display::from_short(Display::Short::InlineFlex); + case ValueID::InlineGrid: + return Display::from_short(Display::Short::InlineGrid); + + default: + return {}; + } + } + return {}; + }; + + auto parse_multi_component_display = [&](Vector const& component_values) -> Optional { + auto list_item = Display::ListItem::No; + Display::Inside inside = Display::Inside::Flow; + Display::Outside outside = Display::Outside::Block; + + for (size_t i = 0; i < component_values.size(); ++i) { + if (auto value = parse_identifier_value(component_values[i])) { + auto identifier = value->to_identifier(); + if (ValueID::ListItem == identifier) { + list_item = Display::ListItem::Yes; + continue; + } + auto inside_value = parse_inside(identifier); + if (inside_value.has_value()) { + inside = inside_value.value(); + continue; + } + auto outside_value = parse_outside(identifier); + if (outside_value.has_value()) { + outside = outside_value.value(); + } + } + } + + // The spec does not allow any other inside values to be combined with list-item + // ? && [ flow | flow-root ]? && list-item + if (list_item == Display::ListItem::Yes && inside != Display::Inside::Flow && inside != Display::Inside::FlowRoot) + return {}; + + return Display { outside, inside, list_item }; + }; + + Optional display; + if (component_values.size() == 1) + display = parse_single_component_display(component_values); + else + display = parse_multi_component_display(component_values); + + if (display.has_value()) + return DisplayStyleValue::create(display.value()); + + return {}; +} + RefPtr Parser::parse_filter_value_list_value(Vector const& component_values) { if (component_values.size() == 1 && component_values.first().is(Token::Type::Ident)) { @@ -6708,6 +6867,10 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property if (auto parsed_value = parse_content_value(component_values)) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + case PropertyID::Display: + if (auto parsed_value = parse_display_value(component_values)) + return parsed_value.release_nonnull(); + return ParseError::SyntaxError; case PropertyID::Flex: if (auto parsed_value = parse_flex_value(component_values)) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 81d2bd94f3..2ffe43b789 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -302,6 +302,7 @@ private: RefPtr parse_border_radius_value(Vector const&); RefPtr parse_border_radius_shorthand_value(Vector const&); RefPtr parse_content_value(Vector const&); + RefPtr parse_display_value(Vector const&); RefPtr parse_flex_value(Vector const&); RefPtr parse_flex_flow_value(Vector const&); RefPtr parse_font_value(Vector const&); diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 14de8dfdc5..5ffa6f072e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1490,14 +1491,14 @@ void StyleComputer::transform_box_type_if_needed(StyleProperties& style, DOM::El // to do that specifically. For now, we simply check for "inline-flex" and convert // that to "flex". if (display.is_flex_inside()) - style.set_property(CSS::PropertyID::Display, IdentifierStyleValue::create(CSS::ValueID::Flex)); + style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create({ CSS::Display::Outside::Block, CSS::Display::Inside::Flex })); else - style.set_property(CSS::PropertyID::Display, IdentifierStyleValue::create(CSS::ValueID::Block)); + style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create({ CSS::Display::Outside::Block, CSS::Display::Inside::Block })); } break; case BoxTypeTransformation::Inlinify: if (!display.is_inline_outside()) - style.set_property(CSS::PropertyID::Display, IdentifierStyleValue::create(CSS::ValueID::Inline)); + style.set_property(CSS::PropertyID::Display, DisplayStyleValue::create({ CSS::Display::Outside::Inline, CSS::Display::Inside::Flow })); break; } } @@ -1510,7 +1511,7 @@ NonnullRefPtr StyleComputer::create_document_style() const absolutize_values(style, nullptr, {}); style->set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().width()))); style->set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length::make_px(viewport_rect().height()))); - style->set_property(CSS::PropertyID::Display, CSS::IdentifierStyleValue::create(CSS::ValueID::Block)); + style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Block))); return style; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index 41bd3e3f40..2adde0ada0 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -598,51 +599,13 @@ Optional StyleProperties::visibility() const return value_id_to_visibility(value->to_identifier()); } -CSS::Display StyleProperties::display() const +Display StyleProperties::display() const { - auto value = property(CSS::PropertyID::Display); - if (!value->is_identifier()) - return CSS::Display::from_short(CSS::Display::Short::Inline); - switch (value->to_identifier()) { - case CSS::ValueID::None: - return CSS::Display::from_short(CSS::Display::Short::None); - case CSS::ValueID::Block: - return CSS::Display::from_short(CSS::Display::Short::Block); - case CSS::ValueID::Inline: - return CSS::Display::from_short(CSS::Display::Short::Inline); - case CSS::ValueID::InlineBlock: - return CSS::Display::from_short(CSS::Display::Short::InlineBlock); - case CSS::ValueID::ListItem: - return CSS::Display::from_short(CSS::Display::Short::ListItem); - case CSS::ValueID::Table: - return CSS::Display::from_short(CSS::Display::Short::Table); - case CSS::ValueID::InlineTable: - return CSS::Display::from_short(CSS::Display::Short::InlineTable); - case CSS::ValueID::TableRow: - return CSS::Display { CSS::Display::Internal::TableRow }; - case CSS::ValueID::TableCell: - return CSS::Display { CSS::Display::Internal::TableCell }; - case CSS::ValueID::TableColumn: - return CSS::Display { CSS::Display::Internal::TableColumn }; - case CSS::ValueID::TableColumnGroup: - return CSS::Display { CSS::Display::Internal::TableColumnGroup }; - case CSS::ValueID::TableCaption: - return CSS::Display { CSS::Display::Internal::TableCaption }; - case CSS::ValueID::TableRowGroup: - return CSS::Display { CSS::Display::Internal::TableRowGroup }; - case CSS::ValueID::TableHeaderGroup: - return CSS::Display { CSS::Display::Internal::TableHeaderGroup }; - case CSS::ValueID::TableFooterGroup: - return CSS::Display { CSS::Display::Internal::TableFooterGroup }; - case CSS::ValueID::Flex: - return CSS::Display::from_short(CSS::Display::Short::Flex); - case CSS::ValueID::InlineFlex: - return CSS::Display::from_short(CSS::Display::Short::InlineFlex); - case CSS::ValueID::Grid: - return CSS::Display::from_short(CSS::Display::Short::Grid); - default: - return CSS::Display::from_short(CSS::Display::Short::Block); + auto value = property(PropertyID::Display); + if (value->is_display()) { + return value->as_display().display(); } + return Display::from_short(Display::Short::Inline); } Vector StyleProperties::text_decoration_line() const diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 0a1e13d72a..095071e4c7 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -141,6 +142,12 @@ ContentStyleValue const& StyleValue::as_content() const return static_cast(*this); } +DisplayStyleValue const& StyleValue::as_display() const +{ + VERIFY(is_display()); + return static_cast(*this); +} + EdgeStyleValue const& StyleValue::as_edge() const { VERIFY(is_edge()); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 35f26f337a..ebce86421f 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -98,6 +98,7 @@ public: Color, ConicGradient, Content, + Display, Edge, FilterValueList, Flex, @@ -150,6 +151,7 @@ public: bool is_color() const { return type() == Type::Color; } bool is_conic_gradient() const { return type() == Type::ConicGradient; } bool is_content() const { return type() == Type::Content; } + bool is_display() const { return type() == Type::Display; } bool is_edge() const { return type() == Type::Edge; } bool is_filter_value_list() const { return type() == Type::FilterValueList; } bool is_flex() const { return type() == Type::Flex; } @@ -200,6 +202,7 @@ public: ColorStyleValue const& as_color() const; ConicGradientStyleValue const& as_conic_gradient() const; ContentStyleValue const& as_content() const; + DisplayStyleValue const& as_display() const; EdgeStyleValue const& as_edge() const; FilterValueListStyleValue const& as_filter_value_list() const; FlexFlowStyleValue const& as_flex_flow() const; @@ -248,6 +251,7 @@ public: ColorStyleValue& as_color() { return const_cast(const_cast(*this).as_color()); } ConicGradientStyleValue& as_conic_gradient() { return const_cast(const_cast(*this).as_conic_gradient()); } ContentStyleValue& as_content() { return const_cast(const_cast(*this).as_content()); } + DisplayStyleValue& as_display() { return const_cast(const_cast(*this).as_display()); } EdgeStyleValue& as_edge() { return const_cast(const_cast(*this).as_edge()); } FilterValueListStyleValue& as_filter_value_list() { return const_cast(const_cast(*this).as_filter_value_list()); } FlexFlowStyleValue& as_flex_flow() { return const_cast(const_cast(*this).as_flex_flow()); } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.cpp new file mode 100644 index 0000000000..2214855244 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023, Emil Militzer + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "DisplayStyleValue.h" + +namespace Web::CSS { + +ValueComparingNonnullRefPtr DisplayStyleValue::create(Display const& display) +{ + return adopt_ref(*new DisplayStyleValue(display)); +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.h new file mode 100644 index 0000000000..474af6a20c --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/DisplayStyleValue.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023, Emil Militzer + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::CSS { + +class DisplayStyleValue : public StyleValueWithDefaultOperators { +public: + static ValueComparingNonnullRefPtr create(Display const&); + virtual ~DisplayStyleValue() override = default; + + virtual ErrorOr to_string() const override { return m_display.to_string(); } + + Display display() const { return m_display; } + + bool properties_equal(DisplayStyleValue const& other) const { return m_display == other.m_display; } + +private: + explicit DisplayStyleValue(Display const& display) + : StyleValueWithDefaultOperators(Type::Display) + , m_display(display) + { + } + + Display m_display; +}; + +} diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 867bf5ae77..03df51f5a4 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -84,6 +84,7 @@ class CSSStyleRule; class CSSStyleSheet; class CSSSupportsRule; class Display; +class DisplayStyleValue; class EdgeStyleValue; class ElementInlineCSSStyleDeclaration; class ExplicitGridTrack; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 2da21fb220..0e20666d64 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -6,6 +6,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -77,7 +78,7 @@ JS::GCPtr HTMLInputElement::create_layout_node(NonnullRefPtrdisplay().is_inline_outside() && style->display().is_flow_inside()) - style->set_property(CSS::PropertyID::Display, CSS::IdentifierStyleValue::create(CSS::ValueID::InlineBlock)); + style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::InlineBlock))); return Element::create_layout_node_for_display_type(document(), style->display(), style, this); } diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index e041c361bb..88b57ef8c1 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -292,9 +293,9 @@ ErrorOr TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder:: auto& progress = static_cast(dom_node); if (!progress.using_system_appearance()) { auto bar_style = TRY(style_computer.compute_style(progress, CSS::Selector::PseudoElement::ProgressBar)); - bar_style->set_property(CSS::PropertyID::Display, CSS::IdentifierStyleValue::create(CSS::ValueID::InlineBlock)); + bar_style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::FlowRoot))); auto value_style = TRY(style_computer.compute_style(progress, CSS::Selector::PseudoElement::ProgressValue)); - value_style->set_property(CSS::PropertyID::Display, CSS::IdentifierStyleValue::create(CSS::ValueID::Block)); + value_style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Block))); auto position = progress.position(); value_style->set_property(CSS::PropertyID::Width, CSS::PercentageStyleValue::create(CSS::Percentage(position >= 0 ? round_to(100 * position) : 0))); auto bar_display = bar_style->display();