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