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