From 8a89a7bd956c0fb171179a094982d5b8624a6308 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 15 Feb 2022 19:16:57 +0100 Subject: [PATCH] LibWeb: Make honor the "checked" attribute Implemented according to spec, although it's very possible that I missed one or two details. :^) --- .../LibWeb/HTML/HTMLInputElement.cpp | 49 ++++++++++++++++++- .../Libraries/LibWeb/HTML/HTMLInputElement.h | 20 +++++++- Userland/Libraries/LibWeb/Layout/CheckBox.cpp | 5 +- .../Libraries/LibWeb/Layout/RadioButton.cpp | 4 +- 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 2e548cfdd2..4a59c0460a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -61,14 +61,37 @@ RefPtr HTMLInputElement::create_layout_node(NonnullRefPtrset_needs_display(); + if (should_run_activation_behavior == ShouldRunActivationBehavior::Yes) + run_activation_behavior(); +} + +void HTMLInputElement::run_activation_behavior() +{ + // The activation behavior for input elements are these steps: + + // FIXME: 1. If this element is not mutable and is not in the Checkbox state and is not in the Radio state, then return. + + // 2. Run this element's input activation behavior, if any, and do nothing otherwise. + run_input_activation_behavior(); +} + +// https://html.spec.whatwg.org/multipage/input.html#input-activation-behavior +void HTMLInputElement::run_input_activation_behavior() +{ dispatch_event(DOM::Event::create(EventNames::change)); } @@ -139,4 +162,26 @@ void HTMLInputElement::removed_from(DOM::Node* old_parent) set_form(nullptr); } +void HTMLInputElement::parse_attribute(FlyString const& name, String const& value) +{ + FormAssociatedElement::parse_attribute(name, value); + if (name == HTML::AttributeNames::checked) { + // When the checked content attribute is added, if the control does not have dirty checkedness, + // the user agent must set the checkedness of the element to true + if (!m_dirty_checkedness) + set_checked(true, ChangeSource::Programmatic, ShouldRunActivationBehavior::No); + } +} + +void HTMLInputElement::did_remove_attribute(FlyString const& name) +{ + FormAssociatedElement::did_remove_attribute(name); + if (name == HTML::AttributeNames::checked) { + // When the checked content attribute is removed, if the control does not have dirty checkedness, + // the user agent must set the checkedness of the element to false. + if (!m_dirty_checkedness) + set_checked(false, ChangeSource::Programmatic, ShouldRunActivationBehavior::No); + } +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index acf3577166..248c968061 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2022, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ @@ -28,7 +28,15 @@ public: void set_value(String); bool checked() const { return m_checked; } - void set_checked(bool); + enum class ChangeSource { + Programmatic, + User, + }; + enum class ShouldRunActivationBehavior { + No, + Yes, + }; + void set_checked(bool, ChangeSource = ChangeSource::Programmatic, ShouldRunActivationBehavior = ShouldRunActivationBehavior::Yes); bool enabled() const; @@ -36,6 +44,9 @@ public: virtual bool is_focusable() const override; + virtual void parse_attribute(FlyString const&, String const&) override; + virtual void did_remove_attribute(FlyString const&) override; + private: // ^DOM::Node virtual void inserted() override; @@ -43,11 +54,16 @@ private: // ^DOM::EventTarget virtual void did_receive_focus() override; + virtual void run_activation_behavior() override; void create_shadow_tree_if_needed(); + void run_input_activation_behavior(); RefPtr m_text_node; bool m_checked { false }; + + // https://html.spec.whatwg.org/multipage/input.html#concept-input-checked-dirty-flag + bool m_dirty_checkedness { false }; }; } diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp index 5d043e0198..c171899834 100644 --- a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,7 @@ void CheckBox::handle_mouseup(Badge, const Gfx::IntPoint& position is_inside_node_or_label = Label::is_inside_associated_label(*this, position); if (is_inside_node_or_label) - dom_node().set_checked(!dom_node().checked()); + dom_node().set_checked(!dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User); m_being_pressed = false; m_tracking_mouse = false; @@ -102,7 +103,7 @@ void CheckBox::handle_associated_label_mouseup(Badge