From 2107ab823d69178dad1cf95672c553f4688f4487 Mon Sep 17 00:00:00 2001 From: Bastiaan van der Plaat Date: Sat, 25 Nov 2023 14:32:40 +0100 Subject: [PATCH] LibWeb: Add basic HTML meter element support --- Base/res/html/misc/meter.html | 61 +++++ Tests/LibWeb/Ref/meter.html | 17 ++ .../LibWeb/Ref/reference/images/meter-ref.png | Bin 0 -> 1039 bytes Tests/LibWeb/Ref/reference/meter-ref.html | 15 ++ Userland/Libraries/LibWeb/CSS/Default.css | 24 ++ Userland/Libraries/LibWeb/CSS/Selector.cpp | 8 + Userland/Libraries/LibWeb/CSS/Selector.h | 12 + Userland/Libraries/LibWeb/Dump.cpp | 12 + .../Libraries/LibWeb/HTML/AttributeNames.h | 3 + .../LibWeb/HTML/HTMLMeterElement.cpp | 219 ++++++++++++++++++ .../Libraries/LibWeb/HTML/HTMLMeterElement.h | 51 ++++ .../LibWeb/HTML/HTMLMeterElement.idl | 12 +- .../Libraries/LibWeb/Layout/TreeBuilder.cpp | 22 +- 13 files changed, 442 insertions(+), 14 deletions(-) create mode 100644 Base/res/html/misc/meter.html create mode 100644 Tests/LibWeb/Ref/meter.html create mode 100644 Tests/LibWeb/Ref/reference/images/meter-ref.png create mode 100644 Tests/LibWeb/Ref/reference/meter-ref.html diff --git a/Base/res/html/misc/meter.html b/Base/res/html/misc/meter.html new file mode 100644 index 0000000000..675b57dee5 --- /dev/null +++ b/Base/res/html/misc/meter.html @@ -0,0 +1,61 @@ + + + + + + Meter showcase + + + +

+ +

+ +

Basic meters:

+

+ 0.5%
50%
that is a half
+

+

+ 4/10 +

+

+ grade 3 +

+ +

Meters with values outside low and high

+

+ +

+

+ +

+ +

Meters with values outside optimum

+

+ +

+ +

Meters with values outside min and max

+

+ +

+

+ +

+ + + + diff --git a/Tests/LibWeb/Ref/meter.html b/Tests/LibWeb/Ref/meter.html new file mode 100644 index 0000000000..5b8be7d8f5 --- /dev/null +++ b/Tests/LibWeb/Ref/meter.html @@ -0,0 +1,17 @@ + + +50% +4/10 +grade 3 + + + + + diff --git a/Tests/LibWeb/Ref/reference/images/meter-ref.png b/Tests/LibWeb/Ref/reference/images/meter-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..6cdefe2c576276fc2d522a431d88dd1054cfd507 GIT binary patch literal 1039 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2WU2W?*2*h?wupz`!6B;1lBNalE9es_N_4 zum3F){@)G%|NsBp$x5{h49q(`T^vIyZoR#8n)i@_fa^sE)`yoHVwp+=E*^TlU-a67 zH=m>r?i7u%BDYFiOdwgq21X_p4gm!R1Ve^7 zX-7oPj*JJfIe*>>GYASuJ5T?WcK%-R{r%HazF&6_ULU}Wu7`m!DLG?D#-BWSB?YmN zcN1C_;`3)V&q}*zv^~95u6q9_gz<0-CG?^KI5`A%I@kT>+xe|Y_g~?Gw7FQ+$*?Ew z$Z%9uaA0BSXkZW&P+(G`Bb&(uVJ{un4IEHEGPyX=p3NaJpMjqCA=||vzzB*ivOg4sm}wnGgQcKd!lzW3oWQ^4Ho8+;fT7#KWV{an^LB{Ts5eFx;u literal 0 HcmV?d00001 diff --git a/Tests/LibWeb/Ref/reference/meter-ref.html b/Tests/LibWeb/Ref/reference/meter-ref.html new file mode 100644 index 0000000000..dd6ea4c475 --- /dev/null +++ b/Tests/LibWeb/Ref/reference/meter-ref.html @@ -0,0 +1,15 @@ + + + diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css index b07ad97099..5db4b582dc 100644 --- a/Userland/Libraries/LibWeb/CSS/Default.css +++ b/Userland/Libraries/LibWeb/CSS/Default.css @@ -66,6 +66,30 @@ option { display: none; } +/* Custom styles */ +meter { + display: inline-block; + width: 300px; + height: 12px; +} +meter::-webkit-meter-bar, meter::-webkit-meter-optimum-value, meter::-webkit-meter-suboptimum-value, meter::-webkit-meter-even-less-good-value { + display: block; + height: 100%; +} +meter::-webkit-meter-bar { + background-color: hsl(0, 0%, 96%); + border: 1px solid rgba(0, 0, 0, 0.5); +} +meter::-webkit-meter-optimum-value { + background-color: hsl(141, 53%, 53%); +} +meter::-webkit-meter-suboptimum-value { + background-color: hsl(48, 100%, 67%); +} +meter::-webkit-meter-even-less-good-value { + background-color: hsl(348, 100%, 61%); +} + /* 15.3.1 Hidden elements * https://html.spec.whatwg.org/multipage/rendering.html#hidden-elements */ diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp index e1cd483704..42b7b4c33b 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.cpp +++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp @@ -373,6 +373,14 @@ Optional pseudo_element_from_string(StringView name) return Selector::PseudoElement::FirstLine; } else if (name.equals_ignoring_ascii_case("marker"sv)) { return Selector::PseudoElement::Marker; + } else if (name.equals_ignoring_ascii_case("-webkit-meter-bar"sv)) { + return Selector::PseudoElement::MeterBar; + } else if (name.equals_ignoring_ascii_case("-webkit-meter-even-less-good-value"sv)) { + return Selector::PseudoElement::MeterEvenLessGoodValue; + } else if (name.equals_ignoring_ascii_case("-webkit-meter-optimum-value"sv)) { + return Selector::PseudoElement::MeterOptimumValue; + } else if (name.equals_ignoring_ascii_case("-webkit-meter-suboptimum-value"sv)) { + return Selector::PseudoElement::MeterSuboptimumValue; } else if (name.equals_ignoring_ascii_case("-webkit-progress-bar"sv)) { return Selector::PseudoElement::ProgressBar; } else if (name.equals_ignoring_ascii_case("-webkit-progress-value"sv)) { diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index 436851511b..4129d1a35f 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -27,6 +27,10 @@ public: FirstLine, FirstLetter, Marker, + MeterBar, + MeterEvenLessGoodValue, + MeterOptimumValue, + MeterSuboptimumValue, ProgressValue, ProgressBar, Placeholder, @@ -217,6 +221,14 @@ constexpr StringView pseudo_element_name(Selector::PseudoElement pseudo_element) return "first-letter"sv; case Selector::PseudoElement::Marker: return "marker"sv; + case Selector::PseudoElement::MeterBar: + return "-webkit-meter-bar"sv; + case Selector::PseudoElement::MeterEvenLessGoodValue: + return "-webkit-meter-even-less-good-value"sv; + case Selector::PseudoElement::MeterOptimumValue: + return "-webkit-meter-optimum-value"sv; + case Selector::PseudoElement::MeterSuboptimumValue: + return "-webkit-meter-suboptimum-value"sv; case Selector::PseudoElement::ProgressBar: return "-webkit-progress-bar"sv; case Selector::PseudoElement::ProgressValue: diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index b09c1d48da..b4939e5812 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -559,6 +559,18 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) case CSS::Selector::PseudoElement::Marker: pseudo_element_description = "marker"; break; + case CSS::Selector::PseudoElement::MeterBar: + pseudo_element_description = "-webkit-meter-bar"; + break; + case CSS::Selector::PseudoElement::MeterEvenLessGoodValue: + pseudo_element_description = "-webkit-meter-even-less-good-value"; + break; + case CSS::Selector::PseudoElement::MeterOptimumValue: + pseudo_element_description = "-webkit-meter-optimum-value"; + break; + case CSS::Selector::PseudoElement::MeterSuboptimumValue: + pseudo_element_description = "-webkit-meter-suboptimum-value"; + break; case CSS::Selector::PseudoElement::ProgressBar: pseudo_element_description = "-webkit-progress-bar"; break; diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h index ed081927df..b14e9c61c8 100644 --- a/Userland/Libraries/LibWeb/HTML/AttributeNames.h +++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h @@ -78,6 +78,7 @@ namespace AttributeNames { __ENUMERATE_HTML_ATTRIBUTE(headers) \ __ENUMERATE_HTML_ATTRIBUTE(height) \ __ENUMERATE_HTML_ATTRIBUTE(hidden) \ + __ENUMERATE_HTML_ATTRIBUTE(high) \ __ENUMERATE_HTML_ATTRIBUTE(href) \ __ENUMERATE_HTML_ATTRIBUTE(hreflang) \ __ENUMERATE_HTML_ATTRIBUTE(hspace) \ @@ -98,6 +99,7 @@ namespace AttributeNames { __ENUMERATE_HTML_ATTRIBUTE(loading) \ __ENUMERATE_HTML_ATTRIBUTE(longdesc) \ __ENUMERATE_HTML_ATTRIBUTE(loop) \ + __ENUMERATE_HTML_ATTRIBUTE(low) \ __ENUMERATE_HTML_ATTRIBUTE(marginheight) \ __ENUMERATE_HTML_ATTRIBUTE(marginwidth) \ __ENUMERATE_HTML_ATTRIBUTE(max) \ @@ -195,6 +197,7 @@ namespace AttributeNames { __ENUMERATE_HTML_ATTRIBUTE(onwebkittransitionend) \ __ENUMERATE_HTML_ATTRIBUTE(onwheel) \ __ENUMERATE_HTML_ATTRIBUTE(open) \ + __ENUMERATE_HTML_ATTRIBUTE(optimum) \ __ENUMERATE_HTML_ATTRIBUTE(pattern) \ __ENUMERATE_HTML_ATTRIBUTE(ping) \ __ENUMERATE_HTML_ATTRIBUTE(placeholder) \ diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp index 9e18c719ea..3acf20e87a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.cpp @@ -1,11 +1,15 @@ /* * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2023, Bastiaan van der Plaat * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include +#include #include +#include namespace Web::HTML { @@ -24,4 +28,219 @@ void HTMLMeterElement::initialize(JS::Realm& realm) set_prototype(&Bindings::ensure_web_prototype(realm, "HTMLMeterElement"_fly_string)); } +void HTMLMeterElement::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_meter_value_element); +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-actual +double HTMLMeterElement::value() const +{ + // If the value attribute is specified and a value could be parsed out of it, then that value is the candidate actual value. Otherwise, the candidate actual value is zero. + double candidate_value = 0.0; + auto maybe_value_string = get_attribute(HTML::AttributeNames::value); + if (maybe_value_string.has_value()) { + auto maybe_value = parse_floating_point_number(maybe_value_string.value()); + if (maybe_value.has_value()) + candidate_value = maybe_value.value(); + } + + // If the candidate actual value is less than the minimum value, then the actual value is the minimum value. + // Otherwise, if the candidate actual value is greater than the maximum value, then the actual value is the maximum value. + // Otherwise, the actual value is the candidate actual value. + return clamp(candidate_value, min(), max()); +} + +WebIDL::ExceptionOr HTMLMeterElement::set_value(double value) +{ + TRY(set_attribute(HTML::AttributeNames::value, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-minimum +double HTMLMeterElement::min() const +{ + // If the min attribute is specified and a value could be parsed out of it, then the minimum value is that value. Otherwise, the minimum value is zero. + auto maybe_min_string = get_attribute(HTML::AttributeNames::min); + if (maybe_min_string.has_value()) { + auto maybe_min = parse_floating_point_number(maybe_min_string.value()); + if (maybe_min.has_value()) + return maybe_min.value(); + } + return 0; +} + +WebIDL::ExceptionOr HTMLMeterElement::set_min(double value) +{ + TRY(set_attribute(HTML::AttributeNames::min, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-maximum +double HTMLMeterElement::max() const +{ + // If the max attribute is specified and a value could be parsed out of it, then the candidate maximum value is that value. Otherwise, the candidate maximum value is 1.0. + double candidate_max = 1.0; + auto maybe_max_string = get_attribute(HTML::AttributeNames::max); + if (maybe_max_string.has_value()) { + auto maybe_max = parse_floating_point_number(maybe_max_string.value()); + if (maybe_max.has_value()) + candidate_max = maybe_max.value(); + } + + // If the candidate maximum value is greater than or equal to the minimum value, then the maximum value is the candidate maximum value. Otherwise, the maximum value is the same as the minimum value. + return AK::max(candidate_max, min()); +} + +WebIDL::ExceptionOr HTMLMeterElement::set_max(double value) +{ + TRY(set_attribute(HTML::AttributeNames::max, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-low +double HTMLMeterElement::low() const +{ + // If the low attribute is specified and a value could be parsed out of it, then the candidate low boundary is that value. Otherwise, the candidate low boundary is the same as the minimum value. + double candidate_low = min(); + auto maybe_low_string = get_attribute(HTML::AttributeNames::low); + if (maybe_low_string.has_value()) { + auto maybe_low = parse_floating_point_number(maybe_low_string.value()); + if (maybe_low.has_value()) + candidate_low = maybe_low.value(); + } + + // If the candidate low boundary is less than the minimum value, then the low boundary is the minimum value. + // Otherwise, if the candidate low boundary is greater than the maximum value, then the low boundary is the maximum value. + // Otherwise, the low boundary is the candidate low boundary. + return clamp(candidate_low, min(), max()); +} + +WebIDL::ExceptionOr HTMLMeterElement::set_low(double value) +{ + TRY(set_attribute(HTML::AttributeNames::low, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-high +double HTMLMeterElement::high() const +{ + // If the high attribute is specified and a value could be parsed out of it, then the candidate high boundary is that value. Otherwise, the candidate high boundary is the same as the maximum value. + double candidate_high = max(); + auto maybe_high_string = get_attribute(HTML::AttributeNames::high); + if (maybe_high_string.has_value()) { + auto maybe_high = parse_floating_point_number(maybe_high_string.value()); + if (maybe_high.has_value()) + candidate_high = maybe_high.value(); + } + + // If the candidate high boundary is less than the low boundary, then the high boundary is the low boundary. + // Otherwise, if the candidate high boundary is greater than the maximum value, then the high boundary is the maximum value. + // Otherwise, the high boundary is the candidate high boundary. + return clamp(candidate_high, low(), max()); +} + +WebIDL::ExceptionOr HTMLMeterElement::set_high(double value) +{ + TRY(set_attribute(HTML::AttributeNames::high, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +// https://html.spec.whatwg.org/multipage/form-elements.html#concept-meter-optimum +double HTMLMeterElement::optimum() const +{ + // If the optimum attribute is specified and a value could be parsed out of it, then the candidate optimum point is that value. Otherwise, the candidate optimum point is the midpoint between the minimum value and the maximum value. + double candidate_optimum = (max() + min()) / 2; + auto maybe_optimum_string = get_attribute(HTML::AttributeNames::optimum); + if (maybe_optimum_string.has_value()) { + auto maybe_optimum = parse_floating_point_number(maybe_optimum_string.value()); + if (maybe_optimum.has_value()) + candidate_optimum = maybe_optimum.value(); + } + + // If the candidate optimum point is less than the minimum value, then the optimum point is the minimum value. + // Otherwise, if the candidate optimum point is greater than the maximum value, then the optimum point is the maximum value. + // Otherwise, the optimum point is the candidate optimum point. + return clamp(candidate_optimum, min(), max()); +} + +WebIDL::ExceptionOr HTMLMeterElement::set_optimum(double value) +{ + TRY(set_attribute(HTML::AttributeNames::optimum, MUST(String::number(value)))); + update_meter_value_element(); + document().invalidate_layout(); + return {}; +} + +void HTMLMeterElement::inserted() +{ + create_shadow_tree_if_needed(); +} + +void HTMLMeterElement::create_shadow_tree_if_needed() +{ + if (shadow_root_internal()) + return; + + auto shadow_root = heap().allocate(realm(), document(), *this, Bindings::ShadowRootMode::Closed); + set_shadow_root(shadow_root); + + auto meter_bar_element = heap().allocate(realm(), document()); + MUST(shadow_root->append_child(*meter_bar_element)); + + m_meter_value_element = heap().allocate(realm(), document()); + MUST(meter_bar_element->append_child(*m_meter_value_element)); + update_meter_value_element(); +} + +void HTMLMeterElement::update_meter_value_element() +{ + if (!m_meter_value_element) + return; + + // UA requirements for regions of the gauge: + double value = this->value(); + double min = this->min(); + double max = this->max(); + double low = this->low(); + double high = this->high(); + double optimum = this->optimum(); + + // 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); + else + m_meter_value_element->set_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); + else + m_meter_value_element->set_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); + else + m_meter_value_element->set_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)))); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h index 6bc108ff18..c95188c93f 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2020, the SerenityOS developers. * Copyright (c) 2022, Luke Wilde + * Copyright (c) 2023, Bastiaan van der Plaat * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,10 +9,38 @@ #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); @@ -19,7 +48,22 @@ class HTMLMeterElement final : public HTMLElement { public: virtual ~HTMLMeterElement() override; + double value() const; + WebIDL::ExceptionOr set_value(double); + double min() const; + WebIDL::ExceptionOr set_min(double value); + double max() const; + WebIDL::ExceptionOr set_max(double value); + double low() const; + WebIDL::ExceptionOr set_low(double value); + double high() const; + WebIDL::ExceptionOr set_high(double value); + double optimum() const; + WebIDL::ExceptionOr set_optimum(double value); + // ^HTMLElement + virtual void inserted() override; + // https://html.spec.whatwg.org/multipage/forms.html#category-label virtual bool is_labelable() const override { return true; } @@ -30,6 +74,13 @@ private: HTMLMeterElement(DOM::Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; + virtual void visit_edges(Cell::Visitor&) override; + + void create_shadow_tree_if_needed(); + + void update_meter_value_element(); + + JS::GCPtr m_meter_value_element; }; } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl index 0b63c71d99..c8ad0c262f 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLMeterElement.idl @@ -5,11 +5,11 @@ interface HTMLMeterElement : HTMLElement { [HTMLConstructor] constructor(); - // FIXME: [CEReactions] attribute double value; - // FIXME: [CEReactions] attribute double min; - // FIXME: [CEReactions] attribute double max; - // FIXME: [CEReactions] attribute double low; - // FIXME: [CEReactions] attribute double high; - // FIXME: [CEReactions] attribute double optimum; + [CEReactions] attribute double value; + [CEReactions] attribute double min; + [CEReactions] attribute double max; + [CEReactions] attribute double low; + [CEReactions] attribute double high; + [CEReactions] attribute double optimum; // FIXME: readonly attribute NodeList labels; }; diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 1c9605ec08..e9e47d7267 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -309,15 +309,21 @@ ErrorOr TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder:: if (is(dom_node)) { auto& element = static_cast(dom_node); - // Special path for ::placeholder, which corresponds to a synthetic DOM element inside the UA shadow root. + // 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) { - auto& input_element = verify_cast(*element.root().parent_or_shadow_host()); - style = TRY(style_computer.compute_style(input_element, CSS::Selector::PseudoElement::Placeholder)); - if (input_element.placeholder_value().has_value()) - display = style->display(); - else - display = CSS::Display::from_short(CSS::Display::Short::None); + 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) { + auto& parent_element = verify_cast(*element.root().parent_or_shadow_host()); + style = TRY(style_computer.compute_style(parent_element, element.pseudo_element())); + 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) { + 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 {