1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:14:58 +00:00

LibWeb: Add internal use_pseudo_element to DOM Element

This commit is contained in:
Bastiaan van der Plaat 2023-12-10 12:42:13 +01:00 committed by Andreas Kling
parent cfe9577b48
commit f621dc464b
12 changed files with 61 additions and 96 deletions

View file

@ -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 <input> at (11,11) content-size 200x25.84375 inline-block [BFC] children: not-inline
Box <div> 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 <div>
TextNode <#text>
BlockContainer <div> 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<BODY>) [9,9 782x29.84375]
PaintableWithLines (BlockContainer<INPUT>) [10,10 202x27.84375]
PaintableBox (Box<DIV>) [11,11 200x25.84375]
PaintableWithLines (BlockContainer(anonymous)) [13,23.921875 0x0]
InlinePaintable (InlineNode<DIV>)
PaintableWithLines (BlockContainer<DIV>) [13,12 196x23.84375]

View file

@ -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> ElementInlineCSSStyleDeclaration::create(DOM::Element& element, Vector<StyleProperty> properties, HashMap<FlyString, StyleProperty> custom_properties)
{
auto& realm = element.realm();

View file

@ -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<StyleProperty> 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<StyleProperty> property(PropertyID) const override;

View file

@ -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<PropertyID>(index + to_underlying(first_longhand_property_id));
}
static NonnullRefPtr<StyleValue const> style_value_for_background_property(Layout::NodeWithStyle const& layout_node, Function<NonnullRefPtr<StyleValue const>(BackgroundLayerData const&)> callback, Function<NonnullRefPtr<StyleValue const>()> default_value)
{
auto const& background_layers = layout_node.background_layers();

View file

@ -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<StyleProperty> property(PropertyID) const override;
virtual WebIDL::ExceptionOr<void> set_property(PropertyID, StringView css_text, StringView priority) override;
virtual WebIDL::ExceptionOr<String> remove_property(PropertyID) override;

View file

@ -173,7 +173,8 @@ public:
RequiredInvalidationAfterStyleChange recompute_style();
virtual Optional<CSS::Selector::PseudoElement> pseudo_element() const { return {}; }
Optional<CSS::Selector::PseudoElement> use_pseudo_element() const { return m_use_pseudo_element; }
void set_use_pseudo_element(Optional<CSS::Selector::PseudoElement> 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<PseudoElementCustomProperties> m_pseudo_element_custom_properties;
PseudoElementCustomProperties& pseudo_element_custom_properties() const;
Optional<CSS::Selector::PseudoElement> m_use_pseudo_element {};
Vector<FlyString> m_classes;
Optional<Dir> m_dir;

View file

@ -524,20 +524,6 @@ Optional<DeprecatedString> 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<CSS::Selector::PseudoElement> 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<PlaceholderElement>(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;

View file

@ -6,9 +6,11 @@
*/
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/HTMLMeterElement.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/Namespace.h>
namespace Web::HTML {
@ -188,10 +190,11 @@ void HTMLMeterElement::create_shadow_tree_if_needed()
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
set_shadow_root(shadow_root);
auto meter_bar_element = heap().allocate<MeterBarElement>(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<MeterValueElement>(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))));
}
}

View file

@ -9,39 +9,10 @@
#pragma once
#include <LibWeb/ARIA/Roles.h>
#include <LibWeb/HTML/HTMLDivElement.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/Namespace.h>
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<CSS::Selector::PseudoElement> 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<CSS::Selector::PseudoElement> 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<MeterValueElement> m_meter_value_element;
JS::GCPtr<DOM::Element> m_meter_value_element;
};
}

View file

@ -7,9 +7,11 @@
*/
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/DOM/ShadowRoot.h>
#include <LibWeb/HTML/HTMLProgressElement.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/Namespace.h>
namespace Web::HTML {
@ -102,17 +104,19 @@ void HTMLProgressElement::create_shadow_tree_if_needed()
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
set_shadow_root(shadow_root);
auto progress_bar_element = heap().allocate<ProgressBarElement>(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<ProgressValueElement>(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))));
}
}

View file

@ -8,34 +8,10 @@
#pragma once
#include <LibWeb/ARIA/Roles.h>
#include <LibWeb/HTML/HTMLDivElement.h>
#include <LibWeb/HTML/HTMLElement.h>
#include <LibWeb/Namespace.h>
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<CSS::Selector::PseudoElement> 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<CSS::Selector::PseudoElement> 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<ProgressValueElement> m_progress_value_element;
JS::GCPtr<DOM::Element> m_progress_value_element;
};
}

View file

@ -307,21 +307,23 @@ ErrorOr<void> TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::
if (is<DOM::Element>(dom_node)) {
auto& element = static_cast<DOM::Element&>(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<HTML::HTMLElement>(*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<HTML::HTMLInputElement>(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 {