diff --git a/Libraries/LibWeb/CSS/PseudoClasses.json b/Libraries/LibWeb/CSS/PseudoClasses.json index d92a7fe8c48..fce688f5b8d 100644 --- a/Libraries/LibWeb/CSS/PseudoClasses.json +++ b/Libraries/LibWeb/CSS/PseudoClasses.json @@ -11,6 +11,9 @@ "checked": { "argument": "" }, + "default": { + "argument": "" + }, "defined": { "argument": "" }, diff --git a/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Libraries/LibWeb/CSS/SelectorEngine.cpp index 79fd77662c4..cba4bbc397d 100644 --- a/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -1037,6 +1037,29 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla return false; } + case CSS::PseudoClass::Default: { + // https://html.spec.whatwg.org/multipage/semantics-other.html#selector-default + + // The :default pseudo-class must match any element falling into one of the following categories: + if (auto const* form_associated_element = as_if(element)) { + // - Submit buttons that are default buttons of their form owner. + if (form_associated_element->is_submit_button() && form_associated_element->form() && form_associated_element->form()->default_button() == form_associated_element) + return true; + + // - input elements to which the checked attribute applies and that have a checked attribute + if (auto const* input_element = as_if(form_associated_element)) { + if (input_element->checked_applies() && input_element->has_attribute(HTML::AttributeNames::checked)) + return true; + } + } + // - option elements that have a selected attribute + else if (auto const* option_element = as_if(element)) { + if (option_element->has_attribute(HTML::AttributeNames::selected)) + return true; + } + + return false; + } } return false; diff --git a/Libraries/LibWeb/CSS/StyleInvalidationData.cpp b/Libraries/LibWeb/CSS/StyleInvalidationData.cpp index f5998387280..f8766d01059 100644 --- a/Libraries/LibWeb/CSS/StyleInvalidationData.cpp +++ b/Libraries/LibWeb/CSS/StyleInvalidationData.cpp @@ -75,6 +75,7 @@ static void collect_properties_used_in_has(Selector::SimpleSelector const& selec case PseudoClass::Link: case PseudoClass::AnyLink: case PseudoClass::LocalLink: + case PseudoClass::Default: if (in_has) style_invalidation_data.pseudo_classes_used_in_has_selectors.set(pseudo_class.type); break; diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Libraries/LibWeb/HTML/HTMLFormElement.cpp index e3fd85c6488..10f5aff1f20 100644 --- a/Libraries/LibWeb/HTML/HTMLFormElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLFormElement.cpp @@ -1176,13 +1176,13 @@ JS::Value HTMLFormElement::named_item_value(FlyString const& name) const } // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#default-button -FormAssociatedElement* HTMLFormElement::default_button() +FormAssociatedElement* HTMLFormElement::default_button() const { // A form element's default button is the first submit button in tree order whose form owner is that form element. FormAssociatedElement* default_button = nullptr; root().for_each_in_subtree([&](auto& node) { - auto* form_associated_element = dynamic_cast(&node); + auto* form_associated_element = const_cast(dynamic_cast(&node)); if (!form_associated_element) return TraversalDecision::Continue; diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.h b/Libraries/LibWeb/HTML/HTMLFormElement.h index 4899dea092d..947d02caad6 100644 --- a/Libraries/LibWeb/HTML/HTMLFormElement.h +++ b/Libraries/LibWeb/HTML/HTMLFormElement.h @@ -102,6 +102,8 @@ public: String action() const; WebIDL::ExceptionOr set_action(String const&); + FormAssociatedElement* default_button() const; + private: HTMLFormElement(DOM::Document&, DOM::QualifiedName); @@ -126,7 +128,6 @@ private: ErrorOr mail_as_body(URL::URL parsed_action, Vector entry_list, EncodingTypeAttributeState encoding_type, String encoding, GC::Ref target_navigable, Bindings::NavigationHistoryBehavior history_handling, UserNavigationInvolvement user_involvement); void plan_to_navigate_to(URL::URL url, Variant post_resource, Vector entry_list, GC::Ref target_navigable, Bindings::NavigationHistoryBehavior history_handling, UserNavigationInvolvement user_involvement); - FormAssociatedElement* default_button(); size_t number_of_fields_blocking_implicit_submission() const; bool m_firing_submission_events { false }; diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 99b0dd25e62..36b1e35f2ac 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -3027,6 +3027,18 @@ bool HTMLInputElement::required_applies() const } } +// https://html.spec.whatwg.org/multipage/input.html#do-not-apply +bool HTMLInputElement::checked_applies() const +{ + switch (type_state()) { + case TypeAttributeState::Checkbox: + case TypeAttributeState::RadioButton: + return true; + default: + return false; + } +} + bool HTMLInputElement::has_selectable_text() const { // Potential FIXME: Date, Month, Week, Time and LocalDateAndTime are rendered as a basic text input for now, diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.h b/Libraries/LibWeb/HTML/HTMLInputElement.h index 63e9c95c734..5cb353eaec5 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -220,6 +220,7 @@ public: bool pattern_applies() const; bool multiple_applies() const; bool required_applies() const; + bool checked_applies() const; bool has_selectable_text() const; bool supports_a_picker() const; diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/selectors/pseudo-classes/default.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/selectors/pseudo-classes/default.txt new file mode 100644 index 00000000000..0cbeaf275fe --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/selectors/pseudo-classes/default.txt @@ -0,0 +1,7 @@ +Harness status: OK + +Found 2 tests + +2 Pass +Pass ':default' matches + + +
+ + +
+ +
+ +
+ +
+ + +
+
+ + +
+
+ +
+ + + + + + + + + + +
+ + +
+
+ + +
+ + +