Skip to main content

script/dom/html/embedded_content/
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 style::attr::AttrValue;
10
11use crate::dom::MoveContext;
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::element::attributes::storage::AttrRef;
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::virtualmethods::VirtualMethods;
25use crate::dom::node::{BindContext, Node, NodeDamage, UnbindContext};
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        cx: &mut js::context::JSContext,
45        local_name: LocalName,
46        prefix: Option<Prefix>,
47        document: &Document,
48        proto: Option<HandleObject>,
49    ) -> DomRoot<HTMLSourceElement> {
50        Node::reflect_node_with_proto(
51            cx,
52            Box::new(HTMLSourceElement::new_inherited(
53                local_name, prefix, document,
54            )),
55            document,
56            proto,
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 JSContext,
73        next_siblings_iterator: impl Iterator<Item = Root<Dom<Node>>>,
74        callback: impl Fn(&mut 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    fn attribute_mutated(
90        &self,
91        cx: &mut js::context::JSContext,
92        attr: AttrRef<'_>,
93        mutation: AttributeMutation,
94    ) {
95        self.super_type()
96            .unwrap()
97            .attribute_mutated(cx, attr, mutation);
98
99        match attr.local_name() {
100            &local_name!("srcset") |
101            &local_name!("sizes") |
102            &local_name!("media") |
103            &local_name!("type") => {
104                // <https://html.spec.whatwg.org/multipage/#reacting-to-dom-mutations>
105                // The element's parent is a picture element and a source element that is a previous
106                // sibling has its srcset, sizes, media, type attributes set, changed, or removed.
107                if let Some(parent) = self.upcast::<Node>().GetParentElement() &&
108                    parent.is::<HTMLPictureElement>()
109                {
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            &local_name!("width") | &local_name!("height") => {
119                // Note: Despite being explicitly stated in the specification that any width or
120                // height attributes changes (set, changed, removed) of the source element should be
121                // counted as relevant mutation for the sibling image element, these attributes
122                // affect only the style presentational hints of the image element.
123                if let Some(parent) = self.upcast::<Node>().GetParentElement() &&
124                    parent.is::<HTMLPictureElement>()
125                {
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(cx.no_gc(), NodeDamage::Other),
130                    );
131                }
132            },
133            _ => {},
134        }
135    }
136
137    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
138        match name {
139            &local_name!("width") | &local_name!("height") => {
140                AttrValue::from_dimension(value.into())
141            },
142            _ => self
143                .super_type()
144                .unwrap()
145                .parse_plain_attribute(name, value),
146        }
147    }
148
149    /// <https://html.spec.whatwg.org/multipage/#the-source-element:html-element-insertion-steps>
150    fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
151        self.super_type().unwrap().bind_to_tree(cx, context);
152
153        // Step 1. Let parent be insertedNode's parent.
154        let parent = self.upcast::<Node>().GetParentNode().unwrap();
155
156        // Step 2. If parent is a media element that has no src attribute and whose networkState has
157        // the value NETWORK_EMPTY, then invoke that media element's resource selection algorithm.
158        if parent.is::<HTMLMediaElement>() && *parent == *context.parent {
159            parent
160                .downcast::<HTMLMediaElement>()
161                .unwrap()
162                .handle_source_child_insertion(cx, self);
163        }
164
165        // Step 3. If parent is a picture element, then for each child of parent's children, if
166        // child is an img element, then count this as a relevant mutation for child.
167        if parent.is::<HTMLPictureElement>() && *parent == *context.parent {
168            let next_sibling_iterator = self.upcast::<Node>().following_siblings();
169            HTMLSourceElement::iterate_next_html_image_element_siblings_with_cx(
170                cx,
171                next_sibling_iterator,
172                |cx, image| image.update_the_image_data(cx),
173            );
174        }
175    }
176
177    /// <https://html.spec.whatwg.org/multipage/#html-element-moving-steps>
178    fn moving_steps(&self, cx: &mut JSContext, context: &MoveContext) {
179        if let Some(super_type) = self.super_type() {
180            super_type.moving_steps(cx, context);
181        }
182
183        // Step 1. If movedNode's parent is a media element that has no src attribute and whose networkState
184        // has the value NETWORK_EMPTY, then invoke that media element's resource selection
185        // algorithm.
186        if let Some(parent) = self
187            .upcast::<Node>()
188            .GetParentNode()
189            .and_then(DomRoot::downcast::<HTMLMediaElement>)
190        {
191            parent.handle_source_child_insertion(cx, self);
192        }
193
194        // Step 2. If isSubtreeRoot is true and oldAncestor is a picture element, then for each child of
195        // oldAncestor's children: if child is an img element, then count this as a relevant
196        // mutation for child.
197        if context.is_subtree_root && context.old_ancestor.is::<HTMLPictureElement>() {
198            for child in context.old_ancestor.child_elements() {
199                if let Some(image_child) = child.downcast::<HTMLImageElement>() {
200                    image_child.update_the_image_data(cx);
201                }
202            }
203        }
204    }
205
206    /// <https://html.spec.whatwg.org/multipage/#the-source-element:html-element-removing-steps>
207    fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
208        self.super_type().unwrap().unbind_from_tree(cx, context);
209
210        // Step 1. If oldParent is a picture element, then for each child of oldParent's children,
211        // if child is an img element, then count this as a relevant mutation for child.
212        if context.parent.is::<HTMLPictureElement>() &&
213            let Some(next_sibling) = context.next_sibling
214        {
215            let next_sibling_iterator = next_sibling.inclusively_following_siblings();
216            HTMLSourceElement::iterate_next_html_image_element_siblings_with_cx(
217                cx,
218                next_sibling_iterator,
219                |cx, image| image.update_the_image_data(cx),
220            );
221        }
222    }
223}
224
225impl HTMLSourceElementMethods<crate::DomTypeHolder> for HTMLSourceElement {
226    // https://html.spec.whatwg.org/multipage/#dom-source-src
227    make_url_getter!(Src, "src");
228
229    // https://html.spec.whatwg.org/multipage/#dom-source-src
230    make_url_setter!(SetSrc, "src");
231
232    // https://html.spec.whatwg.org/multipage/#dom-source-type
233    make_getter!(Type, "type");
234
235    // https://html.spec.whatwg.org/multipage/#dom-source-type
236    make_setter!(SetType, "type");
237
238    // https://html.spec.whatwg.org/multipage/#dom-source-srcset
239    make_url_getter!(Srcset, "srcset");
240
241    // https://html.spec.whatwg.org/multipage/#dom-source-srcset
242    make_url_setter!(SetSrcset, "srcset");
243
244    // https://html.spec.whatwg.org/multipage/#dom-source-sizes
245    make_getter!(Sizes, "sizes");
246
247    // https://html.spec.whatwg.org/multipage/#dom-source-sizes
248    make_setter!(SetSizes, "sizes");
249
250    // https://html.spec.whatwg.org/multipage/#dom-source-media
251    make_getter!(Media, "media");
252
253    // https://html.spec.whatwg.org/multipage/#dom-source-media
254    make_setter!(SetMedia, "media");
255
256    // <https://html.spec.whatwg.org/multipage/#dom-source-width>
257    make_dimension_uint_getter!(Width, "width");
258
259    // <https://html.spec.whatwg.org/multipage/#dom-source-width>
260    make_dimension_uint_setter!(SetWidth, "width");
261
262    // <https://html.spec.whatwg.org/multipage/#dom-source-height>
263    make_dimension_uint_getter!(Height, "height");
264
265    // <https://html.spec.whatwg.org/multipage/#dom-source-height>
266    make_dimension_uint_setter!(SetHeight, "height");
267}