script/dom/html/
htmlsourceelement.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};
7use js::context::JSContext;
8use js::rust::HandleObject;
9use script_bindings::script_runtime::temp_cx;
10use style::attr::AttrValue;
11
12use crate::dom::attr::Attr;
13use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods;
14use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
15use crate::dom::bindings::inheritance::Castable;
16use crate::dom::bindings::root::{Dom, DomRoot, Root};
17use crate::dom::bindings::str::{DOMString, USVString};
18use crate::dom::document::Document;
19use crate::dom::element::AttributeMutation;
20use crate::dom::html::htmlelement::HTMLElement;
21use crate::dom::html::htmlimageelement::HTMLImageElement;
22use crate::dom::html::htmlmediaelement::HTMLMediaElement;
23use crate::dom::html::htmlpictureelement::HTMLPictureElement;
24use crate::dom::node::{BindContext, Node, NodeDamage, UnbindContext};
25use crate::dom::virtualmethods::VirtualMethods;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
29pub(crate) struct HTMLSourceElement {
30    htmlelement: HTMLElement,
31}
32
33impl HTMLSourceElement {
34    fn new_inherited(
35        local_name: LocalName,
36        prefix: Option<Prefix>,
37        document: &Document,
38    ) -> HTMLSourceElement {
39        HTMLSourceElement {
40            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
41        }
42    }
43
44    pub(crate) fn new(
45        cx: &mut js::context::JSContext,
46        local_name: LocalName,
47        prefix: Option<Prefix>,
48        document: &Document,
49        proto: Option<HandleObject>,
50    ) -> DomRoot<HTMLSourceElement> {
51        Node::reflect_node_with_proto(
52            cx,
53            Box::new(HTMLSourceElement::new_inherited(
54                local_name, prefix, document,
55            )),
56            document,
57            proto,
58        )
59    }
60
61    fn iterate_next_html_image_element_siblings(
62        next_siblings_iterator: impl Iterator<Item = Root<Dom<Node>>>,
63        callback: impl Fn(&HTMLImageElement),
64    ) {
65        for next_sibling in next_siblings_iterator {
66            if let Some(html_image_element_sibling) = next_sibling.downcast::<HTMLImageElement>() {
67                callback(html_image_element_sibling);
68            }
69        }
70    }
71
72    fn iterate_next_html_image_element_siblings_with_cx(
73        cx: &mut JSContext,
74        next_siblings_iterator: impl Iterator<Item = Root<Dom<Node>>>,
75        callback: impl Fn(&mut JSContext, &HTMLImageElement),
76    ) {
77        for next_sibling in next_siblings_iterator {
78            if let Some(html_image_element_sibling) = next_sibling.downcast::<HTMLImageElement>() {
79                callback(cx, html_image_element_sibling);
80            }
81        }
82    }
83}
84
85impl VirtualMethods for HTMLSourceElement {
86    fn super_type(&self) -> Option<&dyn VirtualMethods> {
87        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
88    }
89
90    fn attribute_mutated(
91        &self,
92        cx: &mut js::context::JSContext,
93        attr: &Attr,
94        mutation: AttributeMutation,
95    ) {
96        self.super_type()
97            .unwrap()
98            .attribute_mutated(cx, attr, mutation);
99
100        match attr.local_name() {
101            &local_name!("srcset") |
102            &local_name!("sizes") |
103            &local_name!("media") |
104            &local_name!("type") => {
105                // <https://html.spec.whatwg.org/multipage/#reacting-to-dom-mutations>
106                // The element's parent is a picture element and a source element that is a previous
107                // sibling has its srcset, sizes, media, type attributes set, changed, or removed.
108                if let Some(parent) = self.upcast::<Node>().GetParentElement() {
109                    if parent.is::<HTMLPictureElement>() {
110                        let next_sibling_iterator = self.upcast::<Node>().following_siblings();
111                        HTMLSourceElement::iterate_next_html_image_element_siblings_with_cx(
112                            cx,
113                            next_sibling_iterator,
114                            |cx, image| image.update_the_image_data(cx),
115                        );
116                    }
117                }
118            },
119            &local_name!("width") | &local_name!("height") => {
120                // Note: Despite being explicitly stated in the specification that any width or
121                // height attributes changes (set, changed, removed) of the source element should be
122                // counted as relevant mutation for the sibling image element, these attributes
123                // affect only the style presentational hints of the image element.
124                if let Some(parent) = self.upcast::<Node>().GetParentElement() {
125                    if parent.is::<HTMLPictureElement>() {
126                        let next_sibling_iterator = self.upcast::<Node>().following_siblings();
127                        HTMLSourceElement::iterate_next_html_image_element_siblings(
128                            next_sibling_iterator,
129                            |image| image.upcast::<Node>().dirty(NodeDamage::Other),
130                        );
131                    }
132                }
133            },
134            _ => {},
135        }
136    }
137
138    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
139        match name {
140            &local_name!("width") | &local_name!("height") => {
141                AttrValue::from_dimension(value.into())
142            },
143            _ => self
144                .super_type()
145                .unwrap()
146                .parse_plain_attribute(name, value),
147        }
148    }
149
150    /// <https://html.spec.whatwg.org/multipage/#the-source-element:html-element-insertion-steps>
151    fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
152        self.super_type().unwrap().bind_to_tree(cx, context);
153
154        // Step 1. Let parent be insertedNode's parent.
155        let parent = self.upcast::<Node>().GetParentNode().unwrap();
156
157        // Step 2. If parent is a media element that has no src attribute and whose networkState has
158        // the value NETWORK_EMPTY, then invoke that media element's resource selection algorithm.
159        if parent.is::<HTMLMediaElement>() && std::ptr::eq(&*parent, context.parent) {
160            parent
161                .downcast::<HTMLMediaElement>()
162                .unwrap()
163                .handle_source_child_insertion(self, cx);
164        }
165
166        // Step 3. If parent is a picture element, then for each child of parent's children, if
167        // child is an img element, then count this as a relevant mutation for child.
168        if parent.is::<HTMLPictureElement>() && std::ptr::eq(&*parent, context.parent) {
169            let next_sibling_iterator = self.upcast::<Node>().following_siblings();
170            HTMLSourceElement::iterate_next_html_image_element_siblings_with_cx(
171                cx,
172                next_sibling_iterator,
173                |cx, image| image.update_the_image_data(cx),
174            );
175        }
176    }
177
178    #[expect(unsafe_code)]
179    /// <https://html.spec.whatwg.org/multipage/#the-source-element:html-element-removing-steps>
180    fn unbind_from_tree(&self, context: &UnbindContext, _can_gc: CanGc) {
181        // TODO: https://github.com/servo/servo/issues/42837
182        let mut cx = unsafe { temp_cx() };
183        let cx = &mut cx;
184        self.super_type()
185            .unwrap()
186            .unbind_from_tree(context, CanGc::from_cx(cx));
187
188        // Step 1. If oldParent is a picture element, then for each child of oldParent's children,
189        // if child is an img element, then count this as a relevant mutation for child.
190        if context.parent.is::<HTMLPictureElement>() && !self.upcast::<Node>().has_parent() {
191            if let Some(next_sibling) = context.next_sibling {
192                let next_sibling_iterator = next_sibling.inclusively_following_siblings();
193                HTMLSourceElement::iterate_next_html_image_element_siblings_with_cx(
194                    cx,
195                    next_sibling_iterator,
196                    |cx, image| image.update_the_image_data(cx),
197                );
198            }
199        }
200    }
201}
202
203impl HTMLSourceElementMethods<crate::DomTypeHolder> for HTMLSourceElement {
204    // https://html.spec.whatwg.org/multipage/#dom-source-src
205    make_url_getter!(Src, "src");
206
207    // https://html.spec.whatwg.org/multipage/#dom-source-src
208    make_url_setter!(SetSrc, "src");
209
210    // https://html.spec.whatwg.org/multipage/#dom-source-type
211    make_getter!(Type, "type");
212
213    // https://html.spec.whatwg.org/multipage/#dom-source-type
214    make_setter!(SetType, "type");
215
216    // https://html.spec.whatwg.org/multipage/#dom-source-srcset
217    make_url_getter!(Srcset, "srcset");
218
219    // https://html.spec.whatwg.org/multipage/#dom-source-srcset
220    make_url_setter!(SetSrcset, "srcset");
221
222    // https://html.spec.whatwg.org/multipage/#dom-source-sizes
223    make_getter!(Sizes, "sizes");
224
225    // https://html.spec.whatwg.org/multipage/#dom-source-sizes
226    make_setter!(SetSizes, "sizes");
227
228    // https://html.spec.whatwg.org/multipage/#dom-source-media
229    make_getter!(Media, "media");
230
231    // https://html.spec.whatwg.org/multipage/#dom-source-media
232    make_setter!(SetMedia, "media");
233
234    // <https://html.spec.whatwg.org/multipage/#dom-source-width>
235    make_dimension_uint_getter!(Width, "width");
236
237    // <https://html.spec.whatwg.org/multipage/#dom-source-width>
238    make_dimension_uint_setter!(SetWidth, "width");
239
240    // <https://html.spec.whatwg.org/multipage/#dom-source-height>
241    make_dimension_uint_getter!(Height, "height");
242
243    // <https://html.spec.whatwg.org/multipage/#dom-source-height>
244    make_dimension_uint_setter!(SetHeight, "height");
245}