script/dom/
virtualmethods.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 html5ever::LocalName;
6use js::context::JSContext;
7use script_bindings::root::DomRoot;
8use script_bindings::script_runtime::CanGc;
9use style::attr::AttrValue;
10
11use crate::dom::attr::Attr;
12use crate::dom::bindings::inheritance::{
13    Castable, DocumentFragmentTypeId, ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId,
14    NodeTypeId, SVGElementTypeId, SVGGraphicsElementTypeId,
15};
16use crate::dom::bindings::str::DOMString;
17use crate::dom::document::Document;
18use crate::dom::documentfragment::DocumentFragment;
19use crate::dom::element::{AttributeMutation, Element};
20use crate::dom::event::Event;
21use crate::dom::html::htmlanchorelement::HTMLAnchorElement;
22use crate::dom::html::htmlareaelement::HTMLAreaElement;
23use crate::dom::html::htmlbaseelement::HTMLBaseElement;
24use crate::dom::html::htmlbodyelement::HTMLBodyElement;
25use crate::dom::html::htmlbuttonelement::HTMLButtonElement;
26use crate::dom::html::htmlcanvaselement::HTMLCanvasElement;
27use crate::dom::html::htmldetailselement::HTMLDetailsElement;
28use crate::dom::html::htmlelement::HTMLElement;
29use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
30use crate::dom::html::htmlfontelement::HTMLFontElement;
31use crate::dom::html::htmlformelement::HTMLFormElement;
32use crate::dom::html::htmlheadelement::HTMLHeadElement;
33use crate::dom::html::htmlhrelement::HTMLHRElement;
34use crate::dom::html::htmliframeelement::HTMLIFrameElement;
35use crate::dom::html::htmlimageelement::HTMLImageElement;
36use crate::dom::html::htmlinputelement::HTMLInputElement;
37use crate::dom::html::htmllabelelement::HTMLLabelElement;
38use crate::dom::html::htmllielement::HTMLLIElement;
39use crate::dom::html::htmllinkelement::HTMLLinkElement;
40use crate::dom::html::htmlmediaelement::HTMLMediaElement;
41use crate::dom::html::htmlmetaelement::HTMLMetaElement;
42use crate::dom::html::htmlmeterelement::HTMLMeterElement;
43use crate::dom::html::htmlobjectelement::HTMLObjectElement;
44use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
45use crate::dom::html::htmloptionelement::HTMLOptionElement;
46use crate::dom::html::htmloutputelement::HTMLOutputElement;
47use crate::dom::html::htmlpreelement::HTMLPreElement;
48use crate::dom::html::htmlprogresselement::HTMLProgressElement;
49use crate::dom::html::htmlscriptelement::HTMLScriptElement;
50use crate::dom::html::htmlselectelement::HTMLSelectElement;
51use crate::dom::html::htmlslotelement::HTMLSlotElement;
52use crate::dom::html::htmlsourceelement::HTMLSourceElement;
53use crate::dom::html::htmlstyleelement::HTMLStyleElement;
54use crate::dom::html::htmltablecellelement::HTMLTableCellElement;
55use crate::dom::html::htmltablecolelement::HTMLTableColElement;
56use crate::dom::html::htmltableelement::HTMLTableElement;
57use crate::dom::html::htmltablerowelement::HTMLTableRowElement;
58use crate::dom::html::htmltablesectionelement::HTMLTableSectionElement;
59use crate::dom::html::htmltemplateelement::HTMLTemplateElement;
60use crate::dom::html::htmltextareaelement::HTMLTextAreaElement;
61use crate::dom::html::htmltitleelement::HTMLTitleElement;
62use crate::dom::html::htmlvideoelement::HTMLVideoElement;
63use crate::dom::htmlbuttonelement::CommandState;
64use crate::dom::htmldialogelement::HTMLDialogElement;
65use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
66use crate::dom::shadowroot::ShadowRoot;
67use crate::dom::svg::svgelement::SVGElement;
68use crate::dom::svg::svgimageelement::SVGImageElement;
69use crate::dom::svg::svgsvgelement::SVGSVGElement;
70
71/// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
72/// behaviours. Replicates the effect of C++ virtual methods.
73pub(crate) trait VirtualMethods {
74    /// Returns self as the superclass of the implementation for this trait,
75    /// if any.
76    fn super_type(&self) -> Option<&dyn VirtualMethods>;
77
78    /// Called when attributes of a node are mutated.
79    /// <https://dom.spec.whatwg.org/#attribute-is-set>
80    /// <https://dom.spec.whatwg.org/#attribute-is-removed>
81    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
82        if let Some(s) = self.super_type() {
83            s.attribute_mutated(attr, mutation, can_gc);
84        }
85    }
86
87    /// Returns `true` if given attribute `attr` affects style of the
88    /// given element.
89    fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
90        match self.super_type() {
91            Some(s) => s.attribute_affects_presentational_hints(attr),
92            None => false,
93        }
94    }
95
96    /// Returns the right AttrValue variant for the attribute with name `name`
97    /// on this element.
98    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
99        match self.super_type() {
100            Some(s) => s.parse_plain_attribute(name, value),
101            _ => AttrValue::String(value.into()),
102        }
103    }
104
105    /// Invoked during a DOM tree mutation after a node becomes connected, once all
106    /// related DOM tree mutations have been applied.
107    /// <https://dom.spec.whatwg.org/#concept-node-post-connection-ext>
108    fn post_connection_steps(&self, cx: &mut JSContext) {
109        if let Some(s) = self.super_type() {
110            s.post_connection_steps(cx);
111        }
112    }
113
114    /// Called when a Node is appended to a tree.
115    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
116        if let Some(s) = self.super_type() {
117            s.bind_to_tree(context, can_gc);
118        }
119    }
120
121    /// Called when a Node is removed from a tree.
122    /// Implements removing steps:
123    /// <https://dom.spec.whatwg.org/#concept-node-remove-ext>
124    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
125        if let Some(s) = self.super_type() {
126            s.unbind_from_tree(context, can_gc);
127        }
128    }
129
130    /// Called on the parent when its children are changed.
131    fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
132        if let Some(s) = self.super_type() {
133            s.children_changed(mutation, can_gc);
134        }
135    }
136
137    /// Called during event dispatch after the bubbling phase completes.
138    fn handle_event(&self, event: &Event, can_gc: CanGc) {
139        if let Some(s) = self.super_type() {
140            s.handle_event(event, can_gc);
141        }
142    }
143
144    /// <https://html.spec.whatwg.org/multipage/#is-valid-command-steps>
145    fn is_valid_command_steps(&self, command: CommandState) -> bool {
146        self.super_type()
147            .is_some_and(|super_type| super_type.is_valid_command_steps(command))
148    }
149
150    /// <https://html.spec.whatwg.org/multipage/#command-steps>
151    fn command_steps(
152        &self,
153        button: DomRoot<HTMLButtonElement>,
154        command: CommandState,
155        can_gc: CanGc,
156    ) -> bool {
157        self.super_type()
158            .is_some_and(|super_type| super_type.command_steps(button, command, can_gc))
159    }
160
161    /// <https://dom.spec.whatwg.org/#concept-node-adopt-ext>
162    fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
163        if let Some(s) = self.super_type() {
164            s.adopting_steps(old_doc, can_gc);
165        }
166    }
167
168    /// <https://dom.spec.whatwg.org/#concept-node-clone-ext>
169    fn cloning_steps(
170        &self,
171        copy: &Node,
172        maybe_doc: Option<&Document>,
173        clone_children: CloneChildrenFlag,
174        can_gc: CanGc,
175    ) {
176        if let Some(s) = self.super_type() {
177            s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
178        }
179    }
180
181    /// Called on an element when it is popped off the stack of open elements
182    /// of a parser.
183    fn pop(&self) {
184        if let Some(s) = self.super_type() {
185            s.pop();
186        }
187    }
188}
189
190/// Obtain a VirtualMethods instance for a given Node-derived object. Any
191/// method call on the trait object will invoke the corresponding method on the
192/// concrete type, propagating up the parent hierarchy unless otherwise
193/// interrupted.
194pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods {
195    match node.type_id() {
196        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) => {
197            node.downcast::<HTMLAnchorElement>().unwrap() as &dyn VirtualMethods
198        },
199        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
200            node.downcast::<HTMLAreaElement>().unwrap() as &dyn VirtualMethods
201        },
202        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBaseElement)) => {
203            node.downcast::<HTMLBaseElement>().unwrap() as &dyn VirtualMethods
204        },
205        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) => {
206            node.downcast::<HTMLBodyElement>().unwrap() as &dyn VirtualMethods
207        },
208        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => {
209            node.downcast::<HTMLButtonElement>().unwrap() as &dyn VirtualMethods
210        },
211        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement)) => {
212            node.downcast::<HTMLCanvasElement>().unwrap() as &dyn VirtualMethods
213        },
214        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLDetailsElement)) => {
215            node.downcast::<HTMLDetailsElement>().unwrap() as &dyn VirtualMethods
216        },
217        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLDialogElement)) => {
218            node.downcast::<HTMLDialogElement>().unwrap() as &dyn VirtualMethods
219        },
220        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLFieldSetElement)) => {
221            node.downcast::<HTMLFieldSetElement>().unwrap() as &dyn VirtualMethods
222        },
223        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLFontElement)) => {
224            node.downcast::<HTMLFontElement>().unwrap() as &dyn VirtualMethods
225        },
226        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLFormElement)) => {
227            node.downcast::<HTMLFormElement>().unwrap() as &dyn VirtualMethods
228        },
229        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHeadElement)) => {
230            node.downcast::<HTMLHeadElement>().unwrap() as &dyn VirtualMethods
231        },
232        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHRElement)) => {
233            node.downcast::<HTMLHRElement>().unwrap() as &dyn VirtualMethods
234        },
235        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement)) => {
236            node.downcast::<HTMLImageElement>().unwrap() as &dyn VirtualMethods
237        },
238        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement)) => {
239            node.downcast::<HTMLIFrameElement>().unwrap() as &dyn VirtualMethods
240        },
241        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
242            node.downcast::<HTMLInputElement>().unwrap() as &dyn VirtualMethods
243        },
244        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLabelElement)) => {
245            node.downcast::<HTMLLabelElement>().unwrap() as &dyn VirtualMethods
246        },
247        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLIElement)) => {
248            node.downcast::<HTMLLIElement>().unwrap() as &dyn VirtualMethods
249        },
250        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
251            node.downcast::<HTMLLinkElement>().unwrap() as &dyn VirtualMethods
252        },
253        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(
254            media_el,
255        ))) => match media_el {
256            HTMLMediaElementTypeId::HTMLVideoElement => {
257                node.downcast::<HTMLVideoElement>().unwrap() as &dyn VirtualMethods
258            },
259            _ => node.downcast::<HTMLMediaElement>().unwrap() as &dyn VirtualMethods,
260        },
261        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMetaElement)) => {
262            node.downcast::<HTMLMetaElement>().unwrap() as &dyn VirtualMethods
263        },
264        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMeterElement)) => {
265            node.downcast::<HTMLMeterElement>().unwrap() as &dyn VirtualMethods
266        },
267        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => {
268            node.downcast::<HTMLObjectElement>().unwrap() as &dyn VirtualMethods
269        },
270        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement)) => {
271            node.downcast::<HTMLOptGroupElement>().unwrap() as &dyn VirtualMethods
272        },
273        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)) => {
274            node.downcast::<HTMLOptionElement>().unwrap() as &dyn VirtualMethods
275        },
276        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOutputElement)) => {
277            node.downcast::<HTMLOutputElement>().unwrap() as &dyn VirtualMethods
278        },
279        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement)) => {
280            node.downcast::<HTMLPreElement>().unwrap() as &dyn VirtualMethods
281        },
282        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLProgressElement)) => {
283            node.downcast::<HTMLProgressElement>().unwrap() as &dyn VirtualMethods
284        },
285        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)) => {
286            node.downcast::<HTMLScriptElement>().unwrap() as &dyn VirtualMethods
287        },
288        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => {
289            node.downcast::<HTMLSelectElement>().unwrap() as &dyn VirtualMethods
290        },
291        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSourceElement)) => {
292            node.downcast::<HTMLSourceElement>().unwrap() as &dyn VirtualMethods
293        },
294        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSlotElement)) => {
295            node.downcast::<HTMLSlotElement>().unwrap() as &dyn VirtualMethods
296        },
297        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => {
298            node.downcast::<HTMLStyleElement>().unwrap() as &dyn VirtualMethods
299        },
300        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement)) => {
301            node.downcast::<HTMLTableElement>().unwrap() as &dyn VirtualMethods
302        },
303        NodeTypeId::Element(ElementTypeId::HTMLElement(
304            HTMLElementTypeId::HTMLTableCellElement,
305        )) => node.downcast::<HTMLTableCellElement>().unwrap() as &dyn VirtualMethods,
306        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement)) => {
307            node.downcast::<HTMLTableColElement>().unwrap() as &dyn VirtualMethods
308        },
309        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement)) => {
310            node.downcast::<HTMLTableRowElement>().unwrap() as &dyn VirtualMethods
311        },
312        NodeTypeId::Element(ElementTypeId::HTMLElement(
313            HTMLElementTypeId::HTMLTableSectionElement,
314        )) => node.downcast::<HTMLTableSectionElement>().unwrap() as &dyn VirtualMethods,
315        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTemplateElement)) => {
316            node.downcast::<HTMLTemplateElement>().unwrap() as &dyn VirtualMethods
317        },
318        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
319            node.downcast::<HTMLTextAreaElement>().unwrap() as &dyn VirtualMethods
320        },
321        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement)) => {
322            node.downcast::<HTMLTitleElement>().unwrap() as &dyn VirtualMethods
323        },
324        NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
325            SVGGraphicsElementTypeId::SVGImageElement,
326        ))) => node.downcast::<SVGImageElement>().unwrap() as &dyn VirtualMethods,
327        NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
328            SVGGraphicsElementTypeId::SVGSVGElement,
329        ))) => node.downcast::<SVGSVGElement>().unwrap() as &dyn VirtualMethods,
330        NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGElement)) => {
331            node.downcast::<SVGElement>().unwrap() as &dyn VirtualMethods
332        },
333        NodeTypeId::Element(ElementTypeId::Element) => {
334            node.downcast::<Element>().unwrap() as &dyn VirtualMethods
335        },
336        NodeTypeId::Element(_) => node.downcast::<HTMLElement>().unwrap() as &dyn VirtualMethods,
337        NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => {
338            node.downcast::<ShadowRoot>().unwrap() as &dyn VirtualMethods
339        },
340        NodeTypeId::DocumentFragment(_) => {
341            node.downcast::<DocumentFragment>().unwrap() as &dyn VirtualMethods
342        },
343        _ => node as &dyn VirtualMethods,
344    }
345}