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