Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Libraries/LibWeb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,8 @@ set(SOURCES
Layout/TableFormattingContext.cpp
Layout/TableGrid.cpp
Layout/TableWrapper.cpp
Layout/TextAreaBox.cpp
Layout/TextInputBox.cpp
Layout/TextNode.cpp
Layout/TreeBuilder.cpp
Layout/VideoBox.cpp
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/CSS/Parser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1857,7 +1857,7 @@ LengthOrCalculated Parser::parse_as_sizes_attribute(DOM::Element const& element,
// Should this use some of the methods from FormattingContext?
auto concrete_size = run_default_sizing_algorithm(
img->width(), img->height(),
img->natural_width(), img->natural_height(), img->intrinsic_aspect_ratio(),
{ img->natural_width(), img->natural_height(), img->intrinsic_aspect_ratio() },
// NOTE: https://html.spec.whatwg.org/multipage/rendering.html#img-contain-size
CSSPixelSize { 300, 150 });
size = Length::make_px(concrete_size.width());
Expand Down
21 changes: 10 additions & 11 deletions Libraries/LibWeb/CSS/Sizing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ namespace Web::CSS {
// https://drafts.csswg.org/css-images/#default-sizing
CSSPixelSize run_default_sizing_algorithm(
Optional<CSSPixels> specified_width, Optional<CSSPixels> specified_height,
Optional<CSSPixels> natural_width, Optional<CSSPixels> natural_height,
Optional<CSSPixelFraction> natural_aspect_ratio,
SizeWithAspectRatio const& natural,
CSSPixelSize default_size)
{
// If the specified size is a definite width and height, the concrete object size is given that width and height.
Expand All @@ -24,18 +23,18 @@ CSSPixelSize run_default_sizing_algorithm(
if (specified_width.has_value() || specified_height.has_value()) {
// 1. If the object has a natural aspect ratio,
// the missing dimension of the concrete object size is calculated using that aspect ratio and the present dimension.
if (natural_aspect_ratio.has_value() && !natural_aspect_ratio->might_be_saturated()) {
if (natural.has_aspect_ratio() && !natural.aspect_ratio->might_be_saturated()) {
if (specified_width.has_value())
return CSSPixelSize { specified_width.value(), (CSSPixels(1) / natural_aspect_ratio.value()) * specified_width.value() };
return CSSPixelSize { specified_width.value(), (CSSPixels(1) / natural.aspect_ratio.value()) * specified_width.value() };
if (specified_height.has_value())
return CSSPixelSize { specified_height.value() * natural_aspect_ratio.value(), specified_height.value() };
return CSSPixelSize { specified_height.value() * natural.aspect_ratio.value(), specified_height.value() };
}
// 2. Otherwise, if the missing dimension is present in the object’s natural dimensions,
// the missing dimension is taken from the object’s natural dimensions.
if (specified_height.has_value() && natural_width.has_value())
return CSSPixelSize { natural_width.value(), specified_height.value() };
if (specified_width.has_value() && natural_height.has_value())
return CSSPixelSize { specified_width.value(), natural_height.value() };
if (specified_height.has_value() && natural.has_width())
return CSSPixelSize { natural.width.value(), specified_height.value() };
if (specified_width.has_value() && natural.has_height())
return CSSPixelSize { specified_width.value(), natural.height.value() };
// 3. Otherwise, the missing dimension of the concrete object size is taken from the default object size.
if (specified_height.has_value())
return CSSPixelSize { default_size.width(), specified_height.value() };
Expand All @@ -45,8 +44,8 @@ CSSPixelSize run_default_sizing_algorithm(
}
// If the specified size has no constraints:
// 1. If the object has a natural height or width, its size is resolved as if its natural dimensions were given as the specified size.
if (natural_width.has_value() || natural_height.has_value())
return run_default_sizing_algorithm(natural_width, natural_height, natural_width, natural_height, natural_aspect_ratio, default_size);
if (natural.has_width() || natural.has_height())
return run_default_sizing_algorithm(natural.width, natural.height, natural, default_size);
// FIXME: 2. Otherwise, its size is resolved as a contain constraint against the default object size.
return default_size;
}
Expand Down
12 changes: 10 additions & 2 deletions Libraries/LibWeb/CSS/Sizing.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@

namespace Web::CSS {

struct SizeWithAspectRatio {
Optional<CSSPixels> width;
Optional<CSSPixels> height;
Optional<CSSPixelFraction> aspect_ratio;
bool has_width() const { return width.has_value(); }
bool has_height() const { return height.has_value(); }
bool has_aspect_ratio() const { return aspect_ratio.has_value(); }
};

// https://drafts.csswg.org/css-images/#default-sizing
CSSPixelSize run_default_sizing_algorithm(
Optional<CSSPixels> specified_width, Optional<CSSPixels> specified_height,
Optional<CSSPixels> natural_width, Optional<CSSPixels> natural_height,
Optional<CSSPixelFraction> natural_aspect_ratio,
SizeWithAspectRatio const& natural_size,
CSSPixelSize default_size);

}
2 changes: 1 addition & 1 deletion Libraries/LibWeb/CSS/StyleValues/CursorStyleValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Optional<Gfx::ImageCursor> CursorStyleValue::make_image_cursor(Layout::NodeWithS
// 32x32 is selected arbitrarily.
// FIXME: Ask the OS for the default size?
CSSPixelSize const default_cursor_size { 32, 32 };
auto cursor_css_size = run_default_sizing_algorithm({}, {}, image.natural_width(), image.natural_height(), image.natural_aspect_ratio(), default_cursor_size);
auto cursor_css_size = run_default_sizing_algorithm({}, {}, { image.natural_width(), image.natural_height(), image.natural_aspect_ratio() }, default_cursor_size);
// FIXME: How do we determine what cursor sizes the OS allows?
// We don't multiply by the pixel ratio, because we want to use the image's actual pixel size.
DevicePixelSize cursor_device_size { cursor_css_size.to_type<double>().to_rounded<int>() };
Expand Down
27 changes: 20 additions & 7 deletions Libraries/LibWeb/HTML/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <LibWeb/Layout/CheckBox.h>
#include <LibWeb/Layout/ImageBox.h>
#include <LibWeb/Layout/RadioButton.h>
#include <LibWeb/Layout/TextInputBox.h>
#include <LibWeb/MimeSniff/MimeType.h>
#include <LibWeb/MimeSniff/Resource.h>
#include <LibWeb/Namespace.h>
Expand Down Expand Up @@ -118,16 +119,28 @@ GC::Ptr<Layout::Node> HTMLInputElement::create_layout_node(GC::Ref<CSS::Computed
return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
}

if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
return heap().allocate<Layout::BlockContainer>(document(), this, move(style));
switch (type_state()) {

if (type_state() == TypeAttributeState::Checkbox)
case TypeAttributeState::SubmitButton:
case TypeAttributeState::Button:
case TypeAttributeState::ResetButton:
return heap().allocate<Layout::BlockContainer>(document(), this, move(style));
case TypeAttributeState::Checkbox:
return heap().allocate<Layout::CheckBox>(document(), *this, move(style));

if (type_state() == TypeAttributeState::RadioButton)
case TypeAttributeState::RadioButton:
return heap().allocate<Layout::RadioButton>(document(), *this, move(style));

return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
case TypeAttributeState::Text:
case TypeAttributeState::Search:
case TypeAttributeState::URL:
case TypeAttributeState::Telephone:
case TypeAttributeState::Email:
case TypeAttributeState::Password:
case TypeAttributeState::Number:
// FIXME: text padding issues
return heap().allocate<Layout::TextInputBox>(document(), *this, move(style));
default:
return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
}
}

void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style)
Expand Down
11 changes: 6 additions & 5 deletions Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <LibWeb/HTML/HTMLTextAreaElement.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Layout/TextAreaBox.h>
#include <LibWeb/Namespace.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/Selection/Selection.h>
Expand Down Expand Up @@ -51,11 +52,6 @@ void HTMLTextAreaElement::adjust_computed_style(CSS::ComputedProperties& style)
// This is required for the internal shadow tree to work correctly in layout.
if (style.display().is_inline_outside() && style.display().is_flow_inside())
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::InlineBlock)));

if (style.property(CSS::PropertyID::Width).has_auto())
style.set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length(cols(), CSS::LengthUnit::Ch)));
if (style.property(CSS::PropertyID::Height).has_auto())
style.set_property(CSS::PropertyID::Height, CSS::LengthStyleValue::create(CSS::Length(rows(), CSS::LengthUnit::Lh)));
}

void HTMLTextAreaElement::initialize(JS::Realm& realm)
Expand Down Expand Up @@ -466,4 +462,9 @@ bool HTMLTextAreaElement::is_mutable() const
return enabled() && !has_attribute(AttributeNames::readonly);
}

GC::Ptr<Layout::Node> HTMLTextAreaElement::create_layout_node(GC::Ref<CSS::ComputedProperties> style)
{
return heap().allocate<Layout::TextAreaBox>(document(), *this, style);
}

}
1 change: 1 addition & 0 deletions Libraries/LibWeb/HTML/HTMLTextAreaElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class WEB_API HTMLTextAreaElement final

virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;

void set_raw_value(Utf16String);

Expand Down
14 changes: 4 additions & 10 deletions Libraries/LibWeb/Layout/AudioBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ GC_DEFINE_ALLOCATOR(AudioBox);
AudioBox::AudioBox(DOM::Document& document, DOM::Element& element, GC::Ref<CSS::ComputedProperties> style)
: ReplacedBox(document, element, move(style))
{
set_natural_width(300);
set_natural_height(40);
}

HTML::HTMLAudioElement& AudioBox::dom_node()
Expand All @@ -34,15 +32,11 @@ GC::Ptr<Painting::Paintable> AudioBox::create_paintable() const
return Painting::AudioPaintable::create(*this);
}

void AudioBox::prepare_for_replaced_layout()
CSS::SizeWithAspectRatio AudioBox::natural_size() const
{
if (dom_node().should_paint()) {
set_natural_width(300);
set_natural_height(40);
} else {
set_natural_width(0);
set_natural_height(0);
}
if (dom_node().should_paint())
return { 300, 40, {} };
return { 0, 0, {} };
}

}
3 changes: 1 addition & 2 deletions Libraries/LibWeb/Layout/AudioBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ class AudioBox final : public ReplacedBox {
GC_DECLARE_ALLOCATOR(AudioBox);

public:
virtual void prepare_for_replaced_layout() override;

HTML::HTMLAudioElement& dom_node();
HTML::HTMLAudioElement const& dom_node() const;

virtual GC::Ptr<Painting::Paintable> create_paintable() const override;

private:
virtual CSS::SizeWithAspectRatio natural_size() const override;
AudioBox(DOM::Document&, DOM::Element&, GC::Ref<CSS::ComputedProperties>);
};

Expand Down
9 changes: 2 additions & 7 deletions Libraries/LibWeb/Layout/BlockFormattingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,6 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const&
}

if (box_is_sized_as_replaced_element(box, available_space)) {
// FIXME: This should not be done *by* ReplacedBox
if (auto* replaced = as_if<ReplacedBox>(box)) {
// FIXME: This const_cast is gross.
const_cast<ReplacedBox&>(*replaced).prepare_for_replaced_layout();
}
compute_width_for_block_level_replaced_element_in_normal_flow(box, available_space);
if (box.is_floating()) {
// 10.3.6 Floating, replaced elements:
Expand Down Expand Up @@ -803,7 +798,7 @@ void BlockFormattingContext::layout_block_level_box(Box const& box, BlockContain
resolve_used_height_if_not_treated_as_auto(box, available_space_for_height_resolution);

// NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
if (box.is_replaced_box() || box.display().is_flex_inside()) {
if (box.has_intrinsic_content_box_size() || box.display().is_flex_inside()) {
resolve_used_height_if_treated_as_auto(box, available_space_for_height_resolution);
}

Expand Down Expand Up @@ -1083,7 +1078,7 @@ void BlockFormattingContext::layout_floating_box(Box const& box, BlockContainer
resolve_used_height_if_not_treated_as_auto(box, available_space);

// NOTE: Flex containers with `auto` height are treated as `max-content`, so we can compute their height early.
if (box.is_replaced_box() || box.display().is_flex_inside()) {
if (box.has_intrinsic_content_box_size() || box.display().is_flex_inside()) {
resolve_used_height_if_treated_as_auto(box, available_space);
}

Expand Down
50 changes: 19 additions & 31 deletions Libraries/LibWeb/Layout/Box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,16 @@ Box::~Box()
{
}

Optional<CSSPixels> Box::natural_width() const
CSS::SizeWithAspectRatio Box::intrinsic_content_box_size() const
{
// https://drafts.csswg.org/css-contain-2/#containment-size
// https://www.w3.org/TR/css-contain-2/#containment-size
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
// ratio.
if (has_size_containment())
return 0;
return m_natural_width;
}
Optional<CSSPixels> Box::natural_height() const
{
// https://drafts.csswg.org/css-contain-2/#containment-size
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
// ratio.
if (has_size_containment())
return 0;
return m_natural_height;
}
Optional<CSSPixelFraction> Box::natural_aspect_ratio() const
{
// https://drafts.csswg.org/css-contain-2/#containment-size
// Replaced elements must be treated as having a natural width and height of 0 and no natural aspect
// ratio.
if (has_size_containment())
return {};
return m_natural_aspect_ratio;
return { 0, 0, {} };

// Return the content-box intrinsic size
return compute_intrinsic_content_box_size();
}

void Box::visit_edges(Cell::Visitor& visitor)
Expand All @@ -80,18 +64,22 @@ Painting::PaintableBox const* Box::paintable_box() const

Optional<CSSPixelFraction> Box::preferred_aspect_ratio() const
{
auto computed_aspect_ratio = computed_values().aspect_ratio();
if (computed_aspect_ratio.use_natural_aspect_ratio_if_available && natural_aspect_ratio().has_value())
return natural_aspect_ratio();
auto const& aspect = computed_values().aspect_ratio();

// https://www.w3.org/TR/css-contain-2/#containment-size

if (!computed_aspect_ratio.preferred_ratio.has_value())
return {};
if (!has_size_containment() && aspect.use_natural_aspect_ratio_if_available) {
if (auto intrinsic = intrinsic_content_box_size(); intrinsic.has_aspect_ratio())
return intrinsic.aspect_ratio;
}

auto ratio = computed_aspect_ratio.preferred_ratio.release_value();
if (ratio.is_degenerate())
return {};
if (aspect.preferred_ratio.has_value()) {
auto ratio = aspect.preferred_ratio.value();
if (!ratio.is_degenerate())
return CSSPixelFraction(ratio.numerator(), ratio.denominator());
}

return CSSPixelFraction(ratio.numerator(), ratio.denominator());
return {};
}

}
34 changes: 12 additions & 22 deletions Libraries/LibWeb/Layout/Box.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <AK/OwnPtr.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/Sizing.h>
#include <LibWeb/Export.h>
#include <LibWeb/Layout/Node.h>

Expand All @@ -18,7 +19,7 @@ struct LineBoxFragmentCoordinate {
size_t fragment_index { 0 };
};

struct IntrinsicSizes {
struct IntrinsicMinMaxCache {
Optional<CSSPixels> min_content_width;
Optional<CSSPixels> max_content_width;
HashMap<CSSPixels, Optional<CSSPixels>> min_content_height;
Expand All @@ -33,17 +34,9 @@ class WEB_API Box : public NodeWithStyleAndBoxModelMetrics {
Painting::PaintableBox* paintable_box();

// https://www.w3.org/TR/css-images-3/#natural-dimensions
Optional<CSSPixels> natural_width() const;
Optional<CSSPixels> natural_height() const;
Optional<CSSPixelFraction> natural_aspect_ratio() const;

bool has_natural_width() const { return natural_width().has_value(); }
bool has_natural_height() const { return natural_height().has_value(); }
bool has_natural_aspect_ratio() const { return natural_aspect_ratio().has_value(); }

void set_natural_width(Optional<CSSPixels> width) { m_natural_width = width; }
void set_natural_height(Optional<CSSPixels> height) { m_natural_height = height; }
void set_natural_aspect_ratio(Optional<CSSPixelFraction> ratio) { m_natural_aspect_ratio = ratio; }
virtual CSS::SizeWithAspectRatio natural_size() const { return {}; }
CSS::SizeWithAspectRatio intrinsic_content_box_size() const;
virtual bool has_intrinsic_content_box_size() const { return false; }

// https://www.w3.org/TR/css-sizing-4/#preferred-aspect-ratio
Optional<CSSPixelFraction> preferred_aspect_ratio() const;
Expand All @@ -61,28 +54,25 @@ class WEB_API Box : public NodeWithStyleAndBoxModelMetrics {

virtual void visit_edges(Cell::Visitor&) override;

IntrinsicSizes& cached_intrinsic_sizes() const
IntrinsicMinMaxCache& intrinsic_min_max_cache() const
{
if (!m_cached_intrinsic_sizes)
m_cached_intrinsic_sizes = make<IntrinsicSizes>();
return *m_cached_intrinsic_sizes;
if (!m_intrinsic_min_max_cache)
m_intrinsic_min_max_cache = make<IntrinsicMinMaxCache>();
return *m_intrinsic_min_max_cache;
}
void reset_cached_intrinsic_sizes() const { m_cached_intrinsic_sizes.clear(); }
void reset_cached_intrinsic_sizes() const { m_intrinsic_min_max_cache.clear(); }

protected:
Box(DOM::Document&, DOM::Node*, GC::Ref<CSS::ComputedProperties>);
Box(DOM::Document&, DOM::Node*, NonnullOwnPtr<CSS::ComputedValues>);
virtual CSS::SizeWithAspectRatio compute_intrinsic_content_box_size() const { return natural_size(); }

private:
virtual bool is_box() const final { return true; }

Optional<CSSPixels> m_natural_width;
Optional<CSSPixels> m_natural_height;
Optional<CSSPixelFraction> m_natural_aspect_ratio;

Vector<GC::Ref<Node>> m_contained_abspos_children;

OwnPtr<IntrinsicSizes> mutable m_cached_intrinsic_sizes;
OwnPtr<IntrinsicMinMaxCache> mutable m_intrinsic_min_max_cache;
};

template<>
Expand Down
Loading