script/dom/html/
htmllabelelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use html5ever::{LocalName, Prefix, local_name, ns};
7use js::rust::HandleObject;
8use style::attr::AttrValue;
9
10use crate::dom::activation::Activatable;
11use crate::dom::attr::Attr;
12use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
13use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
14use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
15use crate::dom::bindings::codegen::Bindings::HTMLLabelElementBinding::HTMLLabelElementMethods;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::{GetRootNodeOptions, NodeMethods};
17use crate::dom::bindings::inheritance::Castable;
18use crate::dom::bindings::root::DomRoot;
19use crate::dom::bindings::str::DOMString;
20use crate::dom::document::Document;
21use crate::dom::element::{AttributeMutation, Element};
22use crate::dom::event::Event;
23use crate::dom::eventtarget::EventTarget;
24use crate::dom::html::htmlelement::HTMLElement;
25use crate::dom::html::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
26use crate::dom::node::{Node, ShadowIncluding};
27use crate::dom::virtualmethods::VirtualMethods;
28use crate::script_runtime::CanGc;
29
30#[dom_struct]
31pub(crate) struct HTMLLabelElement {
32    htmlelement: HTMLElement,
33}
34
35impl HTMLLabelElement {
36    fn new_inherited(
37        local_name: LocalName,
38        prefix: Option<Prefix>,
39        document: &Document,
40    ) -> HTMLLabelElement {
41        HTMLLabelElement {
42            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
43        }
44    }
45
46    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
47    pub(crate) fn new(
48        local_name: LocalName,
49        prefix: Option<Prefix>,
50        document: &Document,
51        proto: Option<HandleObject>,
52        can_gc: CanGc,
53    ) -> DomRoot<HTMLLabelElement> {
54        Node::reflect_node_with_proto(
55            Box::new(HTMLLabelElement::new_inherited(
56                local_name, prefix, document,
57            )),
58            document,
59            proto,
60            can_gc,
61        )
62    }
63}
64
65impl Activatable for HTMLLabelElement {
66    fn as_element(&self) -> &Element {
67        self.upcast::<Element>()
68    }
69
70    fn is_instance_activatable(&self) -> bool {
71        true
72    }
73
74    // https://html.spec.whatwg.org/multipage/#the-label-element:activation_behaviour
75    // Basically this is telling us that if activation bubbles up to the label
76    // at all, we are free to do an implementation-dependent thing;
77    // firing a click event is an example, and the precise details of that
78    // click event (e.g. isTrusted) are not specified.
79    fn activation_behavior(&self, _event: &Event, _target: &EventTarget, can_gc: CanGc) {
80        if let Some(e) = self.GetControl() {
81            e.Click(can_gc);
82        }
83    }
84}
85
86impl HTMLLabelElementMethods<crate::DomTypeHolder> for HTMLLabelElement {
87    // https://html.spec.whatwg.org/multipage/#dom-fae-form
88    fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
89        self.form_owner()
90    }
91
92    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
93    make_getter!(HtmlFor, "for");
94
95    // https://html.spec.whatwg.org/multipage/#dom-label-htmlfor
96    make_atomic_setter!(SetHtmlFor, "for");
97
98    // https://html.spec.whatwg.org/multipage/#dom-label-control
99    fn GetControl(&self) -> Option<DomRoot<HTMLElement>> {
100        let for_attr = match self
101            .upcast::<Element>()
102            .get_attribute(&ns!(), &local_name!("for"))
103        {
104            Some(for_attr) => for_attr,
105            None => return self.first_labelable_descendant(),
106        };
107
108        let for_value = for_attr.Value();
109
110        // "If the attribute is specified and there is an element in the tree
111        // whose ID is equal to the value of the for attribute, and the first
112        // such element in tree order is a labelable element, then that
113        // element is the label element's labeled control."
114        // Two subtle points here: we need to search the _tree_, which is
115        // not necessarily the document if we're detached from the document,
116        // and we only consider one element even if a later element with
117        // the same ID is labelable.
118
119        let maybe_found = self
120            .upcast::<Node>()
121            .GetRootNode(&GetRootNodeOptions::empty())
122            .traverse_preorder(ShadowIncluding::No)
123            .find_map(|e| {
124                if let Some(htmle) = e.downcast::<HTMLElement>() {
125                    if htmle.upcast::<Element>().Id() == for_value {
126                        Some(DomRoot::from_ref(htmle))
127                    } else {
128                        None
129                    }
130                } else {
131                    None
132                }
133            });
134        // We now have the element that we would return, but only return it
135        // if it's labelable.
136        if let Some(ref maybe_labelable) = maybe_found {
137            if maybe_labelable.is_labelable_element() {
138                return maybe_found;
139            }
140        }
141        None
142    }
143}
144
145impl VirtualMethods for HTMLLabelElement {
146    fn super_type(&self) -> Option<&dyn VirtualMethods> {
147        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
148    }
149
150    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
151        match name {
152            &local_name!("for") => AttrValue::from_atomic(value.into()),
153            _ => self
154                .super_type()
155                .unwrap()
156                .parse_plain_attribute(name, value),
157        }
158    }
159
160    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
161        self.super_type()
162            .unwrap()
163            .attribute_mutated(attr, mutation, can_gc);
164        if *attr.local_name() == local_name!("form") {
165            self.form_attribute_mutated(mutation, can_gc);
166        }
167    }
168}
169
170impl HTMLLabelElement {
171    pub(crate) fn first_labelable_descendant(&self) -> Option<DomRoot<HTMLElement>> {
172        self.upcast::<Node>()
173            .traverse_preorder(ShadowIncluding::No)
174            .filter_map(DomRoot::downcast::<HTMLElement>)
175            .find(|elem| elem.is_labelable_element())
176    }
177}
178
179impl FormControl for HTMLLabelElement {
180    fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
181        self.GetControl()
182            .map(DomRoot::upcast::<Element>)
183            .and_then(|elem| {
184                elem.as_maybe_form_control()
185                    .and_then(|control| control.form_owner())
186            })
187    }
188
189    fn set_form_owner(&self, _: Option<&HTMLFormElement>) {
190        // Label is a special case for form owner, it reflects its control's
191        // form owner. Therefore it doesn't hold form owner itself.
192    }
193
194    fn to_element(&self) -> &Element {
195        self.upcast::<Element>()
196    }
197}