mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-20 03:25:13 +00:00
LibWeb: Avoid many style invalidations on DOM attribute mutation
Many times, attribute mutation doesn't necessitate a full style invalidation on the element. However, the conditions are pretty elaborate, so this first version has a lot of false positives. We only need to invalidate style when any of these things apply: 1. The change may affect the match state of a selector somewhere. 2. The change may affect presentational hints applied to the element. For (1) in this first version, we have a fixed list of attribute names that may affect selectors. We also collect all names referenced by attribute selectors anywhere in the document. For (2), we add a new Element::is_presentational_hint() virtual that tells us whether a given attribute name is a presentational hint. This drastically reduces style work on many websites. As an example, https://cnn.com/ is once again browseable.
This commit is contained in:
parent
b11bdd4022
commit
b981e6f7bc
Notes:
github-actions[bot]
2024-12-24 16:18:00 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/b981e6f7bc7 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3034
56 changed files with 377 additions and 37 deletions
|
@ -2047,8 +2047,14 @@ void StyleComputer::compute_font(ComputedProperties& style, DOM::Element const*
|
|||
|
||||
RefPtr<Gfx::Font const> const found_font = font_list->first();
|
||||
|
||||
style.set_property(CSS::PropertyID::FontSize, LengthStyleValue::create(CSS::Length::make_px(CSSPixels::nearest_value_for(found_font->pixel_size()))));
|
||||
style.set_property(CSS::PropertyID::FontWeight, NumberStyleValue::create(font_weight.to_font_weight()));
|
||||
style.set_property(
|
||||
CSS::PropertyID::FontSize,
|
||||
LengthStyleValue::create(CSS::Length::make_px(CSSPixels::nearest_value_for(found_font->pixel_size()))),
|
||||
style.is_property_inherited(CSS::PropertyID::FontSize) ? ComputedProperties::Inherited::Yes : ComputedProperties::Inherited::No);
|
||||
style.set_property(
|
||||
CSS::PropertyID::FontWeight,
|
||||
NumberStyleValue::create(font_weight.to_font_weight()),
|
||||
style.is_property_inherited(CSS::PropertyID::FontWeight) ? ComputedProperties::Inherited::Yes : ComputedProperties::Inherited::No);
|
||||
|
||||
style.set_computed_font_list(*font_list);
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ public:
|
|||
[[nodiscard]] GC::Ref<ComputedProperties> compute_properties(DOM::Element&, Optional<Selector::PseudoElement::Type>, CascadedProperties&) const;
|
||||
|
||||
void absolutize_values(ComputedProperties&) const;
|
||||
void compute_font(ComputedProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
|
||||
private:
|
||||
enum class ComputeStyleMode {
|
||||
|
@ -193,7 +194,6 @@ private:
|
|||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_ascending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||
static RefPtr<Gfx::FontCascadeList const> find_matching_font_weight_descending(Vector<MatchingFontCandidate> const& candidates, int target_weight, float font_size_in_pt, bool inclusive);
|
||||
RefPtr<Gfx::FontCascadeList const> font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const;
|
||||
void compute_font(ComputedProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_math_depth(ComputedProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void compute_defaulted_values(ComputedProperties&, DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>) const;
|
||||
void start_needed_transitions(ComputedProperties const& old_style, ComputedProperties& new_style, DOM::Element&, Optional<Selector::PseudoElement::Type>) const;
|
||||
|
|
|
@ -573,6 +573,7 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_inherited_style()
|
|||
invalidation |= CSS::compute_property_invalidation(property_id, old_value, new_value);
|
||||
}
|
||||
|
||||
document().style_computer().compute_font(*computed_properties, this, {});
|
||||
document().style_computer().absolutize_values(*computed_properties);
|
||||
|
||||
layout_node()->apply_style(*computed_properties);
|
||||
|
@ -1899,26 +1900,53 @@ ErrorOr<void> Element::scroll_into_view(Optional<Variant<bool, ScrollIntoViewOpt
|
|||
// FIXME: 8. Optionally perform some other action that brings the element to the user’s attention.
|
||||
}
|
||||
|
||||
static bool attribute_name_may_affect_selectors(Element const& element, FlyString const& attribute_name)
|
||||
{
|
||||
// FIXME: We could make these cases more narrow by making the conditions more elaborate.
|
||||
if (attribute_name == HTML::AttributeNames::id
|
||||
|| attribute_name == HTML::AttributeNames::class_
|
||||
|| attribute_name == HTML::AttributeNames::dir
|
||||
|| attribute_name == HTML::AttributeNames::lang
|
||||
|| attribute_name == HTML::AttributeNames::checked
|
||||
|| attribute_name == HTML::AttributeNames::disabled
|
||||
|| attribute_name == HTML::AttributeNames::readonly
|
||||
|| attribute_name == HTML::AttributeNames::switch_
|
||||
|| attribute_name == HTML::AttributeNames::href
|
||||
|| attribute_name == HTML::AttributeNames::open
|
||||
|| attribute_name == HTML::AttributeNames::placeholder) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return element.document().style_computer().has_attribute_selector(attribute_name);
|
||||
}
|
||||
|
||||
void Element::invalidate_style_after_attribute_change(FlyString const& attribute_name)
|
||||
{
|
||||
// FIXME: Only invalidate if the attribute can actually affect style.
|
||||
|
||||
// OPTIMIZATION: For the `style` attribute, unless it's referenced by an attribute selector,
|
||||
// only invalidate the element itself, then let inheritance propagate to descendants.
|
||||
if (attribute_name == HTML::AttributeNames::style
|
||||
&& !document().style_computer().has_attribute_selector(HTML::AttributeNames::style)) {
|
||||
set_needs_style_update(true);
|
||||
for_each_shadow_including_descendant([](Node& node) {
|
||||
if (!node.is_element())
|
||||
if (attribute_name == HTML::AttributeNames::style) {
|
||||
if (!document().style_computer().has_attribute_selector(HTML::AttributeNames::style)) {
|
||||
set_needs_style_update(true);
|
||||
for_each_shadow_including_descendant([](Node& node) {
|
||||
if (!node.is_element())
|
||||
return TraversalDecision::Continue;
|
||||
auto& element = static_cast<Element&>(node);
|
||||
element.set_needs_inherited_style_update(true);
|
||||
return TraversalDecision::Continue;
|
||||
auto& element = static_cast<Element&>(node);
|
||||
element.set_needs_inherited_style_update(true);
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
if (is_presentational_hint(attribute_name)
|
||||
|| attribute_name_may_affect_selectors(*this, attribute_name)) {
|
||||
invalidate_style(StyleInvalidationReason::ElementAttributeChange);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool Element::is_hidden() const
|
||||
|
|
|
@ -173,6 +173,7 @@ public:
|
|||
// https://html.spec.whatwg.org/multipage/embedded-content-other.html#dimension-attributes
|
||||
virtual bool supports_dimension_attributes() const { return false; }
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const { return false; }
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const { }
|
||||
|
||||
void run_attribute_change_steps(FlyString const& local_name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_);
|
||||
|
|
|
@ -71,6 +71,7 @@ enum class IsDescendant {
|
|||
X(ElementSetShadowRoot) \
|
||||
X(FocusedElementChange) \
|
||||
X(HTMLHyperlinkElementHrefChange) \
|
||||
X(HTMLIFrameElementGeometryChange) \
|
||||
X(HTMLInputElementSetChecked) \
|
||||
X(HTMLObjectElementUpdateLayoutAndChildObjects) \
|
||||
X(HTMLSelectElementSetIsOpen) \
|
||||
|
|
|
@ -41,6 +41,17 @@ void HTMLBodyElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLBodyElement);
|
||||
}
|
||||
|
||||
bool HTMLBodyElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::bgcolor,
|
||||
HTML::AttributeNames::text,
|
||||
HTML::AttributeNames::background);
|
||||
}
|
||||
|
||||
void HTMLBodyElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
virtual ~HTMLBodyElement() override;
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// https://www.w3.org/TR/html-aria/#el-body
|
||||
|
|
|
@ -65,6 +65,16 @@ void HTMLCanvasElement::visit_edges(Cell::Visitor& visitor)
|
|||
});
|
||||
}
|
||||
|
||||
bool HTMLCanvasElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::width,
|
||||
HTML::AttributeNames::height);
|
||||
}
|
||||
|
||||
void HTMLCanvasElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/rendering.html#attributes-for-embedded-content-and-images
|
||||
|
|
|
@ -52,6 +52,7 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;
|
||||
|
|
|
@ -21,6 +21,14 @@ HTMLDivElement::HTMLDivElement(DOM::Document& document, DOM::QualifiedName quali
|
|||
|
||||
HTMLDivElement::~HTMLDivElement() = default;
|
||||
|
||||
bool HTMLDivElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::align;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/rendering.html#flow-content-3
|
||||
void HTMLDivElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
|
|
|
@ -26,6 +26,7 @@ protected:
|
|||
|
||||
private:
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,19 @@ void HTMLEmbedElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLEmbedElement);
|
||||
}
|
||||
|
||||
bool HTMLEmbedElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::align,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::hspace,
|
||||
HTML::AttributeNames::vspace,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLEmbedElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -22,6 +22,7 @@ private:
|
|||
|
||||
virtual bool is_html_embed_element() const override { return true; }
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
|
||||
};
|
||||
|
|
|
@ -112,6 +112,17 @@ void HTMLFontElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLFontElement);
|
||||
}
|
||||
|
||||
bool HTMLFontElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::color,
|
||||
HTML::AttributeNames::face,
|
||||
HTML::AttributeNames::size);
|
||||
}
|
||||
|
||||
void HTMLFontElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -17,6 +17,7 @@ class HTMLFontElement final : public HTMLElement {
|
|||
public:
|
||||
virtual ~HTMLFontElement() override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -28,6 +28,14 @@ void HTMLHRElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLHRElement);
|
||||
}
|
||||
|
||||
bool HTMLHRElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name, HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLHRElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -26,6 +26,7 @@ private:
|
|||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,14 @@ void HTMLHeadingElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLHeadingElement);
|
||||
}
|
||||
|
||||
bool HTMLHeadingElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::align;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2
|
||||
void HTMLHeadingElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ class HTMLHeadingElement final : public HTMLElement {
|
|||
public:
|
||||
virtual ~HTMLHeadingElement() override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// https://www.w3.org/TR/html-aria/#el-h1-h6
|
||||
|
|
|
@ -60,6 +60,11 @@ void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional<String
|
|||
if (name == AttributeNames::srcdoc || (name == AttributeNames::src && !has_attribute(AttributeNames::srcdoc)))
|
||||
process_the_iframe_attributes();
|
||||
}
|
||||
|
||||
if (name == HTML::AttributeNames::width || name == HTML::AttributeNames::height) {
|
||||
// FIXME: This should only invalidate the layout, not the style.
|
||||
invalidate_style(DOM::StyleInvalidationReason::ElementAttributeChange);
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:html-element-post-connection-steps
|
||||
|
|
|
@ -82,6 +82,17 @@ void HTMLImageElement::visit_edges(Cell::Visitor& visitor)
|
|||
visit_lazy_loading_element(visitor);
|
||||
}
|
||||
|
||||
bool HTMLImageElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::hspace,
|
||||
HTML::AttributeNames::vspace,
|
||||
HTML::AttributeNames::border);
|
||||
}
|
||||
|
||||
void HTMLImageElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -121,6 +121,7 @@ private:
|
|||
|
||||
virtual void adopted_from(DOM::Document&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element:dimension-attributes
|
||||
|
|
|
@ -1593,6 +1593,23 @@ void HTMLInputElement::form_associated_element_was_removed(DOM::Node*)
|
|||
set_shadow_root(nullptr);
|
||||
}
|
||||
|
||||
bool HTMLInputElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
if (type_state() != TypeAttributeState::ImageButton)
|
||||
return false;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::align,
|
||||
HTML::AttributeNames::border,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::hspace,
|
||||
HTML::AttributeNames::vspace,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLInputElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
if (type_state() != TypeAttributeState::ImageButton)
|
||||
|
|
|
@ -227,6 +227,7 @@ private:
|
|||
|
||||
void type_attribute_changed(TypeAttributeState old_state, TypeAttributeState new_state);
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// ^DOM::Node
|
||||
|
|
|
@ -30,6 +30,19 @@ void HTMLMarqueeElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLMarqueeElement);
|
||||
}
|
||||
|
||||
bool HTMLMarqueeElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::bgcolor,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::hspace,
|
||||
HTML::AttributeNames::vspace,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLMarqueeElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
HTMLElement::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -30,6 +30,7 @@ private:
|
|||
HTMLMarqueeElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -100,6 +100,20 @@ void HTMLObjectElement::form_associated_element_was_removed(DOM::Node*)
|
|||
destroy_the_child_navigable();
|
||||
}
|
||||
|
||||
bool HTMLObjectElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::align,
|
||||
HTML::AttributeNames::border,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::hspace,
|
||||
HTML::AttributeNames::vspace,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLObjectElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -54,6 +54,7 @@ private:
|
|||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;
|
||||
|
|
|
@ -27,6 +27,14 @@ void HTMLParagraphElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLParagraphElement);
|
||||
}
|
||||
|
||||
bool HTMLParagraphElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::align;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2
|
||||
void HTMLParagraphElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ class HTMLParagraphElement final : public HTMLElement {
|
|||
public:
|
||||
virtual ~HTMLParagraphElement() override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// https://www.w3.org/TR/html-aria/#el-p
|
||||
|
|
|
@ -28,6 +28,14 @@ void HTMLPreElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLPreElement);
|
||||
}
|
||||
|
||||
bool HTMLPreElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::wrap;
|
||||
}
|
||||
|
||||
void HTMLPreElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
HTMLElement::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -26,6 +26,7 @@ private:
|
|||
HTMLPreElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,14 @@ void HTMLTableCaptionElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLTableCaptionElement);
|
||||
}
|
||||
|
||||
bool HTMLTableCaptionElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::align;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/rendering.html#tables-2
|
||||
void HTMLTableCaptionElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
|
|
|
@ -18,6 +18,7 @@ class HTMLTableCaptionElement final : public HTMLElement {
|
|||
public:
|
||||
virtual ~HTMLTableCaptionElement() override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
// https://www.w3.org/TR/html-aria/#el-caption
|
||||
|
|
|
@ -37,6 +37,20 @@ void HTMLTableCellElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLTableCellElement);
|
||||
}
|
||||
|
||||
bool HTMLTableCellElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::align,
|
||||
HTML::AttributeNames::background,
|
||||
HTML::AttributeNames::bgcolor,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::valign,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLTableCellElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -34,6 +34,7 @@ private:
|
|||
virtual bool is_html_table_cell_element() const override { return true; }
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,14 @@ WebIDL::ExceptionOr<void> HTMLTableColElement::set_span(unsigned int value)
|
|||
return set_attribute(HTML::AttributeNames::span, String::number(value));
|
||||
}
|
||||
|
||||
bool HTMLTableColElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return name == HTML::AttributeNames::width;
|
||||
}
|
||||
|
||||
void HTMLTableColElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -26,6 +26,7 @@ private:
|
|||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,22 @@ static unsigned parse_border(StringView value)
|
|||
return value.to_number<unsigned>().value_or(0);
|
||||
}
|
||||
|
||||
bool HTMLTableElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::align,
|
||||
HTML::AttributeNames::background,
|
||||
HTML::AttributeNames::bgcolor,
|
||||
HTML::AttributeNames::border,
|
||||
HTML::AttributeNames::cellpadding,
|
||||
HTML::AttributeNames::cellspacing,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::width);
|
||||
}
|
||||
|
||||
void HTMLTableElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -58,6 +58,7 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
||||
|
|
|
@ -39,6 +39,18 @@ void HTMLTableRowElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLTableRowElement);
|
||||
}
|
||||
|
||||
bool HTMLTableRowElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::bgcolor,
|
||||
HTML::AttributeNames::background,
|
||||
HTML::AttributeNames::height,
|
||||
HTML::AttributeNames::valign);
|
||||
}
|
||||
|
||||
void HTMLTableRowElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -35,6 +35,7 @@ private:
|
|||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
GC::Ptr<DOM::HTMLCollection> mutable m_cells;
|
||||
|
|
|
@ -100,6 +100,16 @@ WebIDL::ExceptionOr<void> HTMLTableSectionElement::delete_row(WebIDL::Long index
|
|||
return {};
|
||||
}
|
||||
|
||||
bool HTMLTableSectionElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
HTML::AttributeNames::background,
|
||||
HTML::AttributeNames::bgcolor);
|
||||
}
|
||||
|
||||
void HTMLTableSectionElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
|
|
|
@ -37,6 +37,7 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
GC::Ptr<DOM::HTMLCollection> mutable m_rows;
|
||||
|
|
|
@ -29,6 +29,17 @@ void SVGCircleElement::initialize(JS::Realm& realm)
|
|||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGCircleElement);
|
||||
}
|
||||
|
||||
bool SVGCircleElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
SVG::AttributeNames::cx,
|
||||
SVG::AttributeNames::cy,
|
||||
SVG::AttributeNames::r);
|
||||
}
|
||||
|
||||
void SVGCircleElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -18,6 +18,7 @@ class SVGCircleElement final : public SVGGeometryElement {
|
|||
public:
|
||||
virtual ~SVGCircleElement() override = default;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;
|
||||
|
|
|
@ -52,6 +52,16 @@ GC::Ptr<Layout::Node> SVGForeignObjectElement::create_layout_node(GC::Ref<CSS::C
|
|||
return heap().allocate<Layout::SVGForeignObjectBox>(document(), *this, move(style));
|
||||
}
|
||||
|
||||
bool SVGForeignObjectElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
SVG::AttributeNames::width,
|
||||
SVG::AttributeNames::height);
|
||||
}
|
||||
|
||||
void SVGForeignObjectElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -31,6 +31,7 @@ private:
|
|||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
GC::Ptr<SVG::SVGAnimatedLength> m_x;
|
||||
|
|
|
@ -143,32 +143,40 @@ struct NamedPropertyID {
|
|||
StringView name;
|
||||
};
|
||||
|
||||
static Array const attribute_style_properties {
|
||||
// FIXME: The `fill` attribute and CSS `fill` property are not the same! But our support is limited enough that they are equivalent for now.
|
||||
NamedPropertyID(CSS::PropertyID::Fill),
|
||||
// FIXME: The `stroke` attribute and CSS `stroke` property are not the same! But our support is limited enough that they are equivalent for now.
|
||||
NamedPropertyID(CSS::PropertyID::Stroke),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeDasharray),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeDashoffset),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinecap),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeWidth),
|
||||
NamedPropertyID(CSS::PropertyID::FillRule),
|
||||
NamedPropertyID(CSS::PropertyID::FillOpacity),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeOpacity),
|
||||
NamedPropertyID(CSS::PropertyID::Opacity),
|
||||
NamedPropertyID(CSS::PropertyID::TextAnchor),
|
||||
NamedPropertyID(CSS::PropertyID::FontSize),
|
||||
NamedPropertyID(CSS::PropertyID::Mask),
|
||||
NamedPropertyID(CSS::PropertyID::MaskType),
|
||||
NamedPropertyID(CSS::PropertyID::ClipPath),
|
||||
NamedPropertyID(CSS::PropertyID::ClipRule),
|
||||
NamedPropertyID(CSS::PropertyID::Display),
|
||||
};
|
||||
|
||||
bool SVGGraphicsElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return any_of(attribute_style_properties, [&](auto& property) { return name.equals_ignoring_ascii_case(property.name); });
|
||||
}
|
||||
|
||||
void SVGGraphicsElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
static Array const attribute_style_properties {
|
||||
// FIXME: The `fill` attribute and CSS `fill` property are not the same! But our support is limited enough that they are equivalent for now.
|
||||
NamedPropertyID(CSS::PropertyID::Fill),
|
||||
// FIXME: The `stroke` attribute and CSS `stroke` property are not the same! But our support is limited enough that they are equivalent for now.
|
||||
NamedPropertyID(CSS::PropertyID::Stroke),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeDasharray),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeDashoffset),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinecap),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeLinejoin),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeMiterlimit),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeWidth),
|
||||
NamedPropertyID(CSS::PropertyID::FillRule),
|
||||
NamedPropertyID(CSS::PropertyID::FillOpacity),
|
||||
NamedPropertyID(CSS::PropertyID::StrokeOpacity),
|
||||
NamedPropertyID(CSS::PropertyID::Opacity),
|
||||
NamedPropertyID(CSS::PropertyID::TextAnchor),
|
||||
NamedPropertyID(CSS::PropertyID::FontSize),
|
||||
NamedPropertyID(CSS::PropertyID::Mask),
|
||||
NamedPropertyID(CSS::PropertyID::MaskType),
|
||||
NamedPropertyID(CSS::PropertyID::ClipPath),
|
||||
NamedPropertyID(CSS::PropertyID::ClipRule),
|
||||
NamedPropertyID(CSS::PropertyID::Display),
|
||||
};
|
||||
|
||||
CSS::Parser::ParsingContext parsing_context { document(), CSS::Parser::ParsingContext::Mode::SVGPresentationAttribute };
|
||||
for_each_attribute([&](auto& name, auto& value) {
|
||||
for (auto property : attribute_style_properties) {
|
||||
|
|
|
@ -29,6 +29,7 @@ class SVGGraphicsElement : public SVGElement {
|
|||
WEB_PLATFORM_OBJECT(SVGGraphicsElement, SVGElement);
|
||||
|
||||
public:
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
||||
|
|
|
@ -79,6 +79,20 @@ RefPtr<CSS::CSSStyleValue> SVGSVGElement::height_style_value_from_attribute() co
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool SVGSVGElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
SVG::AttributeNames::x,
|
||||
SVG::AttributeNames::y,
|
||||
SVG::AttributeNames::width,
|
||||
SVG::AttributeNames::height,
|
||||
SVG::AttributeNames::viewBox,
|
||||
SVG::AttributeNames::preserveAspectRatio);
|
||||
}
|
||||
|
||||
void SVGSVGElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
|
|
@ -31,6 +31,7 @@ class SVGSVGElement final : public SVGGraphicsElement
|
|||
public:
|
||||
virtual GC::Ptr<Layout::Node> create_layout_node(GC::Ref<CSS::ComputedProperties>) override;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual bool requires_svg_container() const override { return false; }
|
||||
|
|
|
@ -30,6 +30,16 @@ void SVGStopElement::attribute_changed(FlyString const& name, Optional<String> c
|
|||
}
|
||||
}
|
||||
|
||||
bool SVGStopElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
return first_is_one_of(name,
|
||||
"stop-color"sv,
|
||||
"stop-opacity"sv);
|
||||
}
|
||||
|
||||
void SVGStopElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
CSS::Parser::ParsingContext parsing_context { document() };
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
|
||||
GC::Ref<SVGAnimatedNumber> offset() const;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
NumberPercentage stop_offset() const { return m_offset.value_or(NumberPercentage::create_number(0)); }
|
||||
|
|
|
@ -39,11 +39,24 @@ void SVGSymbolElement::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_view_box_for_bindings);
|
||||
}
|
||||
|
||||
bool SVGSymbolElement::is_presentational_hint(FlyString const& name) const
|
||||
{
|
||||
if (Base::is_presentational_hint(name))
|
||||
return true;
|
||||
|
||||
// FIXME: This is not a correct use of the presentational hint mechanism.
|
||||
if (is_direct_child_of_use_shadow_tree())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://svgwg.org/svg2-draft/struct.html#SymbolNotes
|
||||
void SVGSymbolElement::apply_presentational_hints(GC::Ref<CSS::CascadedProperties> cascaded_properties) const
|
||||
{
|
||||
Base::apply_presentational_hints(cascaded_properties);
|
||||
|
||||
// FIXME: This is not a correct use of the presentational hint mechanism.
|
||||
if (is_direct_child_of_use_shadow_tree()) {
|
||||
// The generated instance of a ‘symbol’ that is the direct referenced element of a ‘use’ element must always have a computed value of inline for the display property.
|
||||
cascaded_properties->set_property_from_presentational_hint(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::Inline)));
|
||||
|
|
|
@ -19,6 +19,7 @@ class SVGSymbolElement final : public SVGGraphicsElement
|
|||
public:
|
||||
virtual ~SVGSymbolElement() override = default;
|
||||
|
||||
virtual bool is_presentational_hint(FlyString const&) const override;
|
||||
virtual void apply_presentational_hints(GC::Ref<CSS::CascadedProperties>) const override;
|
||||
|
||||
virtual Optional<ViewBox> view_box() const override { return m_view_box; }
|
||||
|
|
Loading…
Add table
Reference in a new issue