LibWeb: Add layout objects for fieldset and legend

Add the boilerplate code for the layout objects that represent the
`<fieldset>` and `<legend>` elements. Using these, we can make progress
towards laying out these two elements per the spec at
https://html.spec.whatwg.org/multipage/rendering.html#the-fieldset-and-legend-elements.
This commit is contained in:
Kostya Farber 2024-11-05 08:07:13 +00:00 committed by Alexander Kalenik
parent b342758dbf
commit a820308a02
Notes: github-actions[bot] 2024-11-19 13:32:03 +00:00
12 changed files with 173 additions and 0 deletions

View file

@ -527,6 +527,7 @@ set(SOURCES
Layout/BreakNode.cpp
Layout/CanvasBox.cpp
Layout/CheckBox.cpp
Layout/FieldSetBox.cpp
Layout/FlexFormattingContext.cpp
Layout/FormattingContext.cpp
Layout/FrameBox.cpp
@ -540,6 +541,7 @@ set(SOURCES
Layout/Label.cpp
Layout/LabelableNode.cpp
Layout/LayoutState.cpp
Layout/LegendBox.cpp
Layout/LineBox.cpp
Layout/LineBoxFragment.cpp
Layout/LineBuilder.cpp

View file

@ -598,6 +598,7 @@ class BlockFormattingContext;
class Box;
class ButtonBox;
class CheckBox;
class FieldSetBox;
class FlexFormattingContext;
class FormattingContext;
class ImageBox;
@ -605,6 +606,7 @@ class InlineFormattingContext;
class InlineNode;
class Label;
class LabelableNode;
class LegendBox;
class LineBox;
class LineBoxFragment;
class ListItemBox;

View file

@ -14,6 +14,7 @@
#include <LibWeb/HTML/HTMLOutputElement.h>
#include <LibWeb/HTML/HTMLSelectElement.h>
#include <LibWeb/HTML/HTMLTextAreaElement.h>
#include <LibWeb/Layout/FieldSetBox.h>
namespace Web::HTML {
@ -77,4 +78,14 @@ GC::Ptr<DOM::HTMLCollection> const& HTMLFieldSetElement::elements()
return m_elements;
}
Layout::FieldSetBox* HTMLFieldSetElement::layout_node()
{
return static_cast<Layout::FieldSetBox*>(Node::layout_node());
}
JS::GCPtr<Layout::Node> HTMLFieldSetElement::create_layout_node(CSS::StyleProperties style)
{
return heap().allocate<Layout::FieldSetBox>(document(), *this, style);
}
}

View file

@ -42,6 +42,10 @@ public:
virtual Optional<ARIA::Role> default_role() const override { return ARIA::Role::group; }
virtual JS::GCPtr<Layout::Node> create_layout_node(CSS::StyleProperties) override;
Layout::FieldSetBox* layout_node();
Layout::FieldSetBox const* layout_node() const;
private:
HTMLFieldSetElement(DOM::Document&, DOM::QualifiedName);

View file

@ -8,6 +8,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/HTML/HTMLFieldSetElement.h>
#include <LibWeb/HTML/HTMLLegendElement.h>
#include <LibWeb/Layout/LegendBox.h>
namespace Web::HTML {
@ -39,4 +40,19 @@ HTMLFormElement* HTMLLegendElement::form()
return nullptr;
}
JS::GCPtr<Layout::Node> HTMLLegendElement::create_layout_node(CSS::StyleProperties style)
{
return heap().allocate<Layout::LegendBox>(document(), *this, move(style));
}
Layout::LegendBox* HTMLLegendElement::layout_node()
{
return static_cast<Layout::LegendBox*>(Node::layout_node());
}
Layout::LegendBox const* HTMLLegendElement::layout_node() const
{
return static_cast<Layout::LegendBox const*>(Node::layout_node());
}
}

View file

@ -7,6 +7,7 @@
#pragma once
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/Layout/LegendBox.h>
namespace Web::HTML {
@ -19,6 +20,10 @@ public:
HTMLFormElement* form();
virtual JS::GCPtr<Layout::Node> create_layout_node(CSS::StyleProperties) override;
Layout::LegendBox* layout_node();
Layout::LegendBox const* layout_node() const;
private:
HTMLLegendElement(DOM::Document&, DOM::QualifiedName);

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024, Kostya Farber <kostya.farber@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Forward.h>
#include <LibWeb/HTML/HTMLLegendElement.h>
#include <LibWeb/Layout/FieldSetBox.h>
namespace Web::Layout {
JS_DEFINE_ALLOCATOR(FieldSetBox);
FieldSetBox::FieldSetBox(DOM::Document& document, DOM::Element& element, CSS::StyleProperties style)
: BlockContainer(document, &element, move(style))
{
}
FieldSetBox::~FieldSetBox() = default;
bool FieldSetBox::has_rendered_legend() const
{
// https://html.spec.whatwg.org/#rendered-legend
if (this->has_children() && this->first_child()->is_legend_box()) {
auto* first_child = this->first_child();
return first_child->computed_values().float_() == CSS::Float::None
&& first_child->computed_values().position() != CSS::Positioning::Absolute
&& first_child->computed_values().position() != CSS::Positioning::Fixed;
}
return false;
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2024, Kostya Farber <kostya.farber@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Forward.h>
#include <LibWeb/Layout/BlockContainer.h>
namespace Web::Layout {
class FieldSetBox final : public BlockContainer {
JS_CELL(FieldSetBox, BlockContainer);
JS_DECLARE_ALLOCATOR(FieldSetBox);
public:
FieldSetBox(DOM::Document&, DOM::Element&, CSS::StyleProperties);
virtual ~FieldSetBox() override;
DOM::Element& dom_node() { return static_cast<DOM::Element&>(*BlockContainer::dom_node()); }
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*BlockContainer::dom_node()); }
void layout_legend() const;
private:
bool has_rendered_legend() const;
virtual bool is_fieldset_box() const final
{
return true;
}
};
template<>
inline bool Node::fast_is<FieldSetBox>() const { return is_fieldset_box(); }
}

View file

@ -111,6 +111,12 @@ bool FormattingContext::creates_block_formatting_context(Box const& box)
// FIXME: column-span: all should always create a new formatting context, even when the column-span: all element isn't contained by a multicol container (Spec change, Chrome bug).
// https://html.spec.whatwg.org/#the-fieldset-and-legend-elements
if (box.is_fieldset_box())
// The fieldset element, when it generates a CSS box, is expected to act as follows:
// The element is expected to establish a new block formatting context.
return true;
return false;
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2024, Kostya Farber <kostya.farber@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Layout/LegendBox.h>
namespace Web::Layout {
JS_DEFINE_ALLOCATOR(LegendBox);
LegendBox::LegendBox(DOM::Document& document, DOM::Element& element, CSS::StyleProperties style)
: BlockContainer(document, &element, move(style))
{
}
LegendBox::~LegendBox() = default;
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024, Kostya Farber <kostya.farber@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Layout/BlockContainer.h>
namespace Web::Layout {
class LegendBox final : public BlockContainer {
JS_CELL(LegendBox, BlockContainer);
JS_DECLARE_ALLOCATOR(LegendBox);
public:
LegendBox(DOM::Document&, DOM::Element&, CSS::StyleProperties);
virtual ~LegendBox() override;
DOM::Element& dom_node() { return static_cast<DOM::Element&>(*Box::dom_node()); }
DOM::Element const& dom_node() const { return static_cast<DOM::Element const&>(*Box::dom_node()); }
private:
virtual bool is_legend_box() const final
{
return true;
}
};
template<>
inline bool Node::fast_is<LegendBox>() const { return is_legend_box(); }
}

View file

@ -115,6 +115,8 @@ public:
virtual bool is_replaced_box() const { return false; }
virtual bool is_list_item_box() const { return false; }
virtual bool is_list_item_marker_box() const { return false; }
virtual bool is_fieldset_box() const { return false; }
virtual bool is_legend_box() const { return false; }
virtual bool is_table_wrapper() const { return false; }
virtual bool is_node_with_style_and_box_model_metrics() const { return false; }