mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-26 12:17:52 +00:00
LibWeb: Allow style inheritance through slots
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
When a subtree is projected through a slot, its root now inherits style from the slot's parent, rather than the parent of the unprojected root. This fixes a ton of subtle issues, and is very noticeable on Reddit.
This commit is contained in:
parent
ba62679a7a
commit
9208a54b0b
Notes:
github-actions[bot]
2025-08-16 19:04:51 +00:00
Author: https://github.com/awesomekling
Commit: 9208a54b0b
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5873
7 changed files with 64 additions and 34 deletions
|
@ -81,6 +81,7 @@
|
|||
#include <LibWeb/Fetch/Response.h>
|
||||
#include <LibWeb/HTML/HTMLBRElement.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/HTML/HTMLSlotElement.h>
|
||||
#include <LibWeb/HTML/Parser/HTMLParser.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
|
@ -180,8 +181,6 @@ OwnFontFaceKey::operator FontFaceKey() const
|
|||
&& slope == other.slope;
|
||||
}
|
||||
|
||||
static DOM::Element const* element_to_inherit_style_from(DOM::Element const*, Optional<CSS::PseudoElement>);
|
||||
|
||||
StyleComputer::StyleComputer(DOM::Document& document)
|
||||
: m_document(document)
|
||||
, m_default_font_metrics(16, Platform::FontPlugin::the().default_font(16)->pixel_metrics())
|
||||
|
@ -1665,21 +1664,9 @@ GC::Ref<CascadedProperties> StyleComputer::compute_cascaded_values(DOM::Element&
|
|||
return cascaded_properties;
|
||||
}
|
||||
|
||||
DOM::Element const* element_to_inherit_style_from(DOM::Element const* element, Optional<CSS::PseudoElement> pseudo_element)
|
||||
{
|
||||
// Pseudo-elements treat their originating element as their parent.
|
||||
DOM::Element const* parent_element = nullptr;
|
||||
if (pseudo_element.has_value()) {
|
||||
parent_element = element;
|
||||
} else if (element) {
|
||||
parent_element = element->parent_or_shadow_host_element();
|
||||
}
|
||||
return parent_element;
|
||||
}
|
||||
|
||||
NonnullRefPtr<StyleValue const> StyleComputer::get_inherit_value(CSS::PropertyID property_id, DOM::Element const* element, Optional<CSS::PseudoElement> pseudo_element)
|
||||
{
|
||||
auto* parent_element = element_to_inherit_style_from(element, pseudo_element);
|
||||
auto parent_element = element ? element->element_to_inherit_style_from(pseudo_element) : nullptr;
|
||||
|
||||
if (!parent_element || !parent_element->computed_properties())
|
||||
return property_initial_value(property_id);
|
||||
|
@ -1751,7 +1738,7 @@ void StyleComputer::compute_defaulted_values(ComputedProperties& style, DOM::Ele
|
|||
if (element && element->local_name() == HTML::TagNames::th
|
||||
&& style.property(PropertyID::TextAlign).to_keyword() == Keyword::LibwebInheritOrCenter) {
|
||||
auto const* parent_element = element;
|
||||
while ((parent_element = element_to_inherit_style_from(parent_element, {}))) {
|
||||
while ((parent_element = parent_element->element_to_inherit_style_from({}))) {
|
||||
auto parent_computed = parent_element->computed_properties();
|
||||
auto parent_cascaded = parent_element->cascaded_properties({});
|
||||
if (!parent_computed || !parent_cascaded)
|
||||
|
@ -1911,7 +1898,7 @@ CSSPixelFraction StyleComputer::absolute_size_mapping(Keyword keyword)
|
|||
|
||||
RefPtr<Gfx::FontCascadeList const> StyleComputer::compute_font_for_style_values(DOM::Element const* element, Optional<CSS::PseudoElement> pseudo_element, StyleValue const& font_family, StyleValue const& font_size, StyleValue const& font_style, StyleValue const& font_weight, StyleValue const& font_stretch, int math_depth) const
|
||||
{
|
||||
auto* parent_element = element_to_inherit_style_from(element, pseudo_element);
|
||||
auto parent_element = element ? element->element_to_inherit_style_from(pseudo_element) : nullptr;
|
||||
|
||||
auto width = font_stretch.to_font_width();
|
||||
auto weight = font_weight.to_font_weight();
|
||||
|
@ -2180,7 +2167,7 @@ LogicalAliasMappingContext StyleComputer::compute_logical_alias_mapping_context(
|
|||
{
|
||||
auto normalize_value = [&](auto property_id, auto value) {
|
||||
if (!value || value->is_inherit() || value->is_unset()) {
|
||||
if (auto const* inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) {
|
||||
if (auto const inheritance_parent = element.element_to_inherit_style_from(pseudo_element)) {
|
||||
value = inheritance_parent->computed_properties()->property(property_id);
|
||||
} else {
|
||||
value = property_initial_value(property_id);
|
||||
|
@ -2297,14 +2284,7 @@ static void compute_text_align(ComputedProperties& style, DOM::Element const& el
|
|||
// value of start or end is interpreted against the parent’s direction value and results in a computed value of
|
||||
// either left or right. Computes to start when specified on the root element.
|
||||
if (style.property(PropertyID::TextAlign).to_keyword() == Keyword::MatchParent) {
|
||||
|
||||
// If it's a pseudo-element, then the "parent" is the originating element instead.
|
||||
auto const* parent = [&]() -> DOM::Element const* {
|
||||
if (pseudo_element.has_value())
|
||||
return &element;
|
||||
return element.parent_element();
|
||||
}();
|
||||
|
||||
auto const parent = element.element_to_inherit_style_from(pseudo_element);
|
||||
if (parent) {
|
||||
auto const& parent_text_align = parent->computed_properties()->property(PropertyID::TextAlign);
|
||||
auto const& parent_direction = parent->computed_properties()->direction();
|
||||
|
@ -2355,7 +2335,7 @@ static BoxTypeTransformation required_box_type_transformation(ComputedProperties
|
|||
// FIXME: Containment in a ruby container inlinifies the box’s display type, as described in [CSS-RUBY-1].
|
||||
|
||||
// NOTE: If we're computing style for a pseudo-element, the effective parent will be the originating element itself, not its parent.
|
||||
auto parent = pseudo_element.has_value() ? GC::Ptr<DOM::Element const> { &element } : element.parent_element();
|
||||
auto parent = element.element_to_inherit_style_from(pseudo_element);
|
||||
|
||||
// A parent with a grid or flex display value blockifies the box’s display type. [CSS-GRID-1] [CSS-FLEXBOX-1]
|
||||
if (parent && parent->computed_properties()) {
|
||||
|
@ -2597,10 +2577,10 @@ RefPtr<StyleValue const> StyleComputer::recascade_font_size_if_needed(
|
|||
|
||||
// Reconstruct the line of ancestor elements we need to inherit style from, and then do the cascade again
|
||||
// but only for the font-size property.
|
||||
Vector<DOM::Element&> ancestors;
|
||||
Vector<DOM::Element const&> ancestors;
|
||||
if (pseudo_element.has_value())
|
||||
ancestors.append(element);
|
||||
for (auto ancestor = element.parent_element(); ancestor; ancestor = ancestor->parent_element())
|
||||
for (auto ancestor = element.element_to_inherit_style_from(pseudo_element); ancestor; ancestor = ancestor->element_to_inherit_style_from({}))
|
||||
ancestors.append(*ancestor);
|
||||
|
||||
NonnullRefPtr<StyleValue const> new_font_size = CSS::LengthStyleValue::create(CSS::Length::make_px(default_monospace_font_size_in_px));
|
||||
|
@ -2664,7 +2644,7 @@ GC::Ref<ComputedProperties> StyleComputer::compute_properties(DOM::Element& elem
|
|||
// FIXME: Logical properties should inherit from their parent's equivalent unmapped logical property.
|
||||
if ((!value && is_inherited_property(property_id))
|
||||
|| (value && value->is_inherit())) {
|
||||
if (auto inheritance_parent = element_to_inherit_style_from(&element, pseudo_element)) {
|
||||
if (auto const inheritance_parent = element.element_to_inherit_style_from(pseudo_element)) {
|
||||
value = inheritance_parent->computed_properties()->property(property_id);
|
||||
inherited = ComputedProperties::Inherited::Yes;
|
||||
} else {
|
||||
|
@ -3183,9 +3163,9 @@ NonnullRefPtr<StyleValue const> StyleComputer::compute_value_of_custom_property(
|
|||
// Unset is the same as inherit for inherited properties, and by default all custom properties are inherited.
|
||||
// FIXME: Support non-inherited registered custom properties.
|
||||
if (value->is_inherit() || value->is_unset()) {
|
||||
if (!abstract_element.parent_element())
|
||||
if (!abstract_element.element_to_inherit_style_from())
|
||||
return document.custom_property_initial_value(name);
|
||||
auto inherited_value = DOM::AbstractElement { const_cast<DOM::Element&>(*abstract_element.parent_element()) }.get_custom_property(name);
|
||||
auto inherited_value = DOM::AbstractElement { const_cast<DOM::Element&>(*abstract_element.element_to_inherit_style_from()) }.get_custom_property(name);
|
||||
if (!inherited_value)
|
||||
return document.custom_property_initial_value(name);
|
||||
return inherited_value.release_nonnull();
|
||||
|
@ -3235,9 +3215,9 @@ void StyleComputer::compute_math_depth(ComputedProperties& style, DOM::Element c
|
|||
compute_defaulted_property_value(style, element, CSS::PropertyID::MathStyle, pseudo_element);
|
||||
|
||||
auto inherited_math_depth = [&]() {
|
||||
if (!element || !element->parent_element())
|
||||
if (!element || !element->element_to_inherit_style_from(pseudo_element))
|
||||
return InitialValues::math_depth();
|
||||
return element->parent_element()->computed_properties()->math_depth();
|
||||
return element->element_to_inherit_style_from(pseudo_element)->computed_properties()->math_depth();
|
||||
};
|
||||
|
||||
auto const& value = style.property(CSS::PropertyID::MathDepth);
|
||||
|
|
|
@ -40,6 +40,11 @@ GC::Ptr<Element const> AbstractElement::parent_element() const
|
|||
return m_element->parent_element();
|
||||
}
|
||||
|
||||
GC::Ptr<Element const> AbstractElement::element_to_inherit_style_from() const
|
||||
{
|
||||
return m_element->element_to_inherit_style_from(m_pseudo_element);
|
||||
}
|
||||
|
||||
Optional<AbstractElement> AbstractElement::walk_layout_tree(WalkMethod walk_method)
|
||||
{
|
||||
GC::Ptr<Layout::Node> node = layout_node();
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
GC::Ptr<Layout::NodeWithStyle const> layout_node() const { return const_cast<AbstractElement*>(this)->layout_node(); }
|
||||
|
||||
GC::Ptr<Element const> parent_element() const;
|
||||
GC::Ptr<Element const> element_to_inherit_style_from() const;
|
||||
Optional<AbstractElement> previous_in_tree_order() { return walk_layout_tree(WalkMethod::Previous); }
|
||||
Optional<AbstractElement> previous_sibling_in_tree_order() { return walk_layout_tree(WalkMethod::PreviousSibling); }
|
||||
bool is_before(AbstractElement const&) const;
|
||||
|
|
|
@ -4131,4 +4131,17 @@ GC::Ref<CSS::StylePropertyMapReadOnly> Element::computed_style_map()
|
|||
return *m_computed_style_map_cache;
|
||||
}
|
||||
|
||||
// The element to inherit style from.
|
||||
// If a pseudo-element is specified, this will return the element itself.
|
||||
// Otherwise, if this element is slotted somewhere, it will return the slot's element to inherit style from.
|
||||
// Otherwise, it will return the parent or shadow host element of this element.
|
||||
GC::Ptr<Element const> Element::element_to_inherit_style_from(Optional<CSS::PseudoElement> pseudo_element) const
|
||||
{
|
||||
if (pseudo_element.has_value())
|
||||
return this;
|
||||
while (auto const slot = assigned_slot_internal())
|
||||
return slot->element_to_inherit_style_from({});
|
||||
return parent_or_shadow_host_element();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -233,6 +233,8 @@ public:
|
|||
|
||||
WebIDL::ExceptionOr<GC::Ref<DOM::DocumentFragment>> parse_fragment(StringView markup);
|
||||
|
||||
[[nodiscard]] GC::Ptr<Element const> element_to_inherit_style_from(Optional<CSS::PseudoElement>) const;
|
||||
|
||||
WebIDL::ExceptionOr<String> inner_html() const;
|
||||
WebIDL::ExceptionOr<void> set_inner_html(StringView);
|
||||
|
||||
|
|
20
Tests/LibWeb/Layout/expected/slot-style-inheritance.txt
Normal file
20
Tests/LibWeb/Layout/expected/slot-style-inheritance.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x54 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x38 children: not-inline
|
||||
Box <main> at (13,13) content-size 774x28 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <foobar> at (13,13) content-size 52.5625x28 flex-item [BFC] children: not-inline
|
||||
BlockContainer <div> at (18,18) content-size 42.5625x18 children: inline
|
||||
frag 0 from TextNode start: 0, length: 5, rect: [18,18 42.5625x18] baseline: 13.796875
|
||||
"story"
|
||||
TextNode <#text>
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x54]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x38]
|
||||
PaintableBox (Box<MAIN>) [8,8 784x38]
|
||||
PaintableWithLines (BlockContainer<FOOBAR>) [13,13 52.5625x28]
|
||||
PaintableWithLines (BlockContainer<DIV>) [13,13 52.5625x28]
|
||||
TextPaintable (TextNode<#text>)
|
||||
|
||||
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
|
||||
SC for BlockContainer<HTML> [0,0 800x54] [children: 0] (z-index: auto)
|
9
Tests/LibWeb/Layout/input/slot-style-inheritance.html
Normal file
9
Tests/LibWeb/Layout/input/slot-style-inheritance.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!doctype html><style>
|
||||
* { outline: 1px solid black; }
|
||||
div { border: 5px solid lime; }
|
||||
</style><body><template shadowrootmode="open"><style>
|
||||
main {
|
||||
display: flex;
|
||||
border: 5px solid orange;
|
||||
}
|
||||
</style><main><slot></slot></main></template><foobar><div>story
|
Loading…
Add table
Add a link
Reference in a new issue