LibWeb: Add style invalidation for :target, :focus, :active and :link

This commit is contained in:
Andreas Kling 2024-11-06 18:00:18 +01:00 committed by Andreas Kling
commit 92d9907f8f
Notes: github-actions[bot] 2024-11-06 20:43:56 +00:00
9 changed files with 39 additions and 23 deletions

View file

@ -6,18 +6,18 @@ Rerun
Found 12 tests Found 12 tests
1 Pass 9 Pass
11 Fail 3 Fail
Details Details
Result Test Name MessagePass Initial State Result Test Name MessagePass Initial State
Fail Focus 'target1' Pass Focus 'target1'
Fail Focus 'target2' Pass Focus 'target2'
Fail Focus 'target1' again Pass Focus 'target1' again
Fail Focus 'target2' again Pass Focus 'target2' again
Fail Focus 'target1' once again Pass Focus 'target1' once again
Fail Detach 'container1' from the document Fail Detach 'container1' from the document
Fail Try to focus 'target1' Fail Try to focus 'target1'
Fail Focus 'target2' once again Pass Focus 'target2' once again
Fail Attach 'container1' in 'container2' Pass Attach 'container1' in 'container2'
Fail Focus 'target1' for the last time Pass Focus 'target1' for the last time
Fail Move 'target1' in 'container2' Fail Move 'target1' in 'container2'

View file

@ -6,6 +6,6 @@ Rerun
Found 1 tests Found 1 tests
1 Fail 1 Pass
Details Details
Result Test Name MessageFail CSS Selectors Invalidation: :target pseudo-class in :has() argument Result Test Name MessagePass CSS Selectors Invalidation: :target pseudo-class in :has() argument

View file

@ -2045,16 +2045,18 @@ void Document::set_focused_element(Element* element)
if (m_focused_element.ptr() == element) if (m_focused_element.ptr() == element)
return; return;
if (m_focused_element) { JS::GCPtr<Element> old_focused_element = move(m_focused_element);
m_focused_element->did_lose_focus();
m_focused_element->set_needs_style_update(true); if (old_focused_element)
} old_focused_element->did_lose_focus();
m_focused_element = element; m_focused_element = element;
if (auto* invalidation_target = find_common_ancestor(old_focused_element, m_focused_element) ?: this)
invalidation_target->invalidate_style(StyleInvalidationReason::FocusedElementChange);
if (m_focused_element) { if (m_focused_element) {
m_focused_element->did_receive_focus(); m_focused_element->did_receive_focus();
m_focused_element->set_needs_style_update(true);
} }
if (paintable()) if (paintable())
@ -2078,8 +2080,12 @@ void Document::set_active_element(Element* element)
if (m_active_element.ptr() == element) if (m_active_element.ptr() == element)
return; return;
JS::GCPtr<Node> old_active_element = move(m_active_element);
m_active_element = element; m_active_element = element;
if (auto* invalidation_target = find_common_ancestor(old_active_element, m_active_element) ?: this)
invalidation_target->invalidate_style(StyleInvalidationReason::TargetElementChange);
if (paintable()) if (paintable())
paintable()->set_needs_display(); paintable()->set_needs_display();
} }
@ -2089,13 +2095,11 @@ void Document::set_target_element(Element* element)
if (m_target_element.ptr() == element) if (m_target_element.ptr() == element)
return; return;
if (m_target_element) JS::GCPtr<Element> old_target_element = move(m_target_element);
m_target_element->set_needs_style_update(true);
m_target_element = element; m_target_element = element;
if (m_target_element) if (auto* invalidation_target = find_common_ancestor(old_target_element, m_target_element) ?: this)
m_target_element->set_needs_style_update(true); invalidation_target->invalidate_style(StyleInvalidationReason::TargetElementChange);
if (paintable()) if (paintable())
paintable()->set_needs_display(); paintable()->set_needs_display();

View file

@ -59,6 +59,7 @@ enum class IsDescendant {
}; };
#define ENUMERATE_STYLE_INVALIDATION_REASONS(X) \ #define ENUMERATE_STYLE_INVALIDATION_REASONS(X) \
X(ActiveElementChange) \
X(AdoptedStyleSheetsList) \ X(AdoptedStyleSheetsList) \
X(CSSFontLoaded) \ X(CSSFontLoaded) \
X(CSSImportRule) \ X(CSSImportRule) \
@ -68,6 +69,8 @@ enum class IsDescendant {
X(EditingInsertion) \ X(EditingInsertion) \
X(ElementAttributeChange) \ X(ElementAttributeChange) \
X(ElementSetShadowRoot) \ X(ElementSetShadowRoot) \
X(FocusedElementChange) \
X(HTMLHyperlinkElementHrefChange) \
X(HTMLInputElementSetChecked) \ X(HTMLInputElementSetChecked) \
X(HTMLObjectElementUpdateLayoutAndChildObjects) \ X(HTMLObjectElementUpdateLayoutAndChildObjects) \
X(HTMLSelectElementSetIsOpen) \ X(HTMLSelectElementSetIsOpen) \
@ -84,7 +87,8 @@ enum class IsDescendant {
X(StyleSheetDeleteRule) \ X(StyleSheetDeleteRule) \
X(StyleSheetInsertRule) \ X(StyleSheetInsertRule) \
X(StyleSheetListAddSheet) \ X(StyleSheetListAddSheet) \
X(StyleSheetListRemoveSheet) X(StyleSheetListRemoveSheet) \
X(TargetElementChange)
enum class StyleInvalidationReason { enum class StyleInvalidationReason {
#define __ENUMERATE_STYLE_INVALIDATION_REASON(reason) reason, #define __ENUMERATE_STYLE_INVALIDATION_REASON(reason) reason,

View file

@ -51,6 +51,7 @@ private:
// ^HTML::HTMLHyperlinkElementUtils // ^HTML::HTMLHyperlinkElementUtils
virtual DOM::Document& hyperlink_element_utils_document() override { return document(); } virtual DOM::Document& hyperlink_element_utils_document() override { return document(); }
virtual DOM::Element& hyperlink_element_utils_element() override { return *this; }
virtual Optional<String> hyperlink_element_utils_href() const override; virtual Optional<String> hyperlink_element_utils_href() const override;
virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) override; virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) override;
virtual Optional<String> hyperlink_element_utils_referrerpolicy() const override; virtual Optional<String> hyperlink_element_utils_referrerpolicy() const override;

View file

@ -34,6 +34,7 @@ private:
// ^HTML::HTMLHyperlinkElementUtils // ^HTML::HTMLHyperlinkElementUtils
virtual DOM::Document& hyperlink_element_utils_document() override { return document(); } virtual DOM::Document& hyperlink_element_utils_document() override { return document(); }
virtual DOM::Element& hyperlink_element_utils_element() override { return *this; }
virtual Optional<String> hyperlink_element_utils_href() const override; virtual Optional<String> hyperlink_element_utils_href() const override;
virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) override; virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) override;
virtual Optional<String> hyperlink_element_utils_referrerpolicy() const override; virtual Optional<String> hyperlink_element_utils_referrerpolicy() const override;

View file

@ -31,12 +31,14 @@ void HTMLHyperlinkElementUtils::set_the_url()
auto href_content_attribute = hyperlink_element_utils_href(); auto href_content_attribute = hyperlink_element_utils_href();
if (!href_content_attribute.has_value()) { if (!href_content_attribute.has_value()) {
m_url = {}; m_url = {};
hyperlink_element_utils_element().invalidate_style(DOM::StyleInvalidationReason::HTMLHyperlinkElementHrefChange);
return; return;
} }
// 2. Otherwise, parse this element's href content attribute value relative to this element's node document. // 2. Otherwise, parse this element's href content attribute value relative to this element's node document.
// If parsing is successful, set this element's url to the result; otherwise, set this element's url to null. // If parsing is successful, set this element's url to the result; otherwise, set this element's url to null.
m_url = hyperlink_element_utils_document().parse_url(*href_content_attribute); m_url = hyperlink_element_utils_document().parse_url(*href_content_attribute);
hyperlink_element_utils_element().invalidate_style(DOM::StyleInvalidationReason::HTMLHyperlinkElementHrefChange);
} }
// https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-origin // https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-origin

View file

@ -52,6 +52,7 @@ public:
protected: protected:
virtual DOM::Document& hyperlink_element_utils_document() = 0; virtual DOM::Document& hyperlink_element_utils_document() = 0;
virtual DOM::Element& hyperlink_element_utils_element() = 0;
virtual Optional<String> hyperlink_element_utils_href() const = 0; virtual Optional<String> hyperlink_element_utils_href() const = 0;
virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) = 0; virtual WebIDL::ExceptionOr<void> set_hyperlink_element_utils_href(String) = 0;
virtual Optional<String> hyperlink_element_utils_referrerpolicy() const = 0; virtual Optional<String> hyperlink_element_utils_referrerpolicy() const = 0;

View file

@ -37,6 +37,9 @@ void SVGAElement::visit_edges(Cell::Visitor& visitor)
void SVGAElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value) void SVGAElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value)
{ {
Base::attribute_changed(name, old_value, value); Base::attribute_changed(name, old_value, value);
if (name == SVG::AttributeNames::href) {
invalidate_style(DOM::StyleInvalidationReason::HTMLHyperlinkElementHrefChange);
}
if (name == HTML::AttributeNames::rel) { if (name == HTML::AttributeNames::rel) {
if (m_rel_list) if (m_rel_list)
m_rel_list->associated_attribute_changed(value.value_or(String {})); m_rel_list->associated_attribute_changed(value.value_or(String {}));