diff --git a/Tests/LibWeb/Layout/expected/input-element-with-display-inline.txt b/Tests/LibWeb/Layout/expected/input-element-with-display-inline.txt index fd8360912f..20f97d91c0 100644 --- a/Tests/LibWeb/Layout/expected/input-element-with-display-inline.txt +++ b/Tests/LibWeb/Layout/expected/input-element-with-display-inline.txt @@ -5,6 +5,9 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 200x25.84375] BlockContainer at (11,11) content-size 200x25.84375 inline-block [BFC] children: not-inline Box
at (13,12) content-size 196x23.84375 flex-container(row) [FFC] children: not-inline + BlockContainer <(anonymous)> at (13,23.921875) content-size 0x0 flex-item [BFC] children: inline + InlineNode
+ TextNode <#text> BlockContainer
at (14,13) content-size 194x21.84375 flex-item [BFC] children: inline TextNode <#text> @@ -13,4 +16,6 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600] PaintableWithLines (BlockContainer) [9,9 782x29.84375] PaintableWithLines (BlockContainer) [10,10 202x27.84375] PaintableBox (Box
) [11,11 200x25.84375] + PaintableWithLines (BlockContainer(anonymous)) [13,23.921875 0x0] + InlinePaintable (InlineNode
) PaintableWithLines (BlockContainer
) [13,12 196x23.84375] diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp index 792922821e..9596f9dedb 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.cpp @@ -59,6 +59,11 @@ String PropertyOwningCSSStyleDeclaration::item(size_t index) const return MUST(String::from_utf8(CSS::string_from_property_id(m_properties[index].property_id))); } +CSS::PropertyID PropertyOwningCSSStyleDeclaration::property_id_by_index(size_t index) const +{ + return m_properties[index].property_id; +} + JS::NonnullGCPtr ElementInlineCSSStyleDeclaration::create(DOM::Element& element, Vector properties, HashMap custom_properties) { auto& realm = element.realm(); diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h index b485a6169d..864ccfda36 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleDeclaration.h @@ -24,6 +24,7 @@ public: virtual size_t length() const = 0; virtual String item(size_t index) const = 0; + virtual CSS::PropertyID property_id_by_index(size_t index) const = 0; virtual Optional property(PropertyID) const = 0; @@ -63,6 +64,7 @@ public: virtual size_t length() const override; virtual String item(size_t index) const override; + virtual CSS::PropertyID property_id_by_index(size_t index) const override; virtual Optional property(PropertyID) const override; diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index 0b2f71d216..a8246800f8 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -82,6 +82,11 @@ String ResolvedCSSStyleDeclaration::item(size_t index) const return MUST(String::from_utf8(string_from_property_id(property_id))); } +CSS::PropertyID ResolvedCSSStyleDeclaration::property_id_by_index(size_t index) const +{ + return static_cast(index + to_underlying(first_longhand_property_id)); +} + static NonnullRefPtr style_value_for_background_property(Layout::NodeWithStyle const& layout_node, Function(BackgroundLayerData const&)> callback, Function()> default_value) { auto const& background_layers = layout_node.background_layers(); diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h index a1a02febf5..8682e0ff48 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.h @@ -21,6 +21,8 @@ public: virtual size_t length() const override; virtual String item(size_t index) const override; + virtual CSS::PropertyID property_id_by_index(size_t index) const override; + virtual Optional property(PropertyID) const override; virtual WebIDL::ExceptionOr set_property(PropertyID, StringView css_text, StringView priority) override; virtual WebIDL::ExceptionOr remove_property(PropertyID) override; diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index c3c52f6e34..9a53db04c9 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -173,7 +173,8 @@ public: RequiredInvalidationAfterStyleChange recompute_style(); - virtual Optional pseudo_element() const { return {}; } + Optional use_pseudo_element() const { return m_use_pseudo_element; } + void set_use_pseudo_element(Optional use_pseudo_element) { m_use_pseudo_element = use_pseudo_element; } Layout::NodeWithStyle* layout_node(); Layout::NodeWithStyle const* layout_node() const; @@ -413,6 +414,8 @@ private: mutable OwnPtr m_pseudo_element_custom_properties; PseudoElementCustomProperties& pseudo_element_custom_properties() const; + Optional m_use_pseudo_element {}; + Vector m_classes; Optional m_dir; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index e1004bc18a..5d648ec339 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -524,20 +524,6 @@ Optional HTMLInputElement::placeholder_value() const return placeholder; } -class PlaceholderElement final : public HTMLDivElement { - JS_CELL(PlaceholderElement, HTMLDivElement); - JS_DECLARE_ALLOCATOR(PlaceholderElement); - -public: - PlaceholderElement(DOM::Document& document) - : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""_fly_string, Namespace::HTML }) - { - } - virtual Optional pseudo_element() const override { return CSS::Selector::PseudoElement::Placeholder; } -}; - -JS_DEFINE_ALLOCATOR(PlaceholderElement); - void HTMLInputElement::create_shadow_tree_if_needed() { if (shadow_root_internal()) @@ -579,7 +565,8 @@ void HTMLInputElement::create_text_input_shadow_tree() )~~~"_string)); MUST(shadow_root->append_child(element)); - m_placeholder_element = heap().allocate(realm(), document()); + m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + m_placeholder_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Placeholder); MUST(m_placeholder_element->set_attribute(HTML::AttributeNames::style, R"~~~( flex: 1; height: 1lh; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp index efae3276df..5aa8c39bac 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp @@ -6,9 +6,11 @@ */ #include +#include #include #include #include +#include namespace Web::HTML { @@ -188,10 +190,11 @@ void HTMLMeterElement::create_shadow_tree_if_needed() auto shadow_root = heap().allocate(realm(), document(), *this, Bindings::ShadowRootMode::Closed); set_shadow_root(shadow_root); - auto meter_bar_element = heap().allocate(realm(), document()); + auto meter_bar_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + meter_bar_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterBar); MUST(shadow_root->append_child(*meter_bar_element)); - m_meter_value_element = heap().allocate(realm(), document()); + m_meter_value_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); MUST(meter_bar_element->append_child(*m_meter_value_element)); update_meter_value_element(); } @@ -212,26 +215,26 @@ void HTMLMeterElement::update_meter_value_element() // If the optimum point is equal to the low boundary or the high boundary, or anywhere in between them, then the region between the low and high boundaries of the gauge must be treated as the optimum region, and the low and high parts, if any, must be treated as suboptimal. if (optimum >= low && optimum <= high) { if (value >= low && value <= high) - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterOptimumValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterOptimumValue); else - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); } // Otherwise, if the optimum point is less than the low boundary, then the region between the minimum value and the low boundary must be treated as the optimum region, the region from the low boundary up to the high boundary must be treated as a suboptimal region, and the remaining region must be treated as an even less good region. else if (optimum < low) { if (value >= low && value <= high) - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); else - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterEvenLessGoodValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterEvenLessGoodValue); } // Finally, if the optimum point is higher than the high boundary, then the situation is reversed; the region between the high boundary and the maximum value must be treated as the optimum region, the region from the high boundary down to the low boundary must be treated as a suboptimal region, and the remaining region must be treated as an even less good region. else { if (value >= low && value <= high) - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterSuboptimumValue); else - m_meter_value_element->set_pseudo_element(CSS::Selector::PseudoElement::MeterEvenLessGoodValue); + m_meter_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::MeterEvenLessGoodValue); } double position = (value - min) / (max - min) * 100; - MUST(m_meter_value_element->set_attribute(HTML::AttributeNames::style, MUST(String::formatted("width: {}%;", position)))); + MUST(m_meter_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position)))); } } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h index 391da7867e..1ac3d39d04 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h @@ -9,39 +9,10 @@ #pragma once #include -#include #include -#include namespace Web::HTML { -class MeterBarElement final : public HTMLDivElement { - JS_CELL(MeterBarElement, HTMLDivElement); - -public: - MeterBarElement(DOM::Document& document) - : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""_fly_string, Namespace::HTML }) - { - } - virtual Optional pseudo_element() const override { return CSS::Selector::PseudoElement::MeterBar; } -}; - -class MeterValueElement final : public HTMLDivElement { - JS_CELL(MeterValueElement, HTMLDivElement); - -public: - MeterValueElement(DOM::Document& document) - : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""_fly_string, Namespace::HTML }) - { - } - virtual Optional pseudo_element() const override { return m_pseudo_element; } - - void set_pseudo_element(CSS::Selector::PseudoElement pseudo_element) { m_pseudo_element = pseudo_element; } - -private: - CSS::Selector::PseudoElement m_pseudo_element; -}; - class HTMLMeterElement final : public HTMLElement { WEB_PLATFORM_OBJECT(HTMLMeterElement, HTMLElement); JS_DECLARE_ALLOCATOR(HTMLMeterElement); @@ -82,7 +53,7 @@ private: void update_meter_value_element(); - JS::GCPtr m_meter_value_element; + JS::GCPtr m_meter_value_element; }; } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp index 3368460dbd..e7d9da40a7 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.cpp @@ -7,9 +7,11 @@ */ #include +#include #include #include #include +#include namespace Web::HTML { @@ -102,17 +104,19 @@ void HTMLProgressElement::create_shadow_tree_if_needed() auto shadow_root = heap().allocate(realm(), document(), *this, Bindings::ShadowRootMode::Closed); set_shadow_root(shadow_root); - auto progress_bar_element = heap().allocate(realm(), document()); + auto progress_bar_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + progress_bar_element->set_use_pseudo_element(CSS::Selector::PseudoElement::ProgressBar); MUST(shadow_root->append_child(*progress_bar_element)); - m_progress_value_element = heap().allocate(realm(), document()); + m_progress_value_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + m_progress_value_element->set_use_pseudo_element(CSS::Selector::PseudoElement::ProgressValue); MUST(progress_bar_element->append_child(*m_progress_value_element)); update_progress_value_element(); } void HTMLProgressElement::update_progress_value_element() { - MUST(m_progress_value_element->set_attribute(HTML::AttributeNames::style, MUST(String::formatted("width: {}%;", position() * 100)))); + MUST(m_progress_value_element->style_for_bindings()->set_property(CSS::PropertyID::Width, MUST(String::formatted("{}%", position() * 100)))); } } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h index 089c37217b..de8f266f83 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLProgressElement.h @@ -8,34 +8,10 @@ #pragma once #include -#include #include -#include namespace Web::HTML { -class ProgressBarElement final : public HTMLDivElement { - JS_CELL(ProgressBarElement, HTMLDivElement); - -public: - ProgressBarElement(DOM::Document& document) - : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""_fly_string, Namespace::HTML }) - { - } - virtual Optional pseudo_element() const override { return CSS::Selector::PseudoElement::ProgressBar; } -}; - -class ProgressValueElement final : public HTMLDivElement { - JS_CELL(ProgressValueElement, HTMLDivElement); - -public: - ProgressValueElement(DOM::Document& document) - : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""_fly_string, Namespace::HTML }) - { - } - virtual Optional pseudo_element() const override { return CSS::Selector::PseudoElement::ProgressValue; } -}; - class HTMLProgressElement final : public HTMLElement { WEB_PLATFORM_OBJECT(HTMLProgressElement, HTMLElement); JS_DECLARE_ALLOCATOR(HTMLProgressElement); @@ -76,7 +52,7 @@ private: bool is_determinate() const { return has_attribute(HTML::AttributeNames::value); } - JS::GCPtr m_progress_value_element; + JS::GCPtr m_progress_value_element; }; } diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 9176153126..c7ba7165b1 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -307,21 +307,23 @@ ErrorOr TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder:: if (is(dom_node)) { auto& element = static_cast(dom_node); - // Special path for elements that use pseudo selectors. - // FIXME: This is very hackish. Find a better way to architect this. - if (element.pseudo_element() == CSS::Selector::PseudoElement::Placeholder || element.pseudo_element() == CSS::Selector::PseudoElement::MeterBar || element.pseudo_element() == CSS::Selector::PseudoElement::MeterOptimumValue || element.pseudo_element() == CSS::Selector::PseudoElement::MeterSuboptimumValue || element.pseudo_element() == CSS::Selector::PseudoElement::MeterEvenLessGoodValue || element.pseudo_element() == CSS::Selector::PseudoElement::ProgressBar || element.pseudo_element() == CSS::Selector::PseudoElement::ProgressValue) { + // Special path for elements that use pseudo element as style selector. + if (element.use_pseudo_element().has_value()) { + // Get base psuedo element selector style properties auto& parent_element = verify_cast(*element.root().parent_or_shadow_host()); - style = TRY(style_computer.compute_style(parent_element, element.pseudo_element())); + style = TRY(style_computer.compute_style(parent_element, *element.use_pseudo_element())); + + // Merge back inline styles + auto const* inline_style = element.inline_style(); + if (inline_style) { + auto const& computed_style = element.computed_css_values(); + for (size_t i = 0; i < inline_style->length(); i++) { + auto property_id = inline_style->property_id_by_index(i); + if (auto property = computed_style->maybe_null_property(property_id); property) + style->set_property(property_id, *property); + } + } display = style->display(); - if (element.pseudo_element() == CSS::Selector::PseudoElement::Placeholder) { - auto& input_element = verify_cast(parent_element); - if (!input_element.placeholder_value().has_value()) - display = CSS::Display::from_short(CSS::Display::Short::None); - } - if (element.pseudo_element() == CSS::Selector::PseudoElement::MeterOptimumValue || element.pseudo_element() == CSS::Selector::PseudoElement::MeterSuboptimumValue || element.pseudo_element() == CSS::Selector::PseudoElement::MeterEvenLessGoodValue || element.pseudo_element() == CSS::Selector::PseudoElement::ProgressValue) { - auto computed_style = element.computed_css_values(); - style->set_property(CSS::PropertyID::Width, computed_style->property(CSS::PropertyID::Width)); - } } // Common path: this is a regular DOM element. Style should be present already, thanks to Document::update_style(). else {