mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibWeb: Look for labeled control in DOM tree instead of layout tree
...because "change" event should be dispatched on control even if it has "display: none" style. This change fixes selection in labels dropdown on GitHub's "new issue" page.
This commit is contained in:
parent
730876fda9
commit
f932d5d825
Notes:
sideshowbarker
2024-07-17 07:11:12 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/f932d5d825 Pull-request: https://github.com/SerenityOS/serenity/pull/23684
8 changed files with 74 additions and 49 deletions
|
@ -0,0 +1 @@
|
|||
Label Checkbox changed
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<label id="lbl">Label<input id="checkbox" type="checkbox"/></label>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest((done) => {
|
||||
checkbox.addEventListener("change", () => {
|
||||
println("Checkbox changed");
|
||||
done();
|
||||
});
|
||||
|
||||
internals.click(5, 5);
|
||||
});
|
||||
</script>
|
|
@ -30,4 +30,39 @@ JS::GCPtr<Layout::Node> HTMLLabelElement::create_layout_node(NonnullRefPtr<CSS::
|
|||
return heap().allocate_without_realm<Layout::Label>(document(), this, move(style));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/forms.html#labeled-control
|
||||
JS::GCPtr<HTMLElement> HTMLLabelElement::control() const
|
||||
{
|
||||
JS::GCPtr<HTMLElement> control;
|
||||
|
||||
// The for attribute may be specified to indicate a form control with which the caption is
|
||||
// to be associated. If the attribute is specified, the attribute's value must be the ID of
|
||||
// a labelable element in the same tree as the label element. If the attribute is specified
|
||||
// and there is an element in the tree whose ID is equal to the value of the for attribute,
|
||||
// and the first such element in tree order is a labelable element, then that element is the
|
||||
// label element's labeled control.
|
||||
if (for_().has_value()) {
|
||||
for_each_in_inclusive_subtree_of_type<HTMLElement>([&](auto& element) {
|
||||
if (element.id() == *for_() && element.is_labelable()) {
|
||||
control = &const_cast<HTMLElement&>(element);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return control;
|
||||
}
|
||||
|
||||
// If the for attribute is not specified, but the label element has a labelable element descendant,
|
||||
// then the first such descendant in tree order is the label element's labeled control.
|
||||
for_each_in_subtree_of_type<HTMLElement>([&](auto& element) {
|
||||
if (element.is_labelable()) {
|
||||
control = &const_cast<HTMLElement&>(element);
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ public:
|
|||
|
||||
Optional<String> for_() const { return attribute(HTML::AttributeNames::for_); }
|
||||
|
||||
JS::GCPtr<HTMLElement> control() const;
|
||||
|
||||
private:
|
||||
HTMLLabelElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
|
|
|
@ -8,6 +8,6 @@ interface HTMLLabelElement : HTMLElement {
|
|||
|
||||
// FIXME: readonly attribute HTMLFormElement? form;
|
||||
[CEReactions, Reflect=for] attribute DOMString htmlFor;
|
||||
// FIXME: readonly attribute HTMLElement? control;
|
||||
readonly attribute HTMLElement? control;
|
||||
|
||||
};
|
||||
|
|
|
@ -27,8 +27,10 @@ void Label::handle_mousedown_on_label(Badge<Painting::TextPaintable>, CSSPixelPo
|
|||
if (button != GUI::MouseButton::Primary)
|
||||
return;
|
||||
|
||||
if (auto* control = labeled_control(); control)
|
||||
control->paintable()->handle_associated_label_mousedown({});
|
||||
if (auto control = dom_node().control(); control && control->paintable()) {
|
||||
auto& labelable_paintable = verify_cast<Painting::LabelablePaintable>(*control->paintable());
|
||||
labelable_paintable.handle_associated_label_mousedown({});
|
||||
}
|
||||
|
||||
m_tracking_mouse = true;
|
||||
}
|
||||
|
@ -38,12 +40,13 @@ void Label::handle_mouseup_on_label(Badge<Painting::TextPaintable>, CSSPixelPoin
|
|||
if (!m_tracking_mouse || button != GUI::MouseButton::Primary)
|
||||
return;
|
||||
|
||||
if (auto* control = labeled_control(); control) {
|
||||
if (auto control = dom_node().control(); control && control->paintable()) {
|
||||
bool is_inside_control = control->paintable_box()->absolute_rect().contains(position);
|
||||
bool is_inside_label = paintable_box()->absolute_rect().contains(position);
|
||||
|
||||
if (is_inside_control || is_inside_label)
|
||||
control->paintable()->handle_associated_label_mouseup({});
|
||||
if (is_inside_control || is_inside_label) {
|
||||
auto& labelable_paintable = verify_cast<Painting::LabelablePaintable>(*control->paintable());
|
||||
labelable_paintable.handle_associated_label_mouseup({});
|
||||
}
|
||||
}
|
||||
|
||||
m_tracking_mouse = false;
|
||||
|
@ -54,11 +57,11 @@ void Label::handle_mousemove_on_label(Badge<Painting::TextPaintable>, CSSPixelPo
|
|||
if (!m_tracking_mouse)
|
||||
return;
|
||||
|
||||
if (auto* control = labeled_control(); control) {
|
||||
if (auto control = dom_node().control(); control && control->paintable()) {
|
||||
bool is_inside_control = control->paintable_box()->absolute_rect().contains(position);
|
||||
bool is_inside_label = paintable_box()->absolute_rect().contains(position);
|
||||
|
||||
control->paintable()->handle_associated_label_mousemove({}, is_inside_control || is_inside_label);
|
||||
auto& labelable_paintable = verify_cast<Painting::LabelablePaintable>(*control->paintable());
|
||||
labelable_paintable.handle_associated_label_mousemove({}, is_inside_control || is_inside_label);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,37 +116,4 @@ Label const* Label::label_for_control_node(LabelableNode const& control)
|
|||
return control.first_ancestor_of_type<Label>();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/forms.html#labeled-control
|
||||
LabelableNode* Label::labeled_control()
|
||||
{
|
||||
if (!document().layout_node())
|
||||
return nullptr;
|
||||
|
||||
LabelableNode* control = nullptr;
|
||||
|
||||
// The for attribute may be specified to indicate a form control with which the caption is to be associated.
|
||||
// If the attribute is specified, the attribute's value must be the ID of a labelable element in the
|
||||
// same tree as the label element. If the attribute is specified and there is an element in the tree
|
||||
// whose ID is equal to the value of the for attribute, and the first such element in tree order is
|
||||
// a labelable element, then that element is the label element's labeled control.
|
||||
if (auto for_ = dom_node().for_(); for_.has_value()) {
|
||||
document().layout_node()->for_each_in_inclusive_subtree_of_type<LabelableNode>([&](auto& node) {
|
||||
if (node.dom_node().id() == for_) {
|
||||
control = &node;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
return control;
|
||||
}
|
||||
|
||||
// If the for attribute is not specified, but the label element has a labelable element descendant,
|
||||
// then the first such descendant in tree order is the label element's labeled control.
|
||||
for_each_in_subtree_of_type<LabelableNode>([&](auto& labelable_node) {
|
||||
control = &labelable_node;
|
||||
return IterationDecision::Break;
|
||||
});
|
||||
return control;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,8 +28,6 @@ public:
|
|||
void handle_mouseup_on_label(Badge<Painting::TextPaintable>, CSSPixelPoint, unsigned button);
|
||||
void handle_mousemove_on_label(Badge<Painting::TextPaintable>, CSSPixelPoint, unsigned button);
|
||||
|
||||
LabelableNode* labeled_control();
|
||||
|
||||
private:
|
||||
virtual bool is_label() const override { return true; }
|
||||
|
||||
|
|
|
@ -30,10 +30,8 @@ bool TextPaintable::wants_mouse_events() const
|
|||
|
||||
DOM::Node* TextPaintable::mouse_event_target() const
|
||||
{
|
||||
if (auto* label = layout_node().first_ancestor_of_type<Layout::Label>()) {
|
||||
if (auto* control = const_cast<Layout::Label*>(label)->labeled_control())
|
||||
return &control->dom_node();
|
||||
}
|
||||
if (auto const* label = layout_node().first_ancestor_of_type<Layout::Label>())
|
||||
return label->dom_node().control().ptr();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue