script/dom/node/
node.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
5//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
6
7use std::borrow::Cow;
8use std::cell::{Cell, LazyCell, UnsafeCell};
9use std::default::Default;
10use std::f64::consts::PI;
11use std::marker::PhantomData;
12use std::ops::Deref;
13use std::slice::from_ref;
14use std::{cmp, fmt, iter};
15
16use app_units::Au;
17use bitflags::bitflags;
18use devtools_traits::NodeInfo;
19use dom_struct::dom_struct;
20use embedder_traits::UntrustedNodeAddress;
21use euclid::default::Size2D;
22use euclid::{Point2D, Rect};
23use html5ever::serialize::HtmlSerializer;
24use html5ever::{Namespace, Prefix, QualName, ns, serialize as html_serialize};
25use js::context::{JSContext, NoGC};
26use js::jsapi::JSObject;
27use js::rust::HandleObject;
28use keyboard_types::Modifiers;
29use layout_api::wrapper_traits::SharedSelection;
30use layout_api::{
31    AxesOverflow, BoxAreaType, CSSPixelRectIterator, GenericLayoutData, HTMLCanvasData,
32    HTMLMediaData, LayoutElementType, LayoutNodeType, PhysicalSides, QueryMsg, SVGElementData,
33    StyleData, TrustedNodeAddress, with_layout_state,
34};
35use libc::{self, c_void, uintptr_t};
36use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
37use net_traits::image_cache::Image;
38use pixels::ImageMetadata;
39use script_bindings::codegen::GenericBindings::EventBinding::EventMethods;
40use script_bindings::codegen::InheritTypes::DocumentFragmentTypeId;
41use script_traits::DocumentActivity;
42use servo_arc::Arc as ServoArc;
43use servo_base::id::{BrowsingContextId, PipelineId};
44use servo_config::pref;
45use servo_url::ServoUrl;
46use smallvec::SmallVec;
47use style::Atom;
48use style::attr::AttrValue;
49use style::context::QuirksMode;
50use style::dom::OpaqueNode;
51use style::dom_apis::{QueryAll, QueryFirst};
52use style::properties::ComputedValues;
53use style::selector_parser::PseudoElement;
54use style::stylesheets::Stylesheet;
55use style_traits::CSSPixel;
56use uuid::Uuid;
57use xml5ever::{local_name, serialize as xml_serialize};
58
59use crate::conversions::Convert;
60use crate::document_loader::DocumentLoader;
61use crate::dom::attr::Attr;
62use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
63use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
64use crate::dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
65use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
66use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
67use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
68use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
69use crate::dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods;
70use crate::dom::bindings::codegen::Bindings::NodeBinding::{
71    GetRootNodeOptions, NodeConstants, NodeMethods,
72};
73use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
74use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
75use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Binding::ShadowRootMethods;
76use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
77    ShadowRootMode, SlotAssignmentMode,
78};
79use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
80use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
81use crate::dom::bindings::conversions::{self, DerivedFrom};
82use crate::dom::bindings::domname::namespace_from_domstring;
83use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
84use crate::dom::bindings::inheritance::{
85    Castable, CharacterDataTypeId, ElementTypeId, EventTargetTypeId, HTMLElementTypeId, NodeTypeId,
86    SVGElementTypeId, SVGGraphicsElementTypeId, TextTypeId,
87};
88use crate::dom::bindings::reflector::{
89    DomObject, DomObjectWrap, reflect_dom_object_with_proto_and_cx,
90};
91use crate::dom::bindings::root::{
92    Dom, DomRoot, DomSlice, LayoutDom, MutNullableDom, ToLayout, UnrootedDom,
93};
94use crate::dom::bindings::str::{DOMString, USVString};
95use crate::dom::characterdata::{CharacterData, LayoutCharacterDataHelpers};
96use crate::dom::css::cssstylesheet::CSSStyleSheet;
97use crate::dom::css::stylesheetlist::StyleSheetListOwner;
98use crate::dom::customelementregistry::{
99    CallbackReaction, CustomElementRegistry, try_upgrade_element,
100};
101use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
102use crate::dom::documentfragment::DocumentFragment;
103use crate::dom::documenttype::DocumentType;
104use crate::dom::element::{
105    AttributeMutationReason, CustomElementCreationMode, Element, ElementCreator,
106};
107use crate::dom::event::{Event, EventBubbles, EventCancelable, EventFlags};
108use crate::dom::eventtarget::EventTarget;
109use crate::dom::globalscope::GlobalScope;
110use crate::dom::html::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers};
111use crate::dom::html::htmlcollection::HTMLCollection;
112use crate::dom::html::htmlelement::HTMLElement;
113use crate::dom::html::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
114use crate::dom::html::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
115use crate::dom::html::htmllinkelement::HTMLLinkElement;
116use crate::dom::html::htmlslotelement::{HTMLSlotElement, Slottable};
117use crate::dom::html::htmlstyleelement::HTMLStyleElement;
118use crate::dom::html::htmltextareaelement::{
119    HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers,
120};
121use crate::dom::html::htmlvideoelement::{HTMLVideoElement, LayoutHTMLVideoElementHelpers};
122use crate::dom::html::input_element::{HTMLInputElement, LayoutHTMLInputElementHelpers};
123use crate::dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserver};
124use crate::dom::node::nodelist::NodeList;
125use crate::dom::pointerevent::{PointerEvent, PointerId};
126use crate::dom::processinginstruction::ProcessingInstruction;
127use crate::dom::range::WeakRangeVec;
128use crate::dom::raredata::NodeRareData;
129use crate::dom::servoparser::html::HtmlSerialize;
130use crate::dom::servoparser::{ServoParser, serialize_html_fragment};
131use crate::dom::shadowroot::{IsUserAgentWidget, LayoutShadowRootHelpers, ShadowRoot};
132use crate::dom::svg::svgsvgelement::{LayoutSVGSVGElementHelpers, SVGSVGElement};
133use crate::dom::text::Text;
134use crate::dom::types::{CDATASection, KeyboardEvent};
135use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
136use crate::dom::window::Window;
137use crate::layout_dom::ServoLayoutNode;
138use crate::script_runtime::CanGc;
139use crate::script_thread::ScriptThread;
140
141//
142// The basic Node structure
143//
144
145/// An HTML node.
146#[dom_struct]
147pub struct Node {
148    /// The JavaScript reflector for this node.
149    eventtarget: EventTarget,
150
151    /// The parent of this node.
152    parent_node: MutNullableDom<Node>,
153
154    /// The first child of this node.
155    first_child: MutNullableDom<Node>,
156
157    /// The last child of this node.
158    last_child: MutNullableDom<Node>,
159
160    /// The next sibling of this node.
161    next_sibling: MutNullableDom<Node>,
162
163    /// The previous sibling of this node.
164    prev_sibling: MutNullableDom<Node>,
165
166    /// The document that this node belongs to.
167    owner_doc: MutNullableDom<Document>,
168
169    /// Rare node data.
170    rare_data: DomRefCell<Option<Box<NodeRareData>>>,
171
172    /// The live count of children of this node.
173    children_count: Cell<u32>,
174
175    /// A bitfield of flags for node items.
176    flags: Cell<NodeFlags>,
177
178    /// The maximum version of any inclusive descendant of this node.
179    inclusive_descendants_version: Cell<u64>,
180
181    /// Style data for this node. This is accessed and mutated by style
182    /// passes and is used to lay out this node and populate layout data.
183    #[no_trace]
184    style_data: DomRefCell<Option<Box<StyleData>>>,
185
186    /// Layout data for this node. This is populated during layout and can
187    /// be used for incremental relayout and script queries.
188    #[no_trace]
189    layout_data: DomRefCell<Option<Box<GenericLayoutData>>>,
190}
191
192impl fmt::Debug for Node {
193    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
194        if matches!(self.type_id(), NodeTypeId::Element(_)) {
195            let el = self.downcast::<Element>().unwrap();
196            el.fmt(f)
197        } else {
198            write!(f, "[Node({:?})]", self.type_id())
199        }
200    }
201}
202
203/// Flags for node items
204#[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
205pub(crate) struct NodeFlags(u16);
206
207bitflags! {
208    impl NodeFlags: u16 {
209        /// Specifies whether this node is in a document.
210        ///
211        /// <https://dom.spec.whatwg.org/#in-a-document-tree>
212        const IS_IN_A_DOCUMENT_TREE = 1 << 0;
213
214        /// Specifies whether this node needs style recalc on next reflow.
215        const HAS_DIRTY_DESCENDANTS = 1 << 1;
216
217        /// Specifies whether or not there is an authentic click in progress on
218        /// this element.
219        const CLICK_IN_PROGRESS = 1 << 2;
220
221        // There are three free bits here.
222
223        /// Specifies whether the parser has set an associated form owner for
224        /// this element. Only applicable for form-associatable elements.
225        const PARSER_ASSOCIATED_FORM_OWNER = 1 << 6;
226
227        /// Whether this element has a snapshot stored due to a style or
228        /// attribute change.
229        ///
230        /// See the `style::restyle_hints` module.
231        const HAS_SNAPSHOT = 1 << 7;
232
233        /// Whether this element has already handled the stored snapshot.
234        const HANDLED_SNAPSHOT = 1 << 8;
235
236        /// Whether this node participates in a shadow tree.
237        const IS_IN_SHADOW_TREE = 1 << 9;
238
239        /// Specifies whether this node's shadow-including root is a document.
240        ///
241        /// <https://dom.spec.whatwg.org/#connected>
242        const IS_CONNECTED = 1 << 10;
243
244        /// Whether this node has a weird parser insertion mode. i.e whether setting innerHTML
245        /// needs extra work or not
246        const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
247
248        /// Whether this node resides in UA shadow DOM. Element within UA Shadow DOM
249        /// will have a different style computation behavior
250        const IS_IN_UA_WIDGET = 1 << 12;
251
252        /// Whether this node has a pseudo-element style which uses `attr()` in the `content` attribute.
253        const USES_ATTR_IN_CONTENT_ATTRIBUTE = 1 << 13;
254    }
255}
256
257/// suppress observers flag
258/// <https://dom.spec.whatwg.org/#concept-node-insert>
259/// <https://dom.spec.whatwg.org/#concept-node-remove>
260#[derive(Clone, Copy, MallocSizeOf)]
261enum SuppressObserver {
262    Suppressed,
263    Unsuppressed,
264}
265
266pub(crate) enum ForceSlottableNodeReconciliation {
267    Force,
268    Skip,
269}
270
271impl Node {
272    /// Adds a new child to the end of this node's list of children.
273    ///
274    /// Fails unless `new_child` is disconnected from the tree.
275    fn add_child(&self, cx: &mut JSContext, new_child: &Node, before: Option<&Node>) {
276        assert!(new_child.parent_node.get().is_none());
277        assert!(new_child.prev_sibling.get().is_none());
278        assert!(new_child.next_sibling.get().is_none());
279        match before {
280            Some(before) => {
281                assert!(before.parent_node.get().as_deref() == Some(self));
282                let prev_sibling = before.GetPreviousSibling();
283                match prev_sibling {
284                    None => {
285                        assert!(self.first_child.get().as_deref() == Some(before));
286                        self.first_child.set(Some(new_child));
287                    },
288                    Some(ref prev_sibling) => {
289                        prev_sibling.next_sibling.set(Some(new_child));
290                        new_child.prev_sibling.set(Some(prev_sibling));
291                    },
292                }
293                before.prev_sibling.set(Some(new_child));
294                new_child.next_sibling.set(Some(before));
295            },
296            None => {
297                let last_child = self.GetLastChild();
298                match last_child {
299                    None => self.first_child.set(Some(new_child)),
300                    Some(ref last_child) => {
301                        assert!(last_child.next_sibling.get().is_none());
302                        last_child.next_sibling.set(Some(new_child));
303                        new_child.prev_sibling.set(Some(last_child));
304                    },
305                }
306
307                self.last_child.set(Some(new_child));
308            },
309        }
310
311        new_child.parent_node.set(Some(self));
312        self.children_count.set(self.children_count.get() + 1);
313
314        let parent_is_in_a_document_tree = self.is_in_a_document_tree();
315        let parent_in_shadow_tree = self.is_in_a_shadow_tree();
316        let parent_is_connected = self.is_connected();
317        let parent_is_in_ua_widget = self.is_in_ua_widget();
318
319        let context = BindContext::new(self, IsShadowTree::No);
320
321        for node in new_child.traverse_preorder(ShadowIncluding::No) {
322            if parent_in_shadow_tree {
323                if let Some(shadow_root) = self.containing_shadow_root() {
324                    node.set_containing_shadow_root(Some(&*shadow_root));
325                }
326                debug_assert!(node.containing_shadow_root().is_some());
327            }
328            node.set_flag(
329                NodeFlags::IS_IN_A_DOCUMENT_TREE,
330                parent_is_in_a_document_tree,
331            );
332            node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, parent_in_shadow_tree);
333            node.set_flag(NodeFlags::IS_CONNECTED, parent_is_connected);
334            node.set_flag(NodeFlags::IS_IN_UA_WIDGET, parent_is_in_ua_widget);
335
336            // Out-of-document elements never have the descendants flag set.
337            debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
338            vtable_for(&node).bind_to_tree(cx, &context);
339        }
340    }
341
342    /// Implements the "unsafely set HTML" algorithm as specified in:
343    /// <https://html.spec.whatwg.org/multipage/#concept-unsafely-set-html>
344    pub(crate) fn unsafely_set_html(
345        target: &Node,
346        context_element: &Element,
347        html: DOMString,
348        cx: &mut JSContext,
349    ) {
350        // Step 1. Let newChildren be the result of the HTML fragment parsing algorithm.
351        let new_children = ServoParser::parse_html_fragment(context_element, html, true, cx);
352
353        // Step 2. Let fragment be a new DocumentFragment whose node document is contextElement's node document.
354
355        let context_document = context_element.owner_document();
356        let fragment = DocumentFragment::new(cx, &context_document);
357
358        // Step 3. For each node in newChildren, append node to fragment.
359        for child in new_children {
360            fragment.upcast::<Node>().AppendChild(cx, &child).unwrap();
361        }
362
363        // Step 4. Replace all with fragment within target.
364        Node::replace_all(cx, Some(fragment.upcast()), target);
365    }
366
367    /// Clear this [`Node`]'s layout data and also clear the layout data of all children.
368    /// Note that clears layout data from all non-flat tree descendants and flat tree
369    /// descendants.
370    pub(crate) fn remove_layout_boxes_from_subtree(&self) {
371        for node in self.traverse_preorder(ShadowIncluding::Yes) {
372            node.layout_data.borrow_mut().take();
373        }
374    }
375
376    pub(crate) fn clean_up_style_and_layout_data(&self) {
377        self.owner_doc().cancel_animations_for_node(self);
378        self.style_data.borrow_mut().take();
379        self.layout_data.borrow_mut().take();
380    }
381
382    /// Clean up flags and runs steps 11-14 of remove a node.
383    /// <https://dom.spec.whatwg.org/#concept-node-remove>
384    pub(crate) fn complete_remove_subtree(root: &Node, context: &UnbindContext, can_gc: CanGc) {
385        // Flags that reset when a node is disconnected
386        const RESET_FLAGS: NodeFlags = NodeFlags::IS_IN_A_DOCUMENT_TREE
387            .union(NodeFlags::IS_CONNECTED)
388            .union(NodeFlags::HAS_DIRTY_DESCENDANTS)
389            .union(NodeFlags::HAS_SNAPSHOT)
390            .union(NodeFlags::HANDLED_SNAPSHOT);
391
392        for node in root.traverse_preorder(ShadowIncluding::No) {
393            node.set_flag(RESET_FLAGS | NodeFlags::IS_IN_SHADOW_TREE, false);
394
395            // If the element has a shadow root attached to it then we traverse that as well,
396            // but without touching the IS_IN_SHADOW_TREE flags of the children
397            if let Some(shadow_root) = node.downcast::<Element>().and_then(Element::shadow_root) {
398                for node in shadow_root
399                    .upcast::<Node>()
400                    .traverse_preorder(ShadowIncluding::Yes)
401                {
402                    node.set_flag(RESET_FLAGS, false);
403                }
404            }
405        }
406
407        // Step 12.
408        let is_parent_connected = context.parent.is_connected();
409        let custom_element_reaction_stack = ScriptThread::custom_element_reaction_stack();
410
411        // Since both the initial traversal in light dom and the inner traversal
412        // in shadow DOM share the same code, we define a closure to prevent omissions.
413        let cleanup_node = |node: &Node| {
414            node.clean_up_style_and_layout_data();
415
416            // Step 11 & 14.1. Run the removing steps.
417            // This needs to be in its own loop, because unbind_from_tree may
418            // rely on the state of IS_IN_DOC of the context node's descendants,
419            // e.g. when removing a <form>.
420            vtable_for(node).unbind_from_tree(context, can_gc);
421
422            // Step 12 & 14.2. Enqueue disconnected custom element reactions.
423            if is_parent_connected {
424                if let Some(element) = node.as_custom_element() {
425                    custom_element_reaction_stack.enqueue_callback_reaction(
426                        &element,
427                        CallbackReaction::Disconnected,
428                        None,
429                    );
430                }
431            }
432        };
433
434        for node in root.traverse_preorder(ShadowIncluding::No) {
435            cleanup_node(&node);
436
437            // Make sure that we don't accidentally initialize the rare data for this node
438            // by setting it to None
439            if node.containing_shadow_root().is_some() {
440                // Reset the containing shadowRoot after we unbind the node, since some elements
441                // require the containing shadowRoot for cleanup logic (e.g. <style>).
442                node.set_containing_shadow_root(None);
443            }
444
445            // If the element has a shadow root attached to it then we traverse that as well,
446            // but without resetting the contained shadow root
447            if let Some(shadow_root) = node.downcast::<Element>().and_then(Element::shadow_root) {
448                for node in shadow_root
449                    .upcast::<Node>()
450                    .traverse_preorder(ShadowIncluding::Yes)
451                {
452                    cleanup_node(&node);
453                }
454            }
455        }
456    }
457
458    pub(crate) fn complete_move_subtree(root: &Node) {
459        // Flags that reset when a node is moved
460        const RESET_FLAGS: NodeFlags = NodeFlags::IS_IN_A_DOCUMENT_TREE
461            .union(NodeFlags::IS_CONNECTED)
462            .union(NodeFlags::HAS_DIRTY_DESCENDANTS)
463            .union(NodeFlags::HAS_SNAPSHOT)
464            .union(NodeFlags::HANDLED_SNAPSHOT);
465
466        // Since both the initial traversal in light dom and the inner traversal
467        // in shadow DOM share the same code, we define a closure to prevent omissions.
468        let cleanup_node = |node: &Node| {
469            node.style_data.borrow_mut().take();
470            node.layout_data.borrow_mut().take();
471        };
472
473        for node in root.traverse_preorder(ShadowIncluding::No) {
474            node.set_flag(RESET_FLAGS | NodeFlags::IS_IN_SHADOW_TREE, false);
475            cleanup_node(&node);
476
477            // Make sure that we don't accidentally initialize the rare data for this node
478            // by setting it to None
479            if node.containing_shadow_root().is_some() {
480                // Reset the containing shadowRoot after we unbind the node, since some elements
481                // require the containing shadowRoot for cleanup logic (e.g. <style>).
482                node.set_containing_shadow_root(None);
483            }
484
485            // If the element has a shadow root attached to it then we traverse that as well,
486            // but without touching the IS_IN_SHADOW_TREE flags of the children,
487            // and without resetting the contained shadow root
488            if let Some(shadow_root) = node.downcast::<Element>().and_then(Element::shadow_root) {
489                for node in shadow_root
490                    .upcast::<Node>()
491                    .traverse_preorder(ShadowIncluding::Yes)
492                {
493                    node.set_flag(RESET_FLAGS, false);
494                    cleanup_node(&node);
495                }
496            }
497        }
498    }
499
500    /// Removes the given child from this node's list of children.
501    ///
502    /// Fails unless `child` is a child of this node.
503    fn remove_child(&self, child: &Node, cached_index: Option<u32>, can_gc: CanGc) {
504        assert!(child.parent_node.get().as_deref() == Some(self));
505        self.note_dirty_descendants();
506
507        let prev_sibling = child.GetPreviousSibling();
508        match prev_sibling {
509            None => {
510                self.first_child.set(child.next_sibling.get().as_deref());
511            },
512            Some(ref prev_sibling) => {
513                prev_sibling
514                    .next_sibling
515                    .set(child.next_sibling.get().as_deref());
516            },
517        }
518        let next_sibling = child.GetNextSibling();
519        match next_sibling {
520            None => {
521                self.last_child.set(child.prev_sibling.get().as_deref());
522            },
523            Some(ref next_sibling) => {
524                next_sibling
525                    .prev_sibling
526                    .set(child.prev_sibling.get().as_deref());
527            },
528        }
529
530        let context = UnbindContext::new(
531            self,
532            prev_sibling.as_deref(),
533            next_sibling.as_deref(),
534            cached_index,
535        );
536
537        child.prev_sibling.set(None);
538        child.next_sibling.set(None);
539        child.parent_node.set(None);
540        self.children_count.set(self.children_count.get() - 1);
541
542        Self::complete_remove_subtree(child, &context, can_gc);
543    }
544
545    fn move_child(&self, child: &Node) {
546        assert!(child.parent_node.get().as_deref() == Some(self));
547        self.note_dirty_descendants();
548
549        child.prev_sibling.set(None);
550        child.next_sibling.set(None);
551        child.parent_node.set(None);
552        self.children_count.set(self.children_count.get() - 1);
553        Self::complete_move_subtree(child)
554    }
555
556    pub(crate) fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
557        UntrustedNodeAddress(self.reflector().get_jsobject().get() as *const c_void)
558    }
559
560    pub(crate) fn to_opaque(&self) -> OpaqueNode {
561        OpaqueNode(self.reflector().get_jsobject().get() as usize)
562    }
563
564    pub(crate) fn as_custom_element(&self) -> Option<DomRoot<Element>> {
565        self.downcast::<Element>().and_then(|element| {
566            if element.is_custom() {
567                assert!(element.get_custom_element_definition().is_some());
568                Some(DomRoot::from_ref(element))
569            } else {
570                None
571            }
572        })
573    }
574
575    /// <https://html.spec.whatwg.org/multipage/#fire-a-synthetic-pointer-event>
576    pub(crate) fn fire_synthetic_pointer_event_not_trusted(&self, event_type: Atom, can_gc: CanGc) {
577        // Spec says the choice of which global to create the pointer event
578        // on is not well-defined,
579        // and refers to heycam/webidl#135
580        let window = self.owner_window();
581
582        // <https://w3c.github.io/pointerevents/#the-click-auxclick-and-contextmenu-events>
583        let pointer_event = PointerEvent::new(
584            &window, // ambiguous in spec
585            event_type,
586            EventBubbles::Bubbles,              // Step 3: bubbles
587            EventCancelable::Cancelable,        // Step 3: cancelable
588            Some(&window),                      // Step 7: view
589            0,                                  // detail uninitialized
590            Point2D::zero(),                    // coordinates uninitialized
591            Point2D::zero(),                    // coordinates uninitialized
592            Point2D::zero(),                    // coordinates uninitialized
593            Modifiers::empty(),                 // empty modifiers
594            0,                                  // button, left mouse button
595            0,                                  // buttons
596            None,                               // related_target
597            None,                               // point_in_target
598            PointerId::NonPointerDevice as i32, // pointer_id
599            1,                                  // width
600            1,                                  // height
601            0.5,                                // pressure
602            0.0,                                // tangential_pressure
603            0,                                  // tilt_x
604            0,                                  // tilt_y
605            0,                                  // twist
606            PI / 2.0,                           // altitude_angle
607            0.0,                                // azimuth_angle
608            DOMString::from(""),                // pointer_type
609            false,                              // is_primary
610            vec![],                             // coalesced_events
611            vec![],                             // predicted_events
612            can_gc,
613        );
614
615        // Step 4. Set event's composed flag.
616        pointer_event.upcast::<Event>().set_composed(true);
617
618        // Step 5. If the not trusted flag is set, initialize event's isTrusted attribute to false.
619        pointer_event.upcast::<Event>().set_trusted(false);
620
621        // Step 6,8. TODO keyboard modifiers
622
623        pointer_event
624            .upcast::<Event>()
625            .dispatch(self.upcast::<EventTarget>(), false, can_gc);
626    }
627
628    pub(crate) fn parent_directionality(&self) -> String {
629        let mut current = self.GetParentNode();
630
631        loop {
632            match current {
633                Some(node) => {
634                    if let Some(directionality) = node
635                        .downcast::<HTMLElement>()
636                        .and_then(|html_element| html_element.directionality())
637                    {
638                        return directionality;
639                    } else {
640                        current = node.GetParentNode();
641                    }
642                },
643                None => return "ltr".to_owned(),
644            }
645        }
646    }
647}
648
649impl Node {
650    fn rare_data(&self) -> Ref<'_, Option<Box<NodeRareData>>> {
651        self.rare_data.borrow()
652    }
653
654    fn ensure_rare_data(&self) -> RefMut<'_, Box<NodeRareData>> {
655        let mut rare_data = self.rare_data.borrow_mut();
656        if rare_data.is_none() {
657            *rare_data = Some(Default::default());
658        }
659        RefMut::map(rare_data, |rare_data| rare_data.as_mut().unwrap())
660    }
661
662    /// Returns true if this node is before `other` in the same connected DOM
663    /// tree.
664    pub(crate) fn is_before(&self, other: &Node) -> bool {
665        let cmp = other.CompareDocumentPosition(self);
666        if cmp & NodeConstants::DOCUMENT_POSITION_DISCONNECTED != 0 {
667            return false;
668        }
669
670        cmp & NodeConstants::DOCUMENT_POSITION_PRECEDING != 0
671    }
672
673    /// Return all registered mutation observers for this node. Lazily initialize the
674    /// raredata if it does not exist.
675    pub(crate) fn registered_mutation_observers_mut(&self) -> RefMut<'_, Vec<RegisteredObserver>> {
676        RefMut::map(self.ensure_rare_data(), |rare_data| {
677            &mut rare_data.mutation_observers
678        })
679    }
680
681    pub(crate) fn registered_mutation_observers(&self) -> Option<Ref<'_, Vec<RegisteredObserver>>> {
682        let rare_data: Ref<'_, _> = self.rare_data.borrow();
683
684        if rare_data.is_none() {
685            return None;
686        }
687        Some(Ref::map(rare_data, |rare_data| {
688            &rare_data.as_ref().unwrap().mutation_observers
689        }))
690    }
691
692    /// Add a new mutation observer for a given node.
693    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
694    pub(crate) fn add_mutation_observer(&self, observer: RegisteredObserver) {
695        self.ensure_rare_data().mutation_observers.push(observer);
696    }
697
698    /// Removes the mutation observer for a given node.
699    pub(crate) fn remove_mutation_observer(&self, observer: &MutationObserver) {
700        self.ensure_rare_data()
701            .mutation_observers
702            .retain(|reg_obs| &*reg_obs.observer != observer)
703    }
704
705    /// Dumps the subtree rooted at this node, for debugging.
706    pub(crate) fn dump(&self) {
707        self.dump_indent(0);
708    }
709
710    /// Dumps the node tree, for debugging, with indentation.
711    pub(crate) fn dump_indent(&self, indent: u32) {
712        let mut s = String::new();
713        for _ in 0..indent {
714            s.push_str("    ");
715        }
716
717        s.push_str(&self.debug_str());
718        debug!("{:?}", s);
719
720        // FIXME: this should have a pure version?
721        for kid in self.children() {
722            kid.dump_indent(indent + 1)
723        }
724    }
725
726    /// Returns a string that describes this node.
727    pub(crate) fn debug_str(&self) -> String {
728        format!("{:?}", self.type_id())
729    }
730
731    /// <https://dom.spec.whatwg.org/#in-a-document-tree>
732    pub(crate) fn is_in_a_document_tree(&self) -> bool {
733        self.flags.get().contains(NodeFlags::IS_IN_A_DOCUMENT_TREE)
734    }
735
736    /// Return true iff node's root is a shadow-root.
737    pub(crate) fn is_in_a_shadow_tree(&self) -> bool {
738        self.flags.get().contains(NodeFlags::IS_IN_SHADOW_TREE)
739    }
740
741    pub(crate) fn has_weird_parser_insertion_mode(&self) -> bool {
742        self.flags
743            .get()
744            .contains(NodeFlags::HAS_WEIRD_PARSER_INSERTION_MODE)
745    }
746
747    pub(crate) fn set_weird_parser_insertion_mode(&self) {
748        self.set_flag(NodeFlags::HAS_WEIRD_PARSER_INSERTION_MODE, true)
749    }
750
751    /// <https://dom.spec.whatwg.org/#connected>
752    pub(crate) fn is_connected(&self) -> bool {
753        self.flags.get().contains(NodeFlags::IS_CONNECTED)
754    }
755
756    pub(crate) fn set_in_ua_widget(&self, in_ua_widget: bool) {
757        self.set_flag(NodeFlags::IS_IN_UA_WIDGET, in_ua_widget)
758    }
759
760    pub(crate) fn is_in_ua_widget(&self) -> bool {
761        self.flags.get().contains(NodeFlags::IS_IN_UA_WIDGET)
762    }
763
764    /// Returns the type ID of this node.
765    pub(crate) fn type_id(&self) -> NodeTypeId {
766        match *self.eventtarget.type_id() {
767            EventTargetTypeId::Node(type_id) => type_id,
768            _ => unreachable!(),
769        }
770    }
771
772    /// <https://dom.spec.whatwg.org/#concept-node-length>
773    pub(crate) fn len(&self) -> u32 {
774        match self.type_id() {
775            NodeTypeId::DocumentType => 0,
776            NodeTypeId::CharacterData(_) => self.downcast::<CharacterData>().unwrap().Length(),
777            _ => self.children_count(),
778        }
779    }
780
781    pub(crate) fn is_empty(&self) -> bool {
782        // A node is considered empty if its length is 0.
783        self.len() == 0
784    }
785
786    /// <https://dom.spec.whatwg.org/#concept-tree-index>
787    pub(crate) fn index(&self) -> u32 {
788        self.preceding_siblings().count() as u32
789    }
790
791    /// Returns true if this node has a parent.
792    pub(crate) fn has_parent(&self) -> bool {
793        self.parent_node.get().is_some()
794    }
795
796    pub(crate) fn children_count(&self) -> u32 {
797        self.children_count.get()
798    }
799
800    pub(crate) fn ranges(&self) -> RefMut<'_, WeakRangeVec> {
801        RefMut::map(self.ensure_rare_data(), |rare_data| &mut rare_data.ranges)
802    }
803
804    pub(crate) fn ranges_is_empty(&self) -> bool {
805        self.rare_data()
806            .as_ref()
807            .is_none_or(|data| data.ranges.is_empty())
808    }
809
810    #[inline]
811    pub(crate) fn is_doctype(&self) -> bool {
812        self.type_id() == NodeTypeId::DocumentType
813    }
814
815    pub(crate) fn get_flag(&self, flag: NodeFlags) -> bool {
816        self.flags.get().contains(flag)
817    }
818
819    pub(crate) fn set_flag(&self, flag: NodeFlags, value: bool) {
820        let mut flags = self.flags.get();
821
822        if value {
823            flags.insert(flag);
824        } else {
825            flags.remove(flag);
826        }
827
828        self.flags.set(flags);
829    }
830
831    // FIXME(emilio): This and the function below should move to Element.
832    pub(crate) fn note_dirty_descendants(&self) {
833        self.owner_doc().note_node_with_dirty_descendants(self);
834    }
835
836    pub(crate) fn has_dirty_descendants(&self) -> bool {
837        self.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
838    }
839
840    pub(crate) fn rev_version(&self) {
841        // The new version counter is 1 plus the max of the node's current version counter,
842        // its descendants version, and the document's version. Normally, this will just be
843        // the document's version, but we do have to deal with the case where the node has moved
844        // document, so may have a higher version count than its owning document.
845        let doc: DomRoot<Node> = DomRoot::upcast(self.owner_doc());
846        let version = cmp::max(
847            self.inclusive_descendants_version(),
848            doc.inclusive_descendants_version(),
849        ) + 1;
850
851        // This `while` loop is equivalent to iterating over the non-shadow-inclusive ancestors
852        // without creating intermediate rooted DOM objects.
853        let mut node = &MutNullableDom::new(Some(self));
854        while let Some(p) = node.if_is_some(|p| {
855            p.inclusive_descendants_version.set(version);
856            &p.parent_node
857        }) {
858            node = p
859        }
860        doc.inclusive_descendants_version.set(version);
861    }
862
863    pub(crate) fn dirty(&self, damage: NodeDamage) {
864        self.rev_version();
865        if !self.is_connected() {
866            return;
867        }
868
869        match self.type_id() {
870            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
871                // For content changes in text nodes, we should accurately use
872                // [`NodeDamage::ContentOrHeritage`] to mark the parent node, thereby
873                // reducing the scope of incremental box tree construction.
874                self.parent_node
875                    .get()
876                    .unwrap()
877                    .dirty(NodeDamage::ContentOrHeritage)
878            },
879            NodeTypeId::Element(_) => self.downcast::<Element>().unwrap().restyle(damage),
880            NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => self
881                .downcast::<ShadowRoot>()
882                .unwrap()
883                .Host()
884                .upcast::<Element>()
885                .restyle(damage),
886            _ => {},
887        };
888    }
889
890    /// The maximum version number of this node's descendants, including itself
891    pub(crate) fn inclusive_descendants_version(&self) -> u64 {
892        self.inclusive_descendants_version.get()
893    }
894
895    /// Iterates over this node and all its descendants, in preorder.
896    pub(crate) fn traverse_preorder(&self, shadow_including: ShadowIncluding) -> TreeIterator {
897        TreeIterator::new(self, shadow_including)
898    }
899
900    /// Iterates over this node and all its descendants, in preorder. We take &NoGC to prevent GC which allows us to avoid rooting.
901    pub(crate) fn traverse_preorder_non_rooting<'a, 'b>(
902        &'a self,
903        no_gc: &'b NoGC,
904        shadow_including: ShadowIncluding,
905    ) -> UnrootedTreeIterator<'a, 'b>
906    where
907        'b: 'a,
908    {
909        UnrootedTreeIterator::new(self, shadow_including, no_gc)
910    }
911
912    pub(crate) fn inclusively_following_siblings(
913        &self,
914    ) -> impl Iterator<Item = DomRoot<Node>> + use<> {
915        SimpleNodeIterator {
916            current: Some(DomRoot::from_ref(self)),
917            next_node: |n| n.GetNextSibling(),
918        }
919    }
920
921    pub(crate) fn inclusively_following_siblings_unrooted<'b>(
922        &self,
923        no_gc: &'b NoGC,
924    ) -> impl Iterator<Item = UnrootedDom<'b, Node>> + use<'b> {
925        UnrootedSimpleNodeIterator {
926            current: Some(UnrootedDom::from_dom(Dom::from_ref(self), no_gc)),
927            next_node: |n, no_gc| n.get_next_sibling_unrooted(no_gc),
928            no_gc,
929            phantom: PhantomData,
930        }
931    }
932
933    pub(crate) fn inclusively_preceding_siblings(
934        &self,
935    ) -> impl Iterator<Item = DomRoot<Node>> + use<> {
936        SimpleNodeIterator {
937            current: Some(DomRoot::from_ref(self)),
938            next_node: |n| n.GetPreviousSibling(),
939        }
940    }
941
942    pub(crate) fn inclusively_preceding_siblings_unrooted<'b>(
943        &self,
944        no_gc: &'b NoGC,
945    ) -> impl Iterator<Item = UnrootedDom<'b, Node>> + use<'b> {
946        UnrootedSimpleNodeIterator {
947            current: Some(UnrootedDom::from_dom(Dom::from_ref(self), no_gc)),
948            next_node: |n, no_gc| n.get_previous_sibling_unrooted(no_gc),
949            no_gc,
950            phantom: PhantomData,
951        }
952    }
953
954    pub(crate) fn common_ancestor(
955        &self,
956        other: &Node,
957        shadow_including: ShadowIncluding,
958    ) -> Option<DomRoot<Node>> {
959        self.inclusive_ancestors(shadow_including).find(|ancestor| {
960            other
961                .inclusive_ancestors(shadow_including)
962                .any(|node| node == *ancestor)
963        })
964    }
965
966    pub(crate) fn common_ancestor_in_flat_tree(&self, other: &Node) -> Option<DomRoot<Node>> {
967        self.inclusive_ancestors_in_flat_tree().find(|ancestor| {
968            other
969                .inclusive_ancestors_in_flat_tree()
970                .any(|node| node == *ancestor)
971        })
972    }
973
974    /// <https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor>
975    pub(crate) fn is_inclusive_ancestor_of(&self, child: &Node) -> bool {
976        // > An inclusive ancestor is an object or one of its ancestors.
977        self == child || self.is_ancestor_of(child)
978    }
979
980    /// <https://dom.spec.whatwg.org/#concept-tree-ancestor>
981    pub(crate) fn is_ancestor_of(&self, possible_descendant: &Node) -> bool {
982        // > An object A is called an ancestor of an object B if and only if B is a descendant of A.
983        let mut current = &possible_descendant.parent_node;
984        let mut done = false;
985
986        while let Some(node) = current.if_is_some(|node| {
987            done = node == self;
988            &node.parent_node
989        }) {
990            if done {
991                break;
992            }
993            current = node
994        }
995        done
996    }
997
998    /// <https://dom.spec.whatwg.org/#concept-tree-host-including-inclusive-ancestor>
999    fn is_host_including_inclusive_ancestor(&self, child: &Node) -> bool {
1000        // An object A is a host-including inclusive ancestor of an object B, if either A is an inclusive ancestor of B,
1001        // or if B’s root has a non-null host and A is a host-including inclusive ancestor of B’s root’s host.
1002        self.is_inclusive_ancestor_of(child) ||
1003            child
1004                .GetRootNode(&GetRootNodeOptions::empty())
1005                .downcast::<DocumentFragment>()
1006                .and_then(|fragment| fragment.host())
1007                .is_some_and(|host| self.is_host_including_inclusive_ancestor(host.upcast()))
1008    }
1009
1010    /// <https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor>
1011    pub(crate) fn is_shadow_including_inclusive_ancestor_of(&self, node: &Node) -> bool {
1012        node.inclusive_ancestors(ShadowIncluding::Yes)
1013            .any(|ancestor| &*ancestor == self)
1014    }
1015
1016    pub(crate) fn following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1017        SimpleNodeIterator {
1018            current: self.GetNextSibling(),
1019            next_node: |n| n.GetNextSibling(),
1020        }
1021    }
1022
1023    pub(crate) fn preceding_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1024        SimpleNodeIterator {
1025            current: self.GetPreviousSibling(),
1026            next_node: |n| n.GetPreviousSibling(),
1027        }
1028    }
1029
1030    pub(crate) fn following_nodes(&self, root: &Node) -> FollowingNodeIterator {
1031        FollowingNodeIterator {
1032            current: Some(DomRoot::from_ref(self)),
1033            root: DomRoot::from_ref(root),
1034        }
1035    }
1036
1037    pub(crate) fn preceding_nodes(&self, root: &Node) -> PrecedingNodeIterator {
1038        PrecedingNodeIterator {
1039            current: Some(DomRoot::from_ref(self)),
1040            root: DomRoot::from_ref(root),
1041        }
1042    }
1043
1044    /// Return an iterator that moves from `self` down the tree, choosing the last child
1045    /// at each step of the way.
1046    pub(crate) fn descending_last_children(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1047        SimpleNodeIterator {
1048            current: self.GetLastChild(),
1049            next_node: |n| n.GetLastChild(),
1050        }
1051    }
1052
1053    pub(crate) fn is_parent_of(&self, child: &Node) -> bool {
1054        child
1055            .parent_node
1056            .get()
1057            .is_some_and(|parent| &*parent == self)
1058    }
1059
1060    pub(crate) fn to_trusted_node_address(&self) -> TrustedNodeAddress {
1061        TrustedNodeAddress(self as *const Node as *const libc::c_void)
1062    }
1063
1064    pub(crate) fn padding(&self) -> Option<PhysicalSides> {
1065        self.owner_window().padding_query_without_reflow(self)
1066    }
1067
1068    pub(crate) fn content_box(&self) -> Option<Rect<Au, CSSPixel>> {
1069        self.owner_window()
1070            .box_area_query(self, BoxAreaType::Content, false)
1071    }
1072
1073    pub(crate) fn border_box(&self) -> Option<Rect<Au, CSSPixel>> {
1074        self.owner_window()
1075            .box_area_query(self, BoxAreaType::Border, false)
1076    }
1077
1078    pub(crate) fn padding_box(&self) -> Option<Rect<Au, CSSPixel>> {
1079        self.owner_window()
1080            .box_area_query(self, BoxAreaType::Padding, false)
1081    }
1082
1083    pub(crate) fn border_boxes(&self) -> CSSPixelRectIterator {
1084        self.owner_window()
1085            .box_areas_query(self, BoxAreaType::Border)
1086    }
1087
1088    pub(crate) fn client_rect(&self) -> Rect<i32, CSSPixel> {
1089        self.owner_window().client_rect_query(self)
1090    }
1091
1092    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth>
1093    /// <https://drafts.csswg.org/cssom-view/#dom-element-scrollheight>
1094    pub(crate) fn scroll_area(&self) -> Rect<i32, CSSPixel> {
1095        // "1. Let document be the element’s node document.""
1096        let document = self.owner_doc();
1097
1098        // "2. If document is not the active document, return zero and terminate these steps.""
1099        if !document.is_active() {
1100            return Rect::zero();
1101        }
1102
1103        // "3. Let viewport width/height be the width of the viewport excluding the width/height of the
1104        // scroll bar, if any, or zero if there is no viewport."
1105        let window = document.window();
1106        let viewport = Size2D::new(window.InnerWidth(), window.InnerHeight()).cast_unit();
1107
1108        let in_quirks_mode = document.quirks_mode() == QuirksMode::Quirks;
1109        let is_root = self.downcast::<Element>().is_some_and(|e| e.is_root());
1110        let is_body_element = self
1111            .downcast::<HTMLElement>()
1112            .is_some_and(|e| e.is_body_element());
1113
1114        // "4. If the element is the root element and document is not in quirks mode
1115        // return max(viewport scrolling area width/height, viewport width/height)."
1116        // "5. If the element is the body element, document is in quirks mode and the
1117        // element is not potentially scrollable, return max(viewport scrolling area
1118        // width, viewport width)."
1119        if (is_root && !in_quirks_mode) || (is_body_element && in_quirks_mode) {
1120            let viewport_scrolling_area = window.scrolling_area_query(None);
1121            return Rect::new(
1122                viewport_scrolling_area.origin,
1123                viewport_scrolling_area.size.max(viewport),
1124            );
1125        }
1126
1127        // "6. If the element does not have any associated box return zero and terminate
1128        // these steps."
1129        // "7. Return the width of the element’s scrolling area."
1130        window.scrolling_area_query(Some(self))
1131    }
1132
1133    pub(crate) fn effective_overflow(&self) -> Option<AxesOverflow> {
1134        self.owner_window().query_effective_overflow(self)
1135    }
1136
1137    pub(crate) fn effective_overflow_without_reflow(&self) -> Option<AxesOverflow> {
1138        self.owner_window()
1139            .query_effective_overflow_without_reflow(self)
1140    }
1141
1142    /// <https://dom.spec.whatwg.org/#dom-childnode-before>
1143    pub(crate) fn before(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
1144        // Step 1.
1145        let parent = &self.parent_node;
1146
1147        // Step 2.
1148        let parent = match parent.get() {
1149            None => return Ok(()),
1150            Some(parent) => parent,
1151        };
1152
1153        // Step 3.
1154        let viable_previous_sibling = first_node_not_in(self.preceding_siblings(), &nodes);
1155
1156        // Step 4.
1157        let node = self.owner_doc().node_from_nodes_and_strings(cx, nodes)?;
1158
1159        // Step 5.
1160        let viable_previous_sibling = match viable_previous_sibling {
1161            Some(ref viable_previous_sibling) => viable_previous_sibling.next_sibling.get(),
1162            None => parent.first_child.get(),
1163        };
1164
1165        // Step 6.
1166        Node::pre_insert(cx, &node, &parent, viable_previous_sibling.as_deref())?;
1167
1168        Ok(())
1169    }
1170
1171    /// <https://dom.spec.whatwg.org/#dom-childnode-after>
1172    pub(crate) fn after(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
1173        // Step 1.
1174        let parent = &self.parent_node;
1175
1176        // Step 2.
1177        let parent = match parent.get() {
1178            None => return Ok(()),
1179            Some(parent) => parent,
1180        };
1181
1182        // Step 3.
1183        let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
1184
1185        // Step 4.
1186        let node = self.owner_doc().node_from_nodes_and_strings(cx, nodes)?;
1187
1188        // Step 5.
1189        Node::pre_insert(cx, &node, &parent, viable_next_sibling.as_deref())?;
1190
1191        Ok(())
1192    }
1193
1194    /// <https://dom.spec.whatwg.org/#dom-childnode-replacewith>
1195    pub(crate) fn replace_with(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
1196        // Step 1. Let parent be this’s parent.
1197        let Some(parent) = self.GetParentNode() else {
1198            // Step 2. If parent is null, then return.
1199            return Ok(());
1200        };
1201
1202        // Step 3. Let viableNextSibling be this’s first following sibling not in nodes; otherwise null.
1203        let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
1204
1205        // Step 4. Let node be the result of converting nodes into a node, given nodes and this’s node document.
1206        let node = self.owner_doc().node_from_nodes_and_strings(cx, nodes)?;
1207
1208        if self.parent_node == Some(&*parent) {
1209            // Step 5. If this’s parent is parent, replace this with node within parent.
1210            parent.ReplaceChild(cx, &node, self)?;
1211        } else {
1212            // Step 6. Otherwise, pre-insert node into parent before viableNextSibling.
1213            Node::pre_insert(cx, &node, &parent, viable_next_sibling.as_deref())?;
1214        }
1215        Ok(())
1216    }
1217
1218    /// <https://dom.spec.whatwg.org/#dom-parentnode-prepend>
1219    pub(crate) fn prepend(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
1220        // Step 1.
1221        let doc = self.owner_doc();
1222        let node = doc.node_from_nodes_and_strings(cx, nodes)?;
1223        // Step 2.
1224        let first_child = self.first_child.get();
1225        Node::pre_insert(cx, &node, self, first_child.as_deref()).map(|_| ())
1226    }
1227
1228    /// <https://dom.spec.whatwg.org/#dom-parentnode-append>
1229    pub(crate) fn append(&self, cx: &mut JSContext, nodes: Vec<NodeOrString>) -> ErrorResult {
1230        // Step 1.
1231        let doc = self.owner_doc();
1232        let node = doc.node_from_nodes_and_strings(cx, nodes)?;
1233        // Step 2.
1234        self.AppendChild(cx, &node).map(|_| ())
1235    }
1236
1237    /// <https://dom.spec.whatwg.org/#dom-parentnode-replacechildren>
1238    pub(crate) fn replace_children(
1239        &self,
1240        cx: &mut JSContext,
1241        nodes: Vec<NodeOrString>,
1242    ) -> ErrorResult {
1243        // Step 1. Let node be the result of converting nodes into a node given nodes and this’s
1244        // node document.
1245        let doc = self.owner_doc();
1246        let node = doc.node_from_nodes_and_strings(cx, nodes)?;
1247
1248        // Step 2. Ensure pre-insert validity of node into this before null.
1249        Node::ensure_pre_insertion_validity(&node, self, None)?;
1250
1251        // Step 3. Replace all with node within this.
1252        Node::replace_all(cx, Some(&node), self);
1253        Ok(())
1254    }
1255
1256    /// <https://dom.spec.whatwg.org/#dom-parentnode-movebefore>
1257    pub(crate) fn move_before(
1258        &self,
1259        cx: &mut JSContext,
1260        node: &Node,
1261        child: Option<&Node>,
1262    ) -> ErrorResult {
1263        // Step 1. Let referenceChild be child.
1264        // Step 2. If referenceChild is node, then set referenceChild to node’s next sibling.
1265        let reference_child_root;
1266        let reference_child = match child {
1267            Some(child) if child == node => {
1268                reference_child_root = node.GetNextSibling();
1269                reference_child_root.as_deref()
1270            },
1271            _ => child,
1272        };
1273
1274        // Step 3. Move node into this before referenceChild.
1275        Node::move_fn(cx, node, self, reference_child)
1276    }
1277
1278    /// <https://dom.spec.whatwg.org/#move>
1279    fn move_fn(
1280        cx: &mut JSContext,
1281        node: &Node,
1282        new_parent: &Node,
1283        child: Option<&Node>,
1284    ) -> ErrorResult {
1285        // Step 1. If newParent’s shadow-including root is not the same as node’s shadow-including
1286        // root, then throw a "HierarchyRequestError" DOMException.
1287        // This has the side effect of ensuring that a move is only performed if newParent’s
1288        // connected is node’s connected.
1289        let mut options = GetRootNodeOptions::empty();
1290        options.composed = true;
1291        if new_parent.GetRootNode(&options) != node.GetRootNode(&options) {
1292            return Err(Error::HierarchyRequest(None));
1293        }
1294
1295        // Step 2. If node is a host-including inclusive ancestor of newParent, then throw a
1296        // "HierarchyRequestError" DOMException.
1297        if node.is_inclusive_ancestor_of(new_parent) {
1298            return Err(Error::HierarchyRequest(None));
1299        }
1300
1301        // Step 3. If child is non-null and its parent is not newParent, then throw a
1302        // "NotFoundError" DOMException.
1303        if let Some(child) = child {
1304            if !new_parent.is_parent_of(child) {
1305                return Err(Error::NotFound(None));
1306            }
1307        }
1308
1309        // Step 4. If node is not an Element or a CharacterData node, then throw a
1310        // "HierarchyRequestError" DOMException.
1311        // Step 5. If node is a Text node and newParent is a document, then throw a
1312        // "HierarchyRequestError" DOMException.
1313        match node.type_id() {
1314            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
1315                if new_parent.is::<Document>() {
1316                    return Err(Error::HierarchyRequest(None));
1317                }
1318            },
1319            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
1320            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
1321            NodeTypeId::Element(_) => (),
1322            NodeTypeId::DocumentFragment(_) |
1323            NodeTypeId::DocumentType |
1324            NodeTypeId::Document(_) |
1325            NodeTypeId::Attr => {
1326                return Err(Error::HierarchyRequest(None));
1327            },
1328        }
1329
1330        // Step 6. If newParent is a document, node is an Element node, and either newParent has an
1331        // element child, child is a doctype, or child is non-null and a doctype is following child
1332        // then throw a "HierarchyRequestError" DOMException.
1333        if new_parent.is::<Document>() && node.is::<Element>() {
1334            // either newParent has an element child
1335            if new_parent.child_elements().next().is_some() {
1336                return Err(Error::HierarchyRequest(None));
1337            }
1338
1339            // child is a doctype
1340            // or child is non-null and a doctype is following child
1341            if child.is_some_and(|child| {
1342                child
1343                    .inclusively_following_siblings_unrooted(cx.no_gc())
1344                    .any(|child| child.is_doctype())
1345            }) {
1346                return Err(Error::HierarchyRequest(None));
1347            }
1348        }
1349
1350        // Step 7. Let oldParent be node’s parent.
1351        // Step 8. Assert: oldParent is non-null.
1352        let old_parent = node
1353            .parent_node
1354            .get()
1355            .expect("old_parent should always be initialized");
1356
1357        // Step 9. Run the live range pre-remove steps, given node.
1358        let cached_index = Node::live_range_pre_remove_steps(node, &old_parent);
1359
1360        // TODO Step 10. For each NodeIterator object iterator whose root’s node document is node’s
1361        // node document: run the NodeIterator pre-remove steps given node and iterator.
1362
1363        // Step 11. Let oldPreviousSibling be node’s previous sibling.
1364        let old_previous_sibling = node.prev_sibling.get();
1365
1366        // Step 12. Let oldNextSibling be node’s next sibling.
1367        let old_next_sibling = node.next_sibling.get();
1368
1369        let prev_sibling = node.GetPreviousSibling();
1370        match prev_sibling {
1371            None => {
1372                old_parent
1373                    .first_child
1374                    .set(node.next_sibling.get().as_deref());
1375            },
1376            Some(ref prev_sibling) => {
1377                prev_sibling
1378                    .next_sibling
1379                    .set(node.next_sibling.get().as_deref());
1380            },
1381        }
1382        let next_sibling = node.GetNextSibling();
1383        match next_sibling {
1384            None => {
1385                old_parent
1386                    .last_child
1387                    .set(node.prev_sibling.get().as_deref());
1388            },
1389            Some(ref next_sibling) => {
1390                next_sibling
1391                    .prev_sibling
1392                    .set(node.prev_sibling.get().as_deref());
1393            },
1394        }
1395
1396        let mut context = MoveContext::new(
1397            Some(&old_parent),
1398            prev_sibling.as_deref(),
1399            next_sibling.as_deref(),
1400            cached_index,
1401        );
1402
1403        // Step 13. Remove node from oldParent’s children.
1404        old_parent.move_child(node);
1405
1406        // Step 14. If node is assigned, then run assign slottables for node’s assigned slot.
1407        if let Some(slot) = node.assigned_slot() {
1408            slot.assign_slottables();
1409        }
1410
1411        // Step 15. If oldParent’s root is a shadow root, and oldParent is a slot whose assigned
1412        // nodes is empty, then run signal a slot change for oldParent.
1413        if old_parent.is_in_a_shadow_tree() {
1414            if let Some(slot_element) = old_parent.downcast::<HTMLSlotElement>() {
1415                if !slot_element.has_assigned_nodes() {
1416                    slot_element.signal_a_slot_change();
1417                }
1418            }
1419        }
1420
1421        // Step 16. If node has an inclusive descendant that is a slot:
1422        let has_slot_descendant = node
1423            .traverse_preorder(ShadowIncluding::No)
1424            .any(|element| element.is::<HTMLSlotElement>());
1425        if has_slot_descendant {
1426            // Step 16.1. Run assign slottables for a tree with oldParent’s root.
1427            old_parent
1428                .GetRootNode(&GetRootNodeOptions::empty())
1429                .assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Skip);
1430
1431            // Step 16.2. Run assign slottables for a tree with node.
1432            node.assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Skip);
1433        }
1434
1435        // Step 17. If child is non-null:
1436        if let Some(child) = child {
1437            // Step 17.1. For each live range whose start node is newParent and start offset is
1438            // greater than child’s index: increase its start offset by 1.
1439            // Step 17.2. For each live range whose end node is newParent and end offset is greater
1440            // than child’s index: increase its end offset by 1.
1441            new_parent
1442                .ranges()
1443                .increase_above(new_parent, child.index(), 1)
1444        }
1445
1446        // Step 18. Let newPreviousSibling be child’s previous sibling if child is non-null, and
1447        // newParent’s last child otherwise.
1448        let new_previous_sibling = child.map_or_else(
1449            || new_parent.last_child.get(),
1450            |child| child.prev_sibling.get(),
1451        );
1452
1453        // Step 19. If child is null, then append node to newParent’s children.
1454        // Step 20. Otherwise, insert node into newParent’s children before child’s index.
1455        new_parent.add_child(cx, node, child);
1456
1457        // Step 21. If newParent is a shadow host whose shadow root’s slot assignment is "named" and
1458        // node is a slottable, then assign a slot for node.
1459        if let Some(shadow_root) = new_parent
1460            .downcast::<Element>()
1461            .and_then(Element::shadow_root)
1462        {
1463            if shadow_root.SlotAssignment() == SlotAssignmentMode::Named &&
1464                (node.is::<Element>() || node.is::<Text>())
1465            {
1466                rooted!(&in(cx) let slottable = Slottable(Dom::from_ref(node)));
1467                slottable.assign_a_slot();
1468            }
1469        }
1470
1471        // Step 22. If newParent’s root is a shadow root, and newParent is a slot whose assigned
1472        // nodes is empty, then run signal a slot change for newParent.
1473        if new_parent.is_in_a_shadow_tree() {
1474            if let Some(slot_element) = new_parent.downcast::<HTMLSlotElement>() {
1475                if !slot_element.has_assigned_nodes() {
1476                    slot_element.signal_a_slot_change();
1477                }
1478            }
1479        }
1480
1481        // Step 23. Run assign slottables for a tree with node’s root.
1482        node.GetRootNode(&GetRootNodeOptions::empty())
1483            .assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Skip);
1484
1485        // Step 24. For each shadow-including inclusive descendant inclusiveDescendant of node, in
1486        // shadow-including tree order:
1487        for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
1488            // Step 24.1. If inclusiveDescendant is node, then run the moving steps with
1489            // inclusiveDescendant and oldParent.
1490            // Otherwise, run the moving steps with inclusiveDescendant and null.
1491            if descendant.deref() == node {
1492                vtable_for(&descendant).moving_steps(&context, CanGc::from_cx(cx));
1493            } else {
1494                context.old_parent = None;
1495                vtable_for(&descendant).moving_steps(&context, CanGc::from_cx(cx));
1496            }
1497
1498            // Step 24.2. If inclusiveDescendant is custom and newParent is connected,
1499            if let Some(descendant) = descendant.downcast::<Element>() {
1500                if descendant.is_custom() && new_parent.is_connected() {
1501                    // then enqueue a custom element callback reaction with
1502                    // inclusiveDescendant, callback name "connectedMoveCallback", and « ».
1503                    let custom_element_reaction_stack =
1504                        ScriptThread::custom_element_reaction_stack();
1505                    custom_element_reaction_stack.enqueue_callback_reaction(
1506                        descendant,
1507                        CallbackReaction::ConnectedMove,
1508                        None,
1509                    );
1510                }
1511            }
1512        }
1513
1514        // Step 25. Queue a tree mutation record for oldParent with « », « node »,
1515        // oldPreviousSibling, and oldNextSibling.
1516        let moved = [node];
1517        let mutation = LazyCell::new(|| Mutation::ChildList {
1518            added: None,
1519            removed: Some(&moved),
1520            prev: old_previous_sibling.as_deref(),
1521            next: old_next_sibling.as_deref(),
1522        });
1523        MutationObserver::queue_a_mutation_record(&old_parent, mutation);
1524
1525        // Step 26. Queue a tree mutation record for newParent with « node », « »,
1526        // newPreviousSibling, and child.
1527        let mutation = LazyCell::new(|| Mutation::ChildList {
1528            added: Some(&moved),
1529            removed: None,
1530            prev: new_previous_sibling.as_deref(),
1531            next: child,
1532        });
1533        MutationObserver::queue_a_mutation_record(new_parent, mutation);
1534
1535        Ok(())
1536    }
1537
1538    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
1539    #[allow(unsafe_code)]
1540    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1541    pub(crate) fn query_selector(
1542        &self,
1543        selectors: DOMString,
1544    ) -> Fallible<Option<DomRoot<Element>>> {
1545        // > The querySelector(selectors) method steps are to return the first result of running scope-match
1546        // > a selectors string selectors against this, if the result is not an empty list; otherwise null.
1547        let document_url = self.owner_document().url().get_arc();
1548
1549        // SAFETY: traced_node is unrooted, but we have a reference to "self" so it won't be freed.
1550        let traced_node = Dom::from_ref(self);
1551
1552        let first_matching_element = with_layout_state(|| {
1553            let layout_node = unsafe { traced_node.to_layout() };
1554            ServoLayoutNode::from_layout_dom(layout_node)
1555                .scope_match_a_selectors_string::<QueryFirst>(document_url, &selectors.str())
1556        })?;
1557
1558        Ok(first_matching_element
1559            .map(|element| DomRoot::from_ref(unsafe { element.to_layout_dom().as_ref() })))
1560    }
1561
1562    /// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
1563    #[allow(unsafe_code)]
1564    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1565    pub(crate) fn query_selector_all(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
1566        // > The querySelectorAll(selectors) method steps are to return the static result of running scope-match
1567        // > a selectors string selectors against this.
1568        let document_url = self.owner_document().url().get_arc();
1569
1570        // SAFETY: traced_node is unrooted, but we have a reference to "self" so it won't be freed.
1571        let traced_node = Dom::from_ref(self);
1572        let matching_elements = with_layout_state(|| {
1573            let layout_node = unsafe { traced_node.to_layout() };
1574            ServoLayoutNode::from_layout_dom(layout_node)
1575                .scope_match_a_selectors_string::<QueryAll>(document_url, &selectors.str())
1576        })?;
1577        let iter = matching_elements
1578            .into_iter()
1579            .map(|element| DomRoot::from_ref(unsafe { element.to_layout_dom().as_ref() }))
1580            .map(DomRoot::upcast::<Node>);
1581
1582        // NodeList::new_simple_list immediately collects the iterator, so we're not leaking LayoutDom
1583        // elements here.
1584        Ok(NodeList::new_simple_list(
1585            &self.owner_window(),
1586            iter,
1587            CanGc::note(),
1588        ))
1589    }
1590
1591    pub(crate) fn ancestors(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1592        SimpleNodeIterator {
1593            current: self.GetParentNode(),
1594            next_node: |n| n.GetParentNode(),
1595        }
1596    }
1597
1598    /// <https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor>
1599    pub(crate) fn inclusive_ancestors(
1600        &self,
1601        shadow_including: ShadowIncluding,
1602    ) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1603        SimpleNodeIterator {
1604            current: Some(DomRoot::from_ref(self)),
1605            next_node: move |n| {
1606                if shadow_including == ShadowIncluding::Yes {
1607                    if let Some(shadow_root) = n.downcast::<ShadowRoot>() {
1608                        return Some(DomRoot::from_ref(shadow_root.Host().upcast::<Node>()));
1609                    }
1610                }
1611                n.GetParentNode()
1612            },
1613        }
1614    }
1615
1616    pub(crate) fn owner_doc(&self) -> DomRoot<Document> {
1617        self.owner_doc.get().unwrap()
1618    }
1619
1620    pub(crate) fn set_owner_doc(&self, document: &Document) {
1621        self.owner_doc.set(Some(document));
1622    }
1623
1624    pub(crate) fn containing_shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
1625        self.rare_data()
1626            .as_ref()?
1627            .containing_shadow_root
1628            .as_ref()
1629            .map(|sr| DomRoot::from_ref(&**sr))
1630    }
1631
1632    pub(crate) fn set_containing_shadow_root(&self, shadow_root: Option<&ShadowRoot>) {
1633        self.ensure_rare_data().containing_shadow_root = shadow_root.map(Dom::from_ref);
1634    }
1635
1636    pub(crate) fn is_in_html_doc(&self) -> bool {
1637        self.owner_doc().is_html_document()
1638    }
1639
1640    pub(crate) fn is_connected_with_browsing_context(&self) -> bool {
1641        self.is_connected() && self.owner_doc().browsing_context().is_some()
1642    }
1643
1644    pub(crate) fn children(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1645        SimpleNodeIterator {
1646            current: self.GetFirstChild(),
1647            next_node: |n| n.GetNextSibling(),
1648        }
1649    }
1650
1651    pub(crate) fn rev_children(&self) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1652        SimpleNodeIterator {
1653            current: self.GetLastChild(),
1654            next_node: |n| n.GetPreviousSibling(),
1655        }
1656    }
1657
1658    pub(crate) fn child_elements(&self) -> impl Iterator<Item = DomRoot<Element>> + use<> {
1659        self.children()
1660            .filter_map(DomRoot::downcast as fn(_) -> _)
1661            .peekable()
1662    }
1663
1664    pub(crate) fn remove_self(&self, cx: &mut JSContext) {
1665        if let Some(ref parent) = self.GetParentNode() {
1666            Node::remove(cx, self, parent, SuppressObserver::Unsuppressed);
1667        }
1668    }
1669
1670    /// Returns the node's `unique_id` if it has been computed before and `None` otherwise.
1671    pub(crate) fn unique_id_if_already_present(&self) -> Option<String> {
1672        Ref::filter_map(self.rare_data(), |rare_data| {
1673            rare_data
1674                .as_ref()
1675                .and_then(|rare_data| rare_data.unique_id.as_ref())
1676        })
1677        .ok()
1678        .map(|unique_id| unique_id.borrow().simple().to_string())
1679    }
1680
1681    pub(crate) fn unique_id(&self, pipeline: PipelineId) -> String {
1682        let mut rare_data = self.ensure_rare_data();
1683
1684        if rare_data.unique_id.is_none() {
1685            let node_id = UniqueId::new();
1686            ScriptThread::save_node_id(pipeline, node_id.borrow().simple().to_string());
1687            rare_data.unique_id = Some(node_id);
1688        }
1689        rare_data
1690            .unique_id
1691            .as_ref()
1692            .unwrap()
1693            .borrow()
1694            .simple()
1695            .to_string()
1696    }
1697
1698    pub(crate) fn summarize(&self, can_gc: CanGc) -> NodeInfo {
1699        let USVString(base_uri) = self.BaseURI();
1700        let node_type = self.NodeType();
1701        let pipeline = self.owner_window().pipeline_id();
1702
1703        let maybe_shadow_root = self.downcast::<ShadowRoot>();
1704        let shadow_root_mode = maybe_shadow_root
1705            .map(ShadowRoot::Mode)
1706            .map(ShadowRootMode::convert);
1707        let host = maybe_shadow_root
1708            .map(ShadowRoot::Host)
1709            .map(|host| host.upcast::<Node>().unique_id(pipeline));
1710        let is_shadow_host = self.downcast::<Element>().is_some_and(|potential_host| {
1711            let Some(root) = potential_host.shadow_root() else {
1712                return false;
1713            };
1714            !root.is_user_agent_widget() || pref!(inspector_show_servo_internal_shadow_roots)
1715        });
1716
1717        let num_children = if is_shadow_host {
1718            // Shadow roots count as children
1719            self.ChildNodes(can_gc).Length() as usize + 1
1720        } else {
1721            self.ChildNodes(can_gc).Length() as usize
1722        };
1723
1724        let window = self.owner_window();
1725        let display = self
1726            .downcast::<Element>()
1727            .map(|elem| window.GetComputedStyle(elem, None))
1728            .map(|style| style.Display().into());
1729
1730        NodeInfo {
1731            unique_id: self.unique_id(pipeline),
1732            host,
1733            base_uri,
1734            parent: self
1735                .GetParentNode()
1736                .map_or("".to_owned(), |node| node.unique_id(pipeline)),
1737            node_type,
1738            is_top_level_document: node_type == NodeConstants::DOCUMENT_NODE,
1739            node_name: String::from(self.NodeName()),
1740            node_value: self.GetNodeValue().map(|v| v.into()),
1741            num_children,
1742            attrs: self.downcast().map(Element::summarize).unwrap_or(vec![]),
1743            is_shadow_host,
1744            shadow_root_mode,
1745            display,
1746            // It is not entirely clear when this should be set to false.
1747            // Firefox considers nodes with "display: contents" to be displayed.
1748            // The doctype node is displayed despite being `display: none`.
1749            is_displayed: !self.is_display_none() || self.is::<DocumentType>(),
1750            doctype_name: self
1751                .downcast::<DocumentType>()
1752                .map(DocumentType::name)
1753                .cloned()
1754                .map(String::from),
1755            doctype_public_identifier: self
1756                .downcast::<DocumentType>()
1757                .map(DocumentType::public_id)
1758                .cloned()
1759                .map(String::from),
1760            doctype_system_identifier: self
1761                .downcast::<DocumentType>()
1762                .map(DocumentType::system_id)
1763                .cloned()
1764                .map(String::from),
1765            has_event_listeners: self.upcast::<EventTarget>().has_handlers(),
1766        }
1767    }
1768
1769    /// Used by `HTMLTableSectionElement::InsertRow` and `HTMLTableRowElement::InsertCell`
1770    pub(crate) fn insert_cell_or_row<F, G, I>(
1771        &self,
1772        cx: &mut JSContext,
1773        index: i32,
1774        get_items: F,
1775        new_child: G,
1776    ) -> Fallible<DomRoot<HTMLElement>>
1777    where
1778        F: Fn() -> DomRoot<HTMLCollection>,
1779        G: Fn(&mut JSContext) -> DomRoot<I>,
1780        I: DerivedFrom<Node> + DerivedFrom<HTMLElement> + DomObject,
1781    {
1782        if index < -1 {
1783            return Err(Error::IndexSize(None));
1784        }
1785
1786        let tr = new_child(cx);
1787
1788        {
1789            let tr_node = tr.upcast::<Node>();
1790            if index == -1 {
1791                self.InsertBefore(cx, tr_node, None)?;
1792            } else {
1793                let items = get_items();
1794                let node = match items
1795                    .elements_iter()
1796                    .map(DomRoot::upcast::<Node>)
1797                    .map(Some)
1798                    .chain(iter::once(None))
1799                    .nth(index as usize)
1800                {
1801                    None => return Err(Error::IndexSize(None)),
1802                    Some(node) => node,
1803                };
1804                self.InsertBefore(cx, tr_node, node.as_deref())?;
1805            }
1806        }
1807
1808        Ok(DomRoot::upcast::<HTMLElement>(tr))
1809    }
1810
1811    /// Used by `HTMLTableSectionElement::DeleteRow` and `HTMLTableRowElement::DeleteCell`
1812    pub(crate) fn delete_cell_or_row<F, G>(
1813        &self,
1814        cx: &mut JSContext,
1815        index: i32,
1816        get_items: F,
1817        is_delete_type: G,
1818    ) -> ErrorResult
1819    where
1820        F: Fn() -> DomRoot<HTMLCollection>,
1821        G: Fn(&Element) -> bool,
1822    {
1823        let element = match index {
1824            index if index < -1 => return Err(Error::IndexSize(None)),
1825            -1 => {
1826                let last_child = self.upcast::<Node>().GetLastChild();
1827                match last_child.and_then(|node| {
1828                    node.inclusively_preceding_siblings_unrooted(cx.no_gc())
1829                        .filter_map(UnrootedDom::downcast::<Element>)
1830                        .find(|elem| is_delete_type(elem))
1831                        .map(|elem| elem.as_rooted())
1832                }) {
1833                    Some(element) => element,
1834                    None => return Ok(()),
1835                }
1836            },
1837            index => match get_items().Item(index as u32) {
1838                Some(element) => element,
1839                None => return Err(Error::IndexSize(None)),
1840            },
1841        };
1842
1843        element.upcast::<Node>().remove_self(cx);
1844        Ok(())
1845    }
1846
1847    pub(crate) fn get_stylesheet(&self) -> Option<ServoArc<Stylesheet>> {
1848        if let Some(node) = self.downcast::<HTMLStyleElement>() {
1849            node.get_stylesheet()
1850        } else if let Some(node) = self.downcast::<HTMLLinkElement>() {
1851            node.get_stylesheet()
1852        } else {
1853            None
1854        }
1855    }
1856
1857    pub(crate) fn get_cssom_stylesheet(&self) -> Option<DomRoot<CSSStyleSheet>> {
1858        if let Some(node) = self.downcast::<HTMLStyleElement>() {
1859            node.get_cssom_stylesheet()
1860        } else if let Some(node) = self.downcast::<HTMLLinkElement>() {
1861            node.get_cssom_stylesheet(CanGc::note())
1862        } else {
1863            None
1864        }
1865    }
1866
1867    pub(crate) fn is_styled(&self) -> bool {
1868        self.style_data.borrow().is_some()
1869    }
1870
1871    pub(crate) fn is_display_none(&self) -> bool {
1872        self.style_data.borrow().as_ref().is_none_or(|data| {
1873            data.element_data
1874                .borrow()
1875                .styles
1876                .primary()
1877                .get_box()
1878                .display
1879                .is_none()
1880        })
1881    }
1882
1883    pub(crate) fn style(&self) -> Option<ServoArc<ComputedValues>> {
1884        self.owner_window().layout_reflow(QueryMsg::StyleQuery);
1885        self.style_data
1886            .borrow()
1887            .as_ref()
1888            .map(|data| data.element_data.borrow().styles.primary().clone())
1889    }
1890
1891    /// <https://html.spec.whatwg.org/multipage/#language>
1892    pub(crate) fn get_lang(&self) -> Option<String> {
1893        self.inclusive_ancestors(ShadowIncluding::Yes)
1894            .find_map(|node| {
1895                node.downcast::<Element>().and_then(|el| {
1896                    el.get_attribute_with_namespace(&ns!(xml), &local_name!("lang"))
1897                        .or_else(|| el.get_attribute(&local_name!("lang")))
1898                        .map(|attr| String::from(attr.Value()))
1899                })
1900                // TODO: Check meta tags for a pragma-set default language
1901                // TODO: Check HTTP Content-Language header
1902            })
1903    }
1904
1905    /// <https://dom.spec.whatwg.org/#assign-slotables-for-a-tree>
1906    pub(crate) fn assign_slottables_for_a_tree(&self, force: ForceSlottableNodeReconciliation) {
1907        // NOTE: This method traverses all descendants of the node and is potentially very
1908        // expensive. If the node is neither a shadowroot nor a slot then assigning slottables
1909        // for it won't have any effect, so we take a fast path out.
1910        // In the case of node removal, we need to force re-assignment of slottables
1911        // even if the node is not a shadow root or slot, this allows us to clear assigned
1912        // slots from any slottables that were assigned to slots in the removed subtree.
1913        let is_shadow_root_with_slots = self
1914            .downcast::<ShadowRoot>()
1915            .is_some_and(|shadow_root| shadow_root.has_slot_descendants());
1916        if !is_shadow_root_with_slots &&
1917            !self.is::<HTMLSlotElement>() &&
1918            matches!(force, ForceSlottableNodeReconciliation::Skip)
1919        {
1920            return;
1921        }
1922
1923        // > To assign slottables for a tree, given a node root, run assign slottables for each slot
1924        // > slot in root’s inclusive descendants, in tree order.
1925        for node in self.traverse_preorder(ShadowIncluding::No) {
1926            if let Some(slot) = node.downcast::<HTMLSlotElement>() {
1927                slot.assign_slottables();
1928            }
1929        }
1930    }
1931
1932    pub(crate) fn assigned_slot(&self) -> Option<DomRoot<HTMLSlotElement>> {
1933        let assigned_slot = self
1934            .rare_data
1935            .borrow()
1936            .as_ref()?
1937            .slottable_data
1938            .assigned_slot
1939            .as_ref()?
1940            .as_rooted();
1941        Some(assigned_slot)
1942    }
1943
1944    pub(crate) fn set_assigned_slot(&self, assigned_slot: Option<&HTMLSlotElement>) {
1945        self.ensure_rare_data().slottable_data.assigned_slot = assigned_slot.map(Dom::from_ref);
1946    }
1947
1948    pub(crate) fn manual_slot_assignment(&self) -> Option<DomRoot<HTMLSlotElement>> {
1949        let manually_assigned_slot = self
1950            .rare_data
1951            .borrow()
1952            .as_ref()?
1953            .slottable_data
1954            .manual_slot_assignment
1955            .as_ref()?
1956            .as_rooted();
1957        Some(manually_assigned_slot)
1958    }
1959
1960    pub(crate) fn set_manual_slot_assignment(
1961        &self,
1962        manually_assigned_slot: Option<&HTMLSlotElement>,
1963    ) {
1964        self.ensure_rare_data()
1965            .slottable_data
1966            .manual_slot_assignment = manually_assigned_slot.map(Dom::from_ref);
1967    }
1968
1969    /// Gets the parent of this node from the perspective of layout and style.
1970    ///
1971    /// The returned node is the node's assigned slot, if any, or the
1972    /// shadow host if it's a shadow root. Otherwise, it is the node's
1973    /// parent.
1974    pub(crate) fn parent_in_flat_tree(&self) -> Option<DomRoot<Node>> {
1975        if let Some(assigned_slot) = self.assigned_slot() {
1976            return Some(DomRoot::upcast(assigned_slot));
1977        }
1978
1979        let parent_or_none = self.GetParentNode();
1980        if let Some(parent) = parent_or_none.as_deref() {
1981            if let Some(shadow_root) = parent.downcast::<ShadowRoot>() {
1982                return Some(DomRoot::from_ref(shadow_root.Host().upcast::<Node>()));
1983            }
1984        }
1985
1986        parent_or_none
1987    }
1988
1989    pub(crate) fn inclusive_ancestors_in_flat_tree(
1990        &self,
1991    ) -> impl Iterator<Item = DomRoot<Node>> + use<> {
1992        SimpleNodeIterator {
1993            current: Some(DomRoot::from_ref(self)),
1994            next_node: move |n| n.parent_in_flat_tree(),
1995        }
1996    }
1997
1998    /// We are marking this as an implemented pseudo element.
1999    pub(crate) fn set_implemented_pseudo_element(&self, pseudo_element: PseudoElement) {
2000        // Implemented pseudo element should exist only in the UA shadow DOM.
2001        debug_assert!(self.is_in_ua_widget());
2002        debug_assert!(pseudo_element.is_element_backed());
2003        self.ensure_rare_data().implemented_pseudo_element = Some(pseudo_element);
2004    }
2005
2006    pub(crate) fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
2007        self.rare_data
2008            .borrow()
2009            .as_ref()
2010            .and_then(|rare_data| rare_data.implemented_pseudo_element)
2011    }
2012
2013    /// <https://w3c.github.io/editing/docs/execCommand/#editing-host-of>
2014    pub(crate) fn editing_host_of(&self) -> Option<DomRoot<Node>> {
2015        // > The editing host of node is null if node is neither editable nor an editing host;
2016        // > node itself, if node is an editing host;
2017        // > or the nearest ancestor of node that is an editing host, if node is editable.
2018        for ancestor in self.inclusive_ancestors(ShadowIncluding::No) {
2019            if ancestor.is_editing_host() {
2020                return Some(ancestor);
2021            }
2022            if ancestor
2023                .downcast::<HTMLElement>()
2024                .is_some_and(|el| el.ContentEditable().str() == "false")
2025            {
2026                return None;
2027            }
2028        }
2029        None
2030    }
2031
2032    pub(crate) fn is_editable_or_editing_host(&self) -> bool {
2033        self.editing_host_of().is_some()
2034    }
2035
2036    /// <https://html.spec.whatwg.org/multipage/#editing-host>
2037    pub(crate) fn is_editing_host(&self) -> bool {
2038        self.downcast::<HTMLElement>()
2039            .is_some_and(HTMLElement::is_editing_host)
2040    }
2041
2042    /// <https://w3c.github.io/editing/docs/execCommand/#editable>
2043    pub(crate) fn is_editable(&self) -> bool {
2044        // > Something is editable if it is a node; it is not an editing host;
2045        if self.is_editing_host() {
2046            return false;
2047        }
2048        // > it does not have a contenteditable attribute set to the false state;
2049        let html_element = self.downcast::<HTMLElement>();
2050        if html_element.is_some_and(|el| el.ContentEditable().str() == "false") {
2051            return false;
2052        }
2053        // > its parent is an editing host or editable;
2054        let Some(parent) = self.GetParentNode() else {
2055            return false;
2056        };
2057        if !parent.is_editable_or_editing_host() {
2058            return false;
2059        }
2060        // > and either it is an HTML element, or it is an svg or math element, or it is not an Element and its parent is an HTML element.
2061        html_element.is_some() ||
2062            (!self.is::<Element>() && parent.downcast::<HTMLElement>().is_some())
2063    }
2064}
2065
2066/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
2067fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<DomRoot<Node>>
2068where
2069    I: Iterator<Item = DomRoot<Node>>,
2070{
2071    nodes.find(|node| {
2072        not_in.iter().all(|n| match *n {
2073            NodeOrString::Node(ref n) => n != node,
2074            _ => true,
2075        })
2076    })
2077}
2078
2079/// If the given untrusted node address represents a valid DOM node in the given runtime,
2080/// returns it.
2081#[expect(unsafe_code)]
2082pub(crate) unsafe fn from_untrusted_node_address(candidate: UntrustedNodeAddress) -> DomRoot<Node> {
2083    let node = unsafe { Node::from_untrusted_node_address(candidate) };
2084    DomRoot::from_ref(node)
2085}
2086
2087#[expect(unsafe_code)]
2088pub(crate) trait LayoutNodeHelpers<'dom> {
2089    fn type_id_for_layout(self) -> NodeTypeId;
2090
2091    fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>>;
2092    fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>>;
2093    fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>>;
2094    fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>>;
2095    fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>>;
2096    fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>>;
2097
2098    fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document>;
2099    fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>;
2100    fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>>;
2101
2102    fn is_element_for_layout(&self) -> bool;
2103    fn is_text_node_for_layout(&self) -> bool;
2104    unsafe fn get_flag(self, flag: NodeFlags) -> bool;
2105    unsafe fn set_flag(self, flag: NodeFlags, value: bool);
2106
2107    fn style_data(self) -> Option<&'dom StyleData>;
2108    fn layout_data(self) -> Option<&'dom GenericLayoutData>;
2109
2110    /// Initialize the style data of this node.
2111    ///
2112    /// # Safety
2113    ///
2114    /// This method is unsafe because it modifies the given node during
2115    /// layout. Callers should ensure that no other layout thread is
2116    /// attempting to read or modify the opaque layout data of this node.
2117    unsafe fn initialize_style_data(self);
2118
2119    /// Initialize the opaque layout data of this node.
2120    ///
2121    /// # Safety
2122    ///
2123    /// This method is unsafe because it modifies the given node during
2124    /// layout. Callers should ensure that no other layout thread is
2125    /// attempting to read or modify the opaque layout data of this node.
2126    unsafe fn initialize_layout_data(self, data: Box<GenericLayoutData>);
2127
2128    /// Clear the style and opaque layout data of this node.
2129    ///
2130    /// # Safety
2131    ///
2132    /// This method is unsafe because it modifies the given node during
2133    /// layout. Callers should ensure that no other layout thread is
2134    /// attempting to read or modify the opaque layout data of this node.
2135    unsafe fn clear_style_and_layout_data(self);
2136
2137    /// Whether this element serve as a container of editable text for a text input
2138    /// that is implemented as an UA widget.
2139    fn is_single_line_text_inner_editor(&self) -> bool;
2140
2141    /// Whether this element serve as a container of any text inside a text input
2142    /// that is implemented as an UA widget.
2143    fn is_text_container_of_single_line_input(&self) -> bool;
2144    fn text_content(self) -> Cow<'dom, str>;
2145    fn selection(self) -> Option<SharedSelection>;
2146    fn image_url(self) -> Option<ServoUrl>;
2147    fn image_density(self) -> Option<f64>;
2148    fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)>;
2149    fn showing_broken_image_icon(self) -> bool;
2150    fn canvas_data(self) -> Option<HTMLCanvasData>;
2151    fn media_data(self) -> Option<HTMLMediaData>;
2152    fn svg_data(self) -> Option<SVGElementData<'dom>>;
2153    fn iframe_browsing_context_id(self) -> Option<BrowsingContextId>;
2154    fn iframe_pipeline_id(self) -> Option<PipelineId>;
2155    fn opaque(self) -> OpaqueNode;
2156    fn implemented_pseudo_element(&self) -> Option<PseudoElement>;
2157    fn is_in_ua_widget(&self) -> bool;
2158}
2159
2160impl<'dom> LayoutDom<'dom, Node> {
2161    #[inline]
2162    #[expect(unsafe_code)]
2163    pub(crate) fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
2164        unsafe { self.unsafe_get().parent_node.get_inner_as_layout() }
2165    }
2166}
2167
2168impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
2169    #[inline]
2170    fn type_id_for_layout(self) -> NodeTypeId {
2171        self.unsafe_get().type_id()
2172    }
2173
2174    #[inline]
2175    fn is_element_for_layout(&self) -> bool {
2176        (*self).is::<Element>()
2177    }
2178
2179    fn is_text_node_for_layout(&self) -> bool {
2180        self.type_id_for_layout() ==
2181            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text))
2182    }
2183
2184    #[inline]
2185    #[expect(unsafe_code)]
2186    fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
2187        unsafe { self.unsafe_get().parent_node.get_inner_as_layout() }
2188    }
2189
2190    #[inline]
2191    fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
2192        let parent = self.parent_node_ref();
2193        if let Some(parent) = parent {
2194            if let Some(shadow_root) = parent.downcast::<ShadowRoot>() {
2195                return Some(shadow_root.get_host_for_layout().upcast());
2196            }
2197        }
2198        parent
2199    }
2200
2201    #[inline]
2202    #[expect(unsafe_code)]
2203    fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
2204        unsafe { self.unsafe_get().first_child.get_inner_as_layout() }
2205    }
2206
2207    #[inline]
2208    #[expect(unsafe_code)]
2209    fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
2210        unsafe { self.unsafe_get().last_child.get_inner_as_layout() }
2211    }
2212
2213    #[inline]
2214    #[expect(unsafe_code)]
2215    fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
2216        unsafe { self.unsafe_get().prev_sibling.get_inner_as_layout() }
2217    }
2218
2219    #[inline]
2220    #[expect(unsafe_code)]
2221    fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
2222        unsafe { self.unsafe_get().next_sibling.get_inner_as_layout() }
2223    }
2224
2225    #[inline]
2226    #[expect(unsafe_code)]
2227    fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document> {
2228        unsafe { self.unsafe_get().owner_doc.get_inner_as_layout().unwrap() }
2229    }
2230
2231    #[inline]
2232    #[expect(unsafe_code)]
2233    fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
2234        unsafe {
2235            self.unsafe_get()
2236                .rare_data
2237                .borrow_for_layout()
2238                .as_ref()?
2239                .containing_shadow_root
2240                .as_ref()
2241                .map(|sr| sr.to_layout())
2242        }
2243    }
2244
2245    #[inline]
2246    #[expect(unsafe_code)]
2247    fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>> {
2248        unsafe {
2249            self.unsafe_get()
2250                .rare_data
2251                .borrow_for_layout()
2252                .as_ref()?
2253                .slottable_data
2254                .assigned_slot
2255                .as_ref()
2256                .map(|assigned_slot| assigned_slot.to_layout())
2257        }
2258    }
2259
2260    // FIXME(nox): get_flag/set_flag (especially the latter) are not safe because
2261    // they mutate stuff while values of this type can be used from multiple
2262    // threads at once, this should be revisited.
2263
2264    #[inline]
2265    #[expect(unsafe_code)]
2266    unsafe fn get_flag(self, flag: NodeFlags) -> bool {
2267        (self.unsafe_get()).flags.get().contains(flag)
2268    }
2269
2270    #[inline]
2271    #[expect(unsafe_code)]
2272    unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
2273        let this = self.unsafe_get();
2274        let mut flags = (this).flags.get();
2275
2276        if value {
2277            flags.insert(flag);
2278        } else {
2279            flags.remove(flag);
2280        }
2281
2282        (this).flags.set(flags);
2283    }
2284
2285    // FIXME(nox): How we handle style and layout data needs to be completely
2286    // revisited so we can do that more cleanly and safely in layout 2020.
2287    #[inline]
2288    #[expect(unsafe_code)]
2289    fn style_data(self) -> Option<&'dom StyleData> {
2290        unsafe { self.unsafe_get().style_data.borrow_for_layout().as_deref() }
2291    }
2292
2293    #[inline]
2294    #[expect(unsafe_code)]
2295    fn layout_data(self) -> Option<&'dom GenericLayoutData> {
2296        unsafe { self.unsafe_get().layout_data.borrow_for_layout().as_deref() }
2297    }
2298
2299    #[inline]
2300    #[expect(unsafe_code)]
2301    unsafe fn initialize_style_data(self) {
2302        let data = unsafe { self.unsafe_get().style_data.borrow_mut_for_layout() };
2303        debug_assert!(data.is_none());
2304        *data = Some(Box::default());
2305    }
2306
2307    #[inline]
2308    #[expect(unsafe_code)]
2309    unsafe fn initialize_layout_data(self, new_data: Box<GenericLayoutData>) {
2310        let data = unsafe { self.unsafe_get().layout_data.borrow_mut_for_layout() };
2311        debug_assert!(data.is_none());
2312        *data = Some(new_data);
2313    }
2314
2315    #[inline]
2316    #[expect(unsafe_code)]
2317    unsafe fn clear_style_and_layout_data(self) {
2318        unsafe {
2319            self.unsafe_get().style_data.borrow_mut_for_layout().take();
2320            self.unsafe_get().layout_data.borrow_mut_for_layout().take();
2321        }
2322    }
2323
2324    fn is_single_line_text_inner_editor(&self) -> bool {
2325        matches!(
2326            self.implemented_pseudo_element(),
2327            Some(PseudoElement::ServoTextControlInnerEditor)
2328        )
2329    }
2330
2331    fn is_text_container_of_single_line_input(&self) -> bool {
2332        let is_single_line_text_inner_placeholder = matches!(
2333            self.implemented_pseudo_element(),
2334            Some(PseudoElement::Placeholder)
2335        );
2336        // Currently `::placeholder` is only implemented for single line text input element.
2337        debug_assert!(
2338            !is_single_line_text_inner_placeholder ||
2339                self.containing_shadow_root_for_layout()
2340                    .map(|root| root.get_host_for_layout())
2341                    .map(|host| host.downcast::<HTMLInputElement>())
2342                    .is_some()
2343        );
2344
2345        self.is_single_line_text_inner_editor() || is_single_line_text_inner_placeholder
2346    }
2347
2348    fn text_content(self) -> Cow<'dom, str> {
2349        self.downcast::<Text>()
2350            .expect("Called LayoutDom::text_content on non-Text node!")
2351            .upcast()
2352            .data_for_layout()
2353            .into()
2354    }
2355
2356    /// Get the selection for the given node. This only works for text nodes that are in
2357    /// the shadow DOM of user agent widgets for form controls, specifically for `<input>`
2358    /// and `<textarea>`.
2359    ///
2360    /// As we want to expose the selection on the inner text node of the widget's shadow
2361    /// DOM, we must find the shadow root and then access the containing element itself.
2362    fn selection(self) -> Option<SharedSelection> {
2363        if let Some(input) = self.downcast::<HTMLInputElement>() {
2364            return input.selection_for_layout();
2365        }
2366        if let Some(textarea) = self.downcast::<HTMLTextAreaElement>() {
2367            return Some(textarea.selection_for_layout());
2368        }
2369
2370        let shadow_root = self
2371            .containing_shadow_root_for_layout()?
2372            .get_host_for_layout();
2373        if let Some(input) = shadow_root.downcast::<HTMLInputElement>() {
2374            return input.selection_for_layout();
2375        }
2376        shadow_root
2377            .downcast::<HTMLTextAreaElement>()
2378            .map(|textarea| textarea.selection_for_layout())
2379    }
2380
2381    fn image_url(self) -> Option<ServoUrl> {
2382        self.downcast::<HTMLImageElement>()
2383            .expect("not an image!")
2384            .image_url()
2385    }
2386
2387    fn image_data(self) -> Option<(Option<Image>, Option<ImageMetadata>)> {
2388        self.downcast::<HTMLImageElement>().map(|e| e.image_data())
2389    }
2390
2391    fn image_density(self) -> Option<f64> {
2392        self.downcast::<HTMLImageElement>()
2393            .expect("not an image!")
2394            .image_density()
2395    }
2396
2397    fn showing_broken_image_icon(self) -> bool {
2398        self.downcast::<HTMLImageElement>()
2399            .map(|image_element| image_element.showing_broken_image_icon())
2400            .unwrap_or_default()
2401    }
2402
2403    fn canvas_data(self) -> Option<HTMLCanvasData> {
2404        self.downcast::<HTMLCanvasElement>()
2405            .map(|canvas| canvas.data())
2406    }
2407
2408    fn media_data(self) -> Option<HTMLMediaData> {
2409        self.downcast::<HTMLVideoElement>()
2410            .map(|media| media.data())
2411    }
2412
2413    fn svg_data(self) -> Option<SVGElementData<'dom>> {
2414        self.downcast::<SVGSVGElement>().map(|svg| svg.data())
2415    }
2416
2417    fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
2418        self.downcast::<HTMLIFrameElement>()
2419            .and_then(|iframe_element| iframe_element.browsing_context_id())
2420    }
2421
2422    fn iframe_pipeline_id(self) -> Option<PipelineId> {
2423        self.downcast::<HTMLIFrameElement>()
2424            .and_then(|iframe_element| iframe_element.pipeline_id())
2425    }
2426
2427    #[expect(unsafe_code)]
2428    fn opaque(self) -> OpaqueNode {
2429        unsafe { OpaqueNode(self.get_jsobject() as usize) }
2430    }
2431
2432    #[expect(unsafe_code)]
2433    fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
2434        unsafe {
2435            self.unsafe_get()
2436                .rare_data
2437                .borrow_for_layout()
2438                .as_ref()
2439                .and_then(|rare_data| rare_data.implemented_pseudo_element)
2440        }
2441    }
2442
2443    fn is_in_ua_widget(&self) -> bool {
2444        self.unsafe_get().is_in_ua_widget()
2445    }
2446}
2447
2448//
2449// Iteration and traversal
2450//
2451
2452pub(crate) struct FollowingNodeIterator {
2453    current: Option<DomRoot<Node>>,
2454    root: DomRoot<Node>,
2455}
2456
2457impl FollowingNodeIterator {
2458    /// Skips iterating the children of the current node
2459    pub(crate) fn next_skipping_children(&mut self) -> Option<DomRoot<Node>> {
2460        let current = self.current.take()?;
2461        self.next_skipping_children_impl(current)
2462    }
2463
2464    fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
2465        if self.root == current {
2466            self.current = None;
2467            return None;
2468        }
2469
2470        if let Some(next_sibling) = current.GetNextSibling() {
2471            self.current = Some(next_sibling);
2472            return current.GetNextSibling();
2473        }
2474
2475        for ancestor in current.inclusive_ancestors(ShadowIncluding::No) {
2476            if self.root == ancestor {
2477                break;
2478            }
2479            if let Some(next_sibling) = ancestor.GetNextSibling() {
2480                self.current = Some(next_sibling);
2481                return ancestor.GetNextSibling();
2482            }
2483        }
2484        self.current = None;
2485        None
2486    }
2487}
2488
2489impl Iterator for FollowingNodeIterator {
2490    type Item = DomRoot<Node>;
2491
2492    /// <https://dom.spec.whatwg.org/#concept-tree-following>
2493    fn next(&mut self) -> Option<DomRoot<Node>> {
2494        let current = self.current.take()?;
2495
2496        if let Some(first_child) = current.GetFirstChild() {
2497            self.current = Some(first_child);
2498            return current.GetFirstChild();
2499        }
2500
2501        self.next_skipping_children_impl(current)
2502    }
2503}
2504
2505pub(crate) struct PrecedingNodeIterator {
2506    current: Option<DomRoot<Node>>,
2507    root: DomRoot<Node>,
2508}
2509
2510impl Iterator for PrecedingNodeIterator {
2511    type Item = DomRoot<Node>;
2512
2513    /// <https://dom.spec.whatwg.org/#concept-tree-preceding>
2514    fn next(&mut self) -> Option<DomRoot<Node>> {
2515        let current = self.current.take()?;
2516
2517        self.current = if self.root == current {
2518            None
2519        } else if let Some(previous_sibling) = current.GetPreviousSibling() {
2520            if self.root == previous_sibling {
2521                None
2522            } else if let Some(last_child) = previous_sibling.descending_last_children().last() {
2523                Some(last_child)
2524            } else {
2525                Some(previous_sibling)
2526            }
2527        } else {
2528            current.GetParentNode()
2529        };
2530        self.current.clone()
2531    }
2532}
2533
2534struct SimpleNodeIterator<I>
2535where
2536    I: Fn(&Node) -> Option<DomRoot<Node>>,
2537{
2538    current: Option<DomRoot<Node>>,
2539    next_node: I,
2540}
2541
2542impl<I> Iterator for SimpleNodeIterator<I>
2543where
2544    I: Fn(&Node) -> Option<DomRoot<Node>>,
2545{
2546    type Item = DomRoot<Node>;
2547
2548    fn next(&mut self) -> Option<Self::Item> {
2549        let current = self.current.take();
2550        self.current = current.as_ref().and_then(|c| (self.next_node)(c));
2551        current
2552    }
2553}
2554
2555/// An efficient SimpleNodeIterator because it skips rooting if there are no GC pauses.
2556///
2557/// Use this if you have a `&JSContext` or `NoGC`.
2558///
2559/// Normally we need to root every `Node` we come across as we do not know if we will have a GC pause.
2560/// This does not root the required children. Taking a `&NoGC` enforces that there is no `&mut JSContext`
2561/// while this iterator is alive.
2562#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
2563pub(crate) struct UnrootedSimpleNodeIterator<'a, 'b, I>
2564where
2565    I: Fn(&Node, &'b NoGC) -> Option<UnrootedDom<'b, Node>>,
2566{
2567    current: Option<UnrootedDom<'b, Node>>,
2568    next_node: I,
2569    /// This is unused and only used for lifetime guarantee of NoGC
2570    no_gc: &'b NoGC,
2571    phantom: PhantomData<&'a Node>,
2572}
2573
2574impl<'a, 'b, I> Iterator for UnrootedSimpleNodeIterator<'a, 'b, I>
2575where
2576    'b: 'a,
2577    I: Fn(&Node, &'b NoGC) -> Option<UnrootedDom<'b, Node>>,
2578{
2579    type Item = UnrootedDom<'b, Node>;
2580
2581    fn next(&mut self) -> Option<Self::Item> {
2582        let current = self.current.take();
2583        self.current = current
2584            .as_ref()
2585            .and_then(|c| (self.next_node)(c, self.no_gc));
2586        current
2587    }
2588}
2589
2590/// Whether a tree traversal should pass shadow tree boundaries.
2591#[derive(Clone, Copy, PartialEq)]
2592pub(crate) enum ShadowIncluding {
2593    No,
2594    Yes,
2595}
2596
2597pub(crate) struct TreeIterator {
2598    current: Option<DomRoot<Node>>,
2599    depth: usize,
2600    shadow_including: ShadowIncluding,
2601}
2602
2603impl TreeIterator {
2604    fn new(root: &Node, shadow_including: ShadowIncluding) -> TreeIterator {
2605        TreeIterator {
2606            current: Some(DomRoot::from_ref(root)),
2607            depth: 0,
2608            shadow_including,
2609        }
2610    }
2611
2612    pub(crate) fn next_skipping_children(&mut self) -> Option<DomRoot<Node>> {
2613        let current = self.current.take()?;
2614
2615        self.next_skipping_children_impl(current)
2616    }
2617
2618    fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
2619        let iter = current.inclusive_ancestors(self.shadow_including);
2620
2621        for ancestor in iter {
2622            if self.depth == 0 {
2623                break;
2624            }
2625            if let Some(next_sibling) = ancestor.GetNextSibling() {
2626                self.current = Some(next_sibling);
2627                return Some(current);
2628            }
2629            if let Some(shadow_root) = ancestor.downcast::<ShadowRoot>() {
2630                // Shadow roots don't have sibling, so after we're done traversing
2631                // one we jump to the first child of the host
2632                if let Some(child) = shadow_root.Host().upcast::<Node>().GetFirstChild() {
2633                    self.current = Some(child);
2634                    return Some(current);
2635                }
2636            }
2637            self.depth -= 1;
2638        }
2639        debug_assert_eq!(self.depth, 0);
2640        self.current = None;
2641        Some(current)
2642    }
2643
2644    pub(crate) fn peek(&self) -> Option<&DomRoot<Node>> {
2645        self.current.as_ref()
2646    }
2647}
2648
2649impl Iterator for TreeIterator {
2650    type Item = DomRoot<Node>;
2651
2652    /// <https://dom.spec.whatwg.org/#concept-tree-order>
2653    /// <https://dom.spec.whatwg.org/#concept-shadow-including-tree-order>
2654    fn next(&mut self) -> Option<DomRoot<Node>> {
2655        let current = self.current.take()?;
2656
2657        // Handle a potential shadow root on the element
2658        if let Some(element) = current.downcast::<Element>() {
2659            if let Some(shadow_root) = element.shadow_root() {
2660                if self.shadow_including == ShadowIncluding::Yes {
2661                    self.current = Some(DomRoot::from_ref(shadow_root.upcast::<Node>()));
2662                    self.depth += 1;
2663                    return Some(current);
2664                }
2665            }
2666        }
2667
2668        if let Some(first_child) = current.GetFirstChild() {
2669            self.current = Some(first_child);
2670            self.depth += 1;
2671            return Some(current);
2672        };
2673
2674        self.next_skipping_children_impl(current)
2675    }
2676}
2677
2678/// An efficient TreeIterator because it skips rooting if there are no GC pauses.
2679///
2680/// Use this if you have a `&JSContext` or `NoGC`.
2681///
2682/// Normally we need to root every `Node` we come across as we do not know if we will have a GC pause.
2683/// This does not root the required children. Taking a `&NoGC` enforces that there is no `&mut JSContext`
2684/// while this iterator is alive.
2685#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
2686pub(crate) struct UnrootedTreeIterator<'a, 'b> {
2687    current: Option<UnrootedDom<'b, Node>>,
2688    depth: usize,
2689    shadow_including: ShadowIncluding,
2690    /// This is unused and only used for lifetime guarantee of NoGC
2691    no_gc: &'b NoGC,
2692    phantom: PhantomData<&'a Node>,
2693}
2694
2695impl<'a, 'b> UnrootedTreeIterator<'a, 'b>
2696where
2697    'b: 'a,
2698{
2699    pub(crate) fn new(
2700        root: &'a Node,
2701        shadow_including: ShadowIncluding,
2702        no_gc: &'b NoGC,
2703    ) -> UnrootedTreeIterator<'a, 'b> {
2704        UnrootedTreeIterator {
2705            current: Some(UnrootedDom::from_dom(Dom::from_ref(root), no_gc)),
2706            depth: 0,
2707            shadow_including,
2708            no_gc,
2709            phantom: PhantomData,
2710        }
2711    }
2712
2713    pub(crate) fn next_skipping_children(&mut self) -> Option<UnrootedDom<'b, Node>> {
2714        let current = self.current.take()?;
2715
2716        let iter = current.inclusive_ancestors(self.shadow_including);
2717
2718        for ancestor in iter {
2719            if self.depth == 0 {
2720                break;
2721            }
2722
2723            let next_sibling_option = ancestor.get_next_sibling_unrooted(self.no_gc);
2724
2725            if let Some(next_sibling) = next_sibling_option {
2726                self.current = Some(next_sibling);
2727                return Some(current);
2728            }
2729
2730            if let Some(shadow_root) = ancestor.downcast::<ShadowRoot>() {
2731                // Shadow roots don't have sibling, so after we're done traversing
2732                // one we jump to the first child of the host
2733                let child_option = shadow_root
2734                    .Host()
2735                    .upcast::<Node>()
2736                    .get_first_child_unrooted(self.no_gc);
2737
2738                if let Some(child) = child_option {
2739                    self.current = Some(child);
2740                    return Some(current);
2741                }
2742            }
2743            self.depth -= 1;
2744        }
2745        debug_assert_eq!(self.depth, 0);
2746        self.current = None;
2747        Some(current)
2748    }
2749}
2750
2751impl<'a, 'b> Iterator for UnrootedTreeIterator<'a, 'b>
2752where
2753    'b: 'a,
2754{
2755    type Item = UnrootedDom<'b, Node>;
2756
2757    /// <https://dom.spec.whatwg.org/#concept-tree-order>
2758    /// <https://dom.spec.whatwg.org/#concept-shadow-including-tree-order>
2759    fn next(&mut self) -> Option<UnrootedDom<'b, Node>> {
2760        let current = self.current.take()?;
2761
2762        // Handle a potential shadow root on the element
2763        if let Some(element) = current.downcast::<Element>() {
2764            if let Some(shadow_root) = element.shadow_root() {
2765                if self.shadow_including == ShadowIncluding::Yes {
2766                    self.current = Some(UnrootedDom::from_dom(
2767                        Dom::from_ref(shadow_root.upcast::<Node>()),
2768                        self.no_gc,
2769                    ));
2770                    self.depth += 1;
2771                    return Some(current);
2772                }
2773            }
2774        }
2775
2776        let first_child_option = current.get_first_child_unrooted(self.no_gc);
2777        if let Some(first_child) = first_child_option {
2778            self.current = Some(first_child);
2779            self.depth += 1;
2780            return Some(current);
2781        };
2782
2783        // current is empty.
2784        let _ = self.current.insert(current);
2785        self.next_skipping_children()
2786    }
2787}
2788
2789/// Specifies whether children must be recursively cloned or not.
2790#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
2791pub(crate) enum CloneChildrenFlag {
2792    CloneChildren,
2793    DoNotCloneChildren,
2794}
2795
2796impl From<bool> for CloneChildrenFlag {
2797    fn from(boolean: bool) -> Self {
2798        if boolean {
2799            CloneChildrenFlag::CloneChildren
2800        } else {
2801            CloneChildrenFlag::DoNotCloneChildren
2802        }
2803    }
2804}
2805
2806fn as_uintptr<T>(t: &T) -> uintptr_t {
2807    t as *const T as uintptr_t
2808}
2809
2810impl Node {
2811    pub(crate) fn reflect_node<N>(
2812        cx: &mut js::context::JSContext,
2813        node: Box<N>,
2814        document: &Document,
2815    ) -> DomRoot<N>
2816    where
2817        N: DerivedFrom<Node> + DomObject + DomObjectWrap<crate::DomTypeHolder>,
2818    {
2819        Self::reflect_node_with_proto(cx, node, document, None)
2820    }
2821
2822    pub(crate) fn reflect_node_with_proto<N>(
2823        cx: &mut js::context::JSContext,
2824        node: Box<N>,
2825        document: &Document,
2826        proto: Option<HandleObject>,
2827    ) -> DomRoot<N>
2828    where
2829        N: DerivedFrom<Node> + DomObject + DomObjectWrap<crate::DomTypeHolder>,
2830    {
2831        let window = document.window();
2832        reflect_dom_object_with_proto_and_cx(node, window, proto, cx)
2833    }
2834
2835    pub(crate) fn new_inherited(doc: &Document) -> Node {
2836        Node::new_(NodeFlags::empty(), Some(doc))
2837    }
2838
2839    pub(crate) fn new_document_node() -> Node {
2840        Node::new_(
2841            NodeFlags::IS_IN_A_DOCUMENT_TREE | NodeFlags::IS_CONNECTED,
2842            None,
2843        )
2844    }
2845
2846    fn new_(flags: NodeFlags, doc: Option<&Document>) -> Node {
2847        Node {
2848            eventtarget: EventTarget::new_inherited(),
2849
2850            parent_node: Default::default(),
2851            first_child: Default::default(),
2852            last_child: Default::default(),
2853            next_sibling: Default::default(),
2854            prev_sibling: Default::default(),
2855            owner_doc: MutNullableDom::new(doc),
2856            rare_data: Default::default(),
2857            children_count: Cell::new(0u32),
2858            flags: Cell::new(flags),
2859            inclusive_descendants_version: Cell::new(0),
2860            style_data: Default::default(),
2861            layout_data: Default::default(),
2862        }
2863    }
2864
2865    /// <https://dom.spec.whatwg.org/#concept-node-adopt>
2866    pub(crate) fn adopt(cx: &mut JSContext, node: &Node, document: &Document) {
2867        document.add_script_and_layout_blocker();
2868
2869        // Step 1. Let oldDocument be node’s node document.
2870        let old_doc = node.owner_doc();
2871        old_doc.add_script_and_layout_blocker();
2872
2873        // Step 2. If node’s parent is non-null, then remove node.
2874        node.remove_self(cx);
2875
2876        // Step 3. If document is not oldDocument:
2877        if &*old_doc != document {
2878            // Step 3.1. For each inclusiveDescendant in node’s shadow-including inclusive descendants:
2879            for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
2880                // Step 3.1.1 Set inclusiveDescendant’s node document to document.
2881                descendant.set_owner_doc(document);
2882
2883                // Step 3.1.2 If inclusiveDescendant is an element, then set the node document of each
2884                // attribute in inclusiveDescendant’s attribute list to document.
2885                if let Some(element) = descendant.downcast::<Element>() {
2886                    for attribute in element.attrs().iter() {
2887                        attribute.upcast::<Node>().set_owner_doc(document);
2888                    }
2889                }
2890            }
2891
2892            // Step 3.2 For each inclusiveDescendant in node’s shadow-including inclusive descendants
2893            // that is custom, enqueue a custom element callback reaction with inclusiveDescendant,
2894            // callback name "adoptedCallback", and « oldDocument, document ».
2895            let custom_element_reaction_stack = ScriptThread::custom_element_reaction_stack();
2896            for descendant in node
2897                .traverse_preorder(ShadowIncluding::Yes)
2898                .filter_map(|d| d.as_custom_element())
2899            {
2900                custom_element_reaction_stack.enqueue_callback_reaction(
2901                    &descendant,
2902                    CallbackReaction::Adopted(old_doc.clone(), DomRoot::from_ref(document)),
2903                    None,
2904                );
2905            }
2906
2907            // Step 3.3 For each inclusiveDescendant in node’s shadow-including inclusive descendants,
2908            // in shadow-including tree order, run the adopting steps with inclusiveDescendant and oldDocument.
2909            for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
2910                vtable_for(&descendant).adopting_steps(cx, &old_doc);
2911            }
2912        }
2913
2914        old_doc.remove_script_and_layout_blocker();
2915        document.remove_script_and_layout_blocker();
2916    }
2917
2918    /// <https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity>
2919    pub(crate) fn ensure_pre_insertion_validity(
2920        node: &Node,
2921        parent: &Node,
2922        child: Option<&Node>,
2923    ) -> ErrorResult {
2924        // Step 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
2925        match parent.type_id() {
2926            NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
2927            },
2928            _ => {
2929                return Err(Error::HierarchyRequest(Some(
2930                    "Parent is not a Document, DocumentFragment, or Element node".to_owned(),
2931                )));
2932            },
2933        }
2934
2935        // Step 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException.
2936        if node.is_host_including_inclusive_ancestor(parent) {
2937            return Err(Error::HierarchyRequest(Some(
2938                "Node is a host-including inclusive ancestor of parent".to_owned(),
2939            )));
2940        }
2941
2942        // Step 3. If child is non-null and its parent is not parent, then throw a "NotFoundError" DOMException.
2943        if let Some(child) = child {
2944            if !parent.is_parent_of(child) {
2945                return Err(Error::NotFound(Some(
2946                    "Child is non-null and its parent is not parent".to_owned(),
2947                )));
2948            }
2949        }
2950
2951        match node.type_id() {
2952            // Step 5. If either node is a Text node and parent is a document,
2953            // or node is a doctype and parent is not a document,
2954            // then throw a "HierarchyRequestError" DOMException.
2955            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
2956                if parent.is::<Document>() {
2957                    return Err(Error::HierarchyRequest(Some(
2958                        "Node is a Text node and parent is a document".to_owned(),
2959                    )));
2960                }
2961            },
2962            NodeTypeId::DocumentType => {
2963                if !parent.is::<Document>() {
2964                    return Err(Error::HierarchyRequest(Some(
2965                        "Node is a doctype and parent is not a document".to_owned(),
2966                    )));
2967                }
2968            },
2969            NodeTypeId::DocumentFragment(_) |
2970            NodeTypeId::Element(_) |
2971            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
2972            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => (),
2973            // Step 4. If node is not a DocumentFragment, DocumentType, Element,
2974            // or CharacterData node, then throw a "HierarchyRequestError" DOMException.
2975            NodeTypeId::Document(_) | NodeTypeId::Attr => {
2976                return Err(Error::HierarchyRequest(Some(
2977                    "Node is not a DocumentFragment, DocumentType, Element, or CharacterData node"
2978                        .to_owned(),
2979                )));
2980            },
2981        }
2982
2983        // Step 6. If parent is a document, and any of the statements below, switched on the interface node implements,
2984        // are true, then throw a "HierarchyRequestError" DOMException.
2985        if parent.is::<Document>() {
2986            match node.type_id() {
2987                NodeTypeId::DocumentFragment(_) => {
2988                    // Step 6."DocumentFragment". If node has more than one element child or has a Text node child.
2989                    if node.children().any(|c| c.is::<Text>()) {
2990                        return Err(Error::HierarchyRequest(None));
2991                    }
2992                    match node.child_elements().count() {
2993                        0 => (),
2994                        // Step 6."DocumentFragment". Otherwise, if node has one element child and either parent has an element child,
2995                        // child is a doctype, or child is non-null and a doctype is following child.
2996                        1 => {
2997                            if parent.child_elements().next().is_some() {
2998                                return Err(Error::HierarchyRequest(None));
2999                            }
3000                            if let Some(child) = child {
3001                                if child
3002                                    .inclusively_following_siblings()
3003                                    .any(|child| child.is_doctype())
3004                                {
3005                                    return Err(Error::HierarchyRequest(None));
3006                                }
3007                            }
3008                        },
3009                        _ => return Err(Error::HierarchyRequest(None)),
3010                    }
3011                },
3012                NodeTypeId::Element(_) => {
3013                    // Step 6."Element". parent has an element child, child is a doctype, or child is non-null and a doctype is following child.
3014                    if parent.child_elements().next().is_some() {
3015                        return Err(Error::HierarchyRequest(Some(
3016                            "Parent has an element child".to_owned(),
3017                        )));
3018                    }
3019                    if let Some(child) = child {
3020                        if child
3021                            .inclusively_following_siblings()
3022                            .any(|following| following.is_doctype())
3023                        {
3024                            return Err(Error::HierarchyRequest(Some(
3025                                "Child is a doctype, or child is non-null and a doctype is following child".to_owned(),
3026                            )));
3027                        }
3028                    }
3029                },
3030                NodeTypeId::DocumentType => {
3031                    // Step 6."DocumentType". parent has a doctype child, child is non-null and an element is preceding child,
3032                    // or child is null and parent has an element child.
3033                    if parent.children().any(|c| c.is_doctype()) {
3034                        return Err(Error::HierarchyRequest(None));
3035                    }
3036                    match child {
3037                        Some(child) => {
3038                            if parent
3039                                .children()
3040                                .take_while(|c| &**c != child)
3041                                .any(|c| c.is::<Element>())
3042                            {
3043                                return Err(Error::HierarchyRequest(None));
3044                            }
3045                        },
3046                        None => {
3047                            if parent.child_elements().next().is_some() {
3048                                return Err(Error::HierarchyRequest(None));
3049                            }
3050                        },
3051                    }
3052                },
3053                NodeTypeId::CharacterData(_) => (),
3054                // Because Document and Attr should already throw `HierarchyRequest`
3055                // error, both of them are unreachable here.
3056                NodeTypeId::Document(_) | NodeTypeId::Attr => unreachable!(),
3057            }
3058        }
3059        Ok(())
3060    }
3061
3062    /// <https://dom.spec.whatwg.org/#concept-node-pre-insert>
3063    pub(crate) fn pre_insert(
3064        cx: &mut JSContext,
3065        node: &Node,
3066        parent: &Node,
3067        child: Option<&Node>,
3068    ) -> Fallible<DomRoot<Node>> {
3069        // Step 1. Ensure pre-insert validity of node into parent before child.
3070        Node::ensure_pre_insertion_validity(node, parent, child)?;
3071
3072        // Step 2. Let referenceChild be child.
3073        let reference_child_root;
3074        let reference_child = match child {
3075            // Step 3. If referenceChild is node, then set referenceChild to node’s next sibling.
3076            Some(child) if child == node => {
3077                reference_child_root = node.GetNextSibling();
3078                reference_child_root.as_deref()
3079            },
3080            _ => child,
3081        };
3082
3083        // Step 4. Insert node into parent before referenceChild.
3084        Node::insert(
3085            cx,
3086            node,
3087            parent,
3088            reference_child,
3089            SuppressObserver::Unsuppressed,
3090        );
3091
3092        // Step 5. Return node.
3093        Ok(DomRoot::from_ref(node))
3094    }
3095
3096    /// <https://dom.spec.whatwg.org/#concept-node-insert>
3097    fn insert(
3098        cx: &mut JSContext,
3099        node: &Node,
3100        parent: &Node,
3101        child: Option<&Node>,
3102        suppress_observers: SuppressObserver,
3103    ) {
3104        debug_assert!(child.is_none_or(|child| Some(parent) == child.GetParentNode().as_deref()));
3105
3106        // Step 1. Let nodes be node’s children, if node is a DocumentFragment node; otherwise « node ».
3107        rooted_vec!(let mut new_nodes);
3108        let new_nodes = if let NodeTypeId::DocumentFragment(_) = node.type_id() {
3109            new_nodes.extend(node.children().map(|node| Dom::from_ref(&*node)));
3110            new_nodes.r()
3111        } else {
3112            from_ref(&node)
3113        };
3114
3115        // Step 2. Let count be nodes’s size.
3116        let count = new_nodes.len();
3117
3118        // Step 3. If count is 0, then return.
3119        if count == 0 {
3120            return;
3121        }
3122
3123        // Script and layout blockers must be added after any early return.
3124        // `node.owner_doc()` may change during the algorithm.
3125        let parent_document = parent.owner_doc();
3126        let from_document = node.owner_doc();
3127        from_document.add_script_and_layout_blocker();
3128        parent_document.add_script_and_layout_blocker();
3129
3130        // Step 4. If node is a DocumentFragment node:
3131        if let NodeTypeId::DocumentFragment(_) = node.type_id() {
3132            // Step 4.1. Remove its children with the suppress observers flag set.
3133            for kid in new_nodes {
3134                Node::remove(cx, kid, node, SuppressObserver::Suppressed);
3135            }
3136            vtable_for(node).children_changed(cx, &ChildrenMutation::replace_all(new_nodes, &[]));
3137
3138            // Step 4.2. Queue a tree mutation record for node with « », nodes, null, and null.
3139            let mutation = LazyCell::new(|| Mutation::ChildList {
3140                added: None,
3141                removed: Some(new_nodes),
3142                prev: None,
3143                next: None,
3144            });
3145            MutationObserver::queue_a_mutation_record(node, mutation);
3146        }
3147
3148        // Step 5. If child is non-null:
3149        //     1. For each live range whose start node is parent and start offset is
3150        //        greater than child’s index, increase its start offset by count.
3151        //     2. For each live range whose end node is parent and end offset is
3152        //        greater than child’s index, increase its end offset by count.
3153        if let Some(child) = child {
3154            if !parent.ranges_is_empty() {
3155                parent
3156                    .ranges()
3157                    .increase_above(parent, child.index(), count.try_into().unwrap());
3158            }
3159        }
3160
3161        // Step 6. Let previousSibling be child’s previous sibling or parent’s last child if child is null.
3162        let previous_sibling = match suppress_observers {
3163            SuppressObserver::Unsuppressed => match child {
3164                Some(child) => child.GetPreviousSibling(),
3165                None => parent.GetLastChild(),
3166            },
3167            SuppressObserver::Suppressed => None,
3168        };
3169
3170        let custom_element_reaction_stack = ScriptThread::custom_element_reaction_stack();
3171
3172        // Step 10. Let staticNodeList be a list of nodes, initially « ».
3173        let mut static_node_list: SmallVec<[_; 4]> = Default::default();
3174
3175        let parent_shadow_root = parent.downcast::<Element>().and_then(Element::shadow_root);
3176        let parent_in_shadow_tree = parent.is_in_a_shadow_tree();
3177        let parent_as_slot = parent.downcast::<HTMLSlotElement>();
3178
3179        // Step 7. For each node in nodes, in tree order:
3180        for kid in new_nodes {
3181            // Step 7.1. Adopt node into parent’s node document.
3182            Node::adopt(cx, kid, &parent.owner_document());
3183
3184            // Step 7.2. If child is null, then append node to parent’s children.
3185            // Step 7.3. Otherwise, insert node into parent’s children before child’s index.
3186            parent.add_child(cx, kid, child);
3187
3188            // Step 7.4 If parent is a shadow host whose shadow root’s slot assignment is "named"
3189            // and node is a slottable, then assign a slot for node.
3190            if let Some(ref shadow_root) = parent_shadow_root {
3191                if shadow_root.SlotAssignment() == SlotAssignmentMode::Named {
3192                    let cx = GlobalScope::get_cx();
3193                    if kid.is::<Element>() || kid.is::<Text>() {
3194                        rooted!(in(*cx) let slottable = Slottable(Dom::from_ref(kid)));
3195                        slottable.assign_a_slot();
3196                    }
3197                }
3198            }
3199
3200            // Step 7.5 If parent’s root is a shadow root, and parent is a slot whose assigned nodes
3201            // is the empty list, then run signal a slot change for parent.
3202            if parent_in_shadow_tree {
3203                if let Some(slot_element) = parent_as_slot {
3204                    if !slot_element.has_assigned_nodes() {
3205                        slot_element.signal_a_slot_change();
3206                    }
3207                }
3208            }
3209
3210            // Step 7.6 Run assign slottables for a tree with node’s root.
3211            kid.GetRootNode(&GetRootNodeOptions::empty())
3212                .assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Skip);
3213
3214            // Step 7.7. For each shadow-including inclusive descendant inclusiveDescendant of node,
3215            // in shadow-including tree order:
3216            for descendant in kid.traverse_preorder(ShadowIncluding::Yes) {
3217                // Step 11.1 For each shadow-including inclusive descendant inclusiveDescendant of node,
3218                //           in shadow-including tree order, append inclusiveDescendant to staticNodeList.
3219                if descendant.is_connected() {
3220                    static_node_list.push(descendant.clone());
3221                }
3222
3223                // Step 7.7.1. Run the insertion steps with inclusiveDescendant.
3224                // This is done in `parent.add_child()`.
3225
3226                // Step 7.7.2, whatwg/dom#833
3227                // Enqueue connected reactions for custom elements or try upgrade.
3228                if let Some(descendant) = DomRoot::downcast::<Element>(descendant) {
3229                    if descendant.is_custom() {
3230                        if descendant.is_connected() {
3231                            custom_element_reaction_stack.enqueue_callback_reaction(
3232                                &descendant,
3233                                CallbackReaction::Connected,
3234                                None,
3235                            );
3236                        }
3237                    } else {
3238                        try_upgrade_element(&descendant);
3239                    }
3240                }
3241            }
3242        }
3243
3244        if let SuppressObserver::Unsuppressed = suppress_observers {
3245            // Step 9. Run the children changed steps for parent.
3246            // TODO(xiaochengh): If we follow the spec and move it out of the if block, some WPT fail. Investigate.
3247            vtable_for(parent).children_changed(
3248                cx,
3249                &ChildrenMutation::insert(previous_sibling.as_deref(), new_nodes, child),
3250            );
3251
3252            // Step 8. If suppress observers flag is unset, then queue a tree mutation record for parent
3253            // with nodes, « », previousSibling, and child.
3254            let mutation = LazyCell::new(|| Mutation::ChildList {
3255                added: Some(new_nodes),
3256                removed: None,
3257                prev: previous_sibling.as_deref(),
3258                next: child,
3259            });
3260            MutationObserver::queue_a_mutation_record(parent, mutation);
3261        }
3262
3263        // We use a delayed task for this step to work around an awkward interaction between
3264        // script/layout blockers, Node::replace_all, and the children_changed vtable method.
3265        // Any node with a post connection step that triggers layout (such as iframes) needs
3266        // to be marked as dirty before doing so. This is handled by Node's children_changed
3267        // callback, but when Node::insert is called as part of Node::replace_all then the
3268        // callback is suppressed until we return to Node::replace_all. To ensure the sequence:
3269        // 1) children_changed in Node::replace_all,
3270        // 2) post_connection_steps from Node::insert,
3271        // we use a delayed task that will run as soon as Node::insert removes its
3272        // script/layout blocker.
3273        parent_document.add_delayed_task(
3274            task!(PostConnectionSteps: |cx, static_node_list: SmallVec<[DomRoot<Node>; 4]>| {
3275                // Step 12. For each node of staticNodeList, if node is connected, then run the
3276                //          post-connection steps with node.
3277                for node in static_node_list {
3278                    vtable_for(&node).post_connection_steps(cx);
3279                }
3280            }),
3281        );
3282
3283        parent_document.remove_script_and_layout_blocker();
3284        from_document.remove_script_and_layout_blocker();
3285    }
3286
3287    /// <https://dom.spec.whatwg.org/#concept-node-replace-all>
3288    pub(crate) fn replace_all(cx: &mut JSContext, node: Option<&Node>, parent: &Node) {
3289        parent.owner_doc().add_script_and_layout_blocker();
3290
3291        // Step 1. Let removedNodes be parent’s children.
3292        rooted_vec!(let removed_nodes <- parent.children().map(|child| DomRoot::as_traced(&child)));
3293
3294        // Step 2. Let addedNodes be the empty set.
3295        // Step 3. If node is a DocumentFragment node, then set addedNodes to node’s children.
3296        // Step 4. Otherwise, if node is non-null, set addedNodes to « node ».
3297        rooted_vec!(let mut added_nodes);
3298        let added_nodes = if let Some(node) = node.as_ref() {
3299            if let NodeTypeId::DocumentFragment(_) = node.type_id() {
3300                added_nodes.extend(node.children().map(|child| Dom::from_ref(&*child)));
3301                added_nodes.r()
3302            } else {
3303                from_ref(node)
3304            }
3305        } else {
3306            &[] as &[&Node]
3307        };
3308
3309        // Step 5. Remove all parent’s children, in tree order, with suppressObservers set to true.
3310        for child in &*removed_nodes {
3311            Node::remove(cx, child, parent, SuppressObserver::Suppressed);
3312        }
3313
3314        // Step 6. If node is non-null, then insert node into parent before null with suppressObservers set to true.
3315        if let Some(node) = node {
3316            Node::insert(cx, node, parent, None, SuppressObserver::Suppressed);
3317        }
3318
3319        vtable_for(parent).children_changed(
3320            cx,
3321            &ChildrenMutation::replace_all(removed_nodes.r(), added_nodes),
3322        );
3323
3324        // Step 7. If either addedNodes or removedNodes is not empty, then queue a tree mutation record
3325        // for parent with addedNodes, removedNodes, null, and null.
3326        if !removed_nodes.is_empty() || !added_nodes.is_empty() {
3327            let mutation = LazyCell::new(|| Mutation::ChildList {
3328                added: Some(added_nodes),
3329                removed: Some(removed_nodes.r()),
3330                prev: None,
3331                next: None,
3332            });
3333            MutationObserver::queue_a_mutation_record(parent, mutation);
3334        }
3335        parent.owner_doc().remove_script_and_layout_blocker();
3336    }
3337
3338    /// <https://dom.spec.whatwg.org/multipage/#string-replace-all>
3339    pub(crate) fn string_replace_all(cx: &mut JSContext, string: DOMString, parent: &Node) {
3340        if string.is_empty() {
3341            Node::replace_all(cx, None, parent);
3342        } else {
3343            let text = Text::new(cx, string, &parent.owner_document());
3344            Node::replace_all(cx, Some(text.upcast::<Node>()), parent);
3345        };
3346    }
3347
3348    /// <https://dom.spec.whatwg.org/#concept-node-pre-remove>
3349    fn pre_remove(cx: &mut JSContext, child: &Node, parent: &Node) -> Fallible<DomRoot<Node>> {
3350        // Step 1.
3351        match child.GetParentNode() {
3352            Some(ref node) if &**node != parent => return Err(Error::NotFound(None)),
3353            None => return Err(Error::NotFound(None)),
3354            _ => (),
3355        }
3356
3357        // Step 2.
3358        Node::remove(cx, child, parent, SuppressObserver::Unsuppressed);
3359
3360        // Step 3.
3361        Ok(DomRoot::from_ref(child))
3362    }
3363
3364    /// <https://dom.spec.whatwg.org/#concept-node-remove>
3365    fn remove(
3366        cx: &mut JSContext,
3367        node: &Node,
3368        parent: &Node,
3369        suppress_observers: SuppressObserver,
3370    ) {
3371        parent.owner_doc().add_script_and_layout_blocker();
3372
3373        // Step 1. Let parent be node’s parent.
3374        // Step 2. Assert: parent is non-null.
3375        // NOTE: We get parent as an argument instead
3376        assert!(
3377            node.GetParentNode()
3378                .is_some_and(|node_parent| &*node_parent == parent)
3379        );
3380
3381        // Step 3. Run the live range pre-remove steps.
3382        // https://dom.spec.whatwg.org/#live-range-pre-remove-steps
3383        let cached_index = Node::live_range_pre_remove_steps(node, parent);
3384
3385        // TODO: Step 4. Pre-removing steps for node iterators
3386
3387        // Step 5.
3388        let old_previous_sibling = node.GetPreviousSibling();
3389
3390        // Step 6.
3391        let old_next_sibling = node.GetNextSibling();
3392
3393        // Step 7. Remove node from its parent's children.
3394        // Step 11-14. Run removing steps and enqueue disconnected custom element reactions for the subtree.
3395        parent.remove_child(node, cached_index, CanGc::from_cx(cx));
3396
3397        // Step 8. If node is assigned, then run assign slottables for node’s assigned slot.
3398        if let Some(slot) = node.assigned_slot() {
3399            slot.assign_slottables();
3400        }
3401
3402        // Step 9. If parent’s root is a shadow root, and parent is a slot whose assigned nodes is the empty list,
3403        // then run signal a slot change for parent.
3404        if parent.is_in_a_shadow_tree() {
3405            if let Some(slot_element) = parent.downcast::<HTMLSlotElement>() {
3406                if !slot_element.has_assigned_nodes() {
3407                    slot_element.signal_a_slot_change();
3408                }
3409            }
3410        }
3411
3412        // Step 10. If node has an inclusive descendant that is a slot:
3413        let has_slot_descendant = node
3414            .traverse_preorder(ShadowIncluding::No)
3415            .any(|elem| elem.is::<HTMLSlotElement>());
3416        if has_slot_descendant {
3417            // Step 10.1 Run assign slottables for a tree with parent’s root.
3418            parent
3419                .GetRootNode(&GetRootNodeOptions::empty())
3420                .assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Skip);
3421
3422            // Step 10.2 Run assign slottables for a tree with node.
3423            node.assign_slottables_for_a_tree(ForceSlottableNodeReconciliation::Force);
3424        }
3425
3426        // TODO: Step 15. transient registered observers
3427
3428        // Step 16.
3429        if let SuppressObserver::Unsuppressed = suppress_observers {
3430            vtable_for(parent).children_changed(
3431                cx,
3432                &ChildrenMutation::replace(
3433                    old_previous_sibling.as_deref(),
3434                    &Some(node),
3435                    &[],
3436                    old_next_sibling.as_deref(),
3437                ),
3438            );
3439
3440            let removed = [node];
3441            let mutation = LazyCell::new(|| Mutation::ChildList {
3442                added: None,
3443                removed: Some(&removed),
3444                prev: old_previous_sibling.as_deref(),
3445                next: old_next_sibling.as_deref(),
3446            });
3447            MutationObserver::queue_a_mutation_record(parent, mutation);
3448        }
3449        parent.owner_doc().remove_script_and_layout_blocker();
3450    }
3451
3452    /// <https://dom.spec.whatwg.org/#live-range-pre-remove-steps>
3453    fn live_range_pre_remove_steps(node: &Node, parent: &Node) -> Option<u32> {
3454        if parent.ranges_is_empty() {
3455            return None;
3456        }
3457
3458        // Step 1. Let parent be node’s parent.
3459        // Step 2. Assert: parent is not null.
3460        // NOTE: We already have the parent.
3461
3462        // Step 3. Let index be node’s index.
3463        let index = node.index();
3464
3465        // Steps 4-5 are handled in Node::unbind_from_tree.
3466
3467        // Step 6. For each live range whose start node is parent and start offset is greater than index,
3468        // decrease its start offset by 1.
3469        // Step 7. For each live range whose end node is parent and end offset is greater than index,
3470        // decrease its end offset by 1.
3471        parent.ranges().decrease_above(parent, index, 1);
3472
3473        // Parent had ranges, we needed the index, let's keep track of
3474        // it to avoid computing it for other ranges when calling
3475        // unbind_from_tree recursively.
3476        Some(index)
3477    }
3478
3479    /// Ensure that for styles, we clone the already-parsed property declaration block.
3480    /// This does two things:
3481    /// 1. it uses the same fast-path as CSSStyleDeclaration
3482    /// 2. it also avoids the CSP checks when cloning (it shouldn't run any when cloning
3483    ///    existing valid attributes)
3484    fn compute_attribute_value_with_style_fast_path(attr: &Dom<Attr>, elem: &Element) -> AttrValue {
3485        if *attr.local_name() == local_name!("style") {
3486            if let Some(ref pdb) = *elem.style_attribute().borrow() {
3487                let document = elem.owner_document();
3488                let shared_lock = document.style_shared_lock();
3489                let new_pdb = pdb.read_with(&shared_lock.read()).clone();
3490                return AttrValue::Declaration(
3491                    (**attr.value()).to_owned(),
3492                    ServoArc::new(shared_lock.wrap(new_pdb)),
3493                );
3494            }
3495        }
3496
3497        attr.value().clone()
3498    }
3499
3500    /// <https://dom.spec.whatwg.org/#concept-node-clone>
3501    pub(crate) fn clone(
3502        cx: &mut JSContext,
3503        node: &Node,
3504        maybe_doc: Option<&Document>,
3505        clone_children: CloneChildrenFlag,
3506        registry: Option<DomRoot<CustomElementRegistry>>,
3507    ) -> DomRoot<Node> {
3508        // Step 1. If document is not given, let document be node’s node document.
3509        let document = match maybe_doc {
3510            Some(doc) => DomRoot::from_ref(doc),
3511            None => node.owner_doc(),
3512        };
3513
3514        // Step 2. / Step 3.
3515        // XXXabinader: clone() for each node as trait?
3516        let copy: DomRoot<Node> = match node.type_id() {
3517            NodeTypeId::DocumentType => {
3518                let doctype = node.downcast::<DocumentType>().unwrap();
3519                let doctype = DocumentType::new(
3520                    cx,
3521                    doctype.name().clone(),
3522                    Some(doctype.public_id().clone()),
3523                    Some(doctype.system_id().clone()),
3524                    &document,
3525                );
3526                DomRoot::upcast::<Node>(doctype)
3527            },
3528            NodeTypeId::Attr => {
3529                let attr = node.downcast::<Attr>().unwrap();
3530                let attr = Attr::new(
3531                    cx,
3532                    &document,
3533                    attr.local_name().clone(),
3534                    attr.value().clone(),
3535                    attr.name().clone(),
3536                    attr.namespace().clone(),
3537                    attr.prefix().cloned(),
3538                    None,
3539                );
3540                DomRoot::upcast::<Node>(attr)
3541            },
3542            NodeTypeId::DocumentFragment(_) => {
3543                let doc_fragment = DocumentFragment::new(cx, &document);
3544                DomRoot::upcast::<Node>(doc_fragment)
3545            },
3546            NodeTypeId::CharacterData(_) => {
3547                let cdata = node.downcast::<CharacterData>().unwrap();
3548                cdata.clone_with_data(cx, cdata.Data(), &document)
3549            },
3550            NodeTypeId::Document(_) => {
3551                // Step 1. Set copy’s encoding, content type, URL, origin, type, mode,
3552                // and allow declarative shadow roots, to those of node.
3553                let document = node.downcast::<Document>().unwrap();
3554                let is_html_doc = if document.is_html_document() {
3555                    IsHTMLDocument::HTMLDocument
3556                } else {
3557                    IsHTMLDocument::NonHTMLDocument
3558                };
3559                let window = document.window();
3560                let loader = DocumentLoader::new(&document.loader());
3561                let document = Document::new(
3562                    window,
3563                    HasBrowsingContext::No,
3564                    Some(document.url()),
3565                    None,
3566                    // https://github.com/whatwg/dom/issues/378
3567                    document.origin().clone(),
3568                    is_html_doc,
3569                    None,
3570                    None,
3571                    DocumentActivity::Inactive,
3572                    DocumentSource::NotFromParser,
3573                    loader,
3574                    None,
3575                    document.status_code(),
3576                    Default::default(),
3577                    false,
3578                    document.allow_declarative_shadow_roots(),
3579                    Some(document.insecure_requests_policy()),
3580                    document.has_trustworthy_ancestor_or_current_origin(),
3581                    document.custom_element_reaction_stack(),
3582                    document.creation_sandboxing_flag_set(),
3583                    CanGc::from_cx(cx),
3584                );
3585                // Step 2. If node’s custom element registry’s is scoped is true,
3586                // then set copy’s custom element registry to node’s custom element registry.
3587                // TODO
3588                DomRoot::upcast::<Node>(document)
3589            },
3590            // Step 2. If node is an element:
3591            NodeTypeId::Element(..) => {
3592                let element = node.downcast::<Element>().unwrap();
3593                // Step 2.1. Let registry be node’s custom element registry.
3594                // Step 2.2. If registry is null, then set registry to fallbackRegistry.
3595                let registry = element.custom_element_registry().or(registry);
3596                // Step 2.3. If registry is a global custom element registry, then
3597                // set registry to document’s effective global custom element registry.
3598                let registry =
3599                    if CustomElementRegistry::is_a_global_element_registry(registry.as_deref()) {
3600                        Some(document.custom_element_registry())
3601                    } else {
3602                        registry
3603                    };
3604                // Step 2.4. Set copy to the result of creating an element,
3605                // given document, node’s local name, node’s namespace,
3606                // node’s namespace prefix, node’s is value, false, and registry.
3607                let name = QualName {
3608                    prefix: element.prefix().as_ref().map(|p| Prefix::from(&**p)),
3609                    ns: element.namespace().clone(),
3610                    local: element.local_name().clone(),
3611                };
3612                let element = Element::create(
3613                    cx,
3614                    name,
3615                    element.get_is(),
3616                    &document,
3617                    ElementCreator::ScriptCreated,
3618                    CustomElementCreationMode::Asynchronous,
3619                    None,
3620                );
3621                // TODO: Move this into `Element::create`
3622                element.set_custom_element_registry(registry);
3623                DomRoot::upcast::<Node>(element)
3624            },
3625        };
3626
3627        // Step 4. Set copy’s node document and document to copy, if copy is a document,
3628        // and set copy’s node document to document otherwise.
3629        let document = match copy.downcast::<Document>() {
3630            Some(doc) => DomRoot::from_ref(doc),
3631            None => DomRoot::from_ref(&*document),
3632        };
3633        assert!(copy.owner_doc() == document);
3634
3635        // TODO: The spec tells us to do this in step 3.
3636        match node.type_id() {
3637            NodeTypeId::Document(_) => {
3638                let node_doc = node.downcast::<Document>().unwrap();
3639                let copy_doc = copy.downcast::<Document>().unwrap();
3640                copy_doc.set_encoding(node_doc.encoding());
3641                copy_doc.set_quirks_mode(node_doc.quirks_mode());
3642            },
3643            NodeTypeId::Element(..) => {
3644                let node_elem = node.downcast::<Element>().unwrap();
3645                let copy_elem = copy.downcast::<Element>().unwrap();
3646
3647                // Step 2.5. For each attribute of node’s attribute list:
3648                for attr in node_elem.attrs().iter() {
3649                    // Step 2.5.1. Let copyAttribute be the result of cloning a single node given attribute, document, and null.
3650                    let new_value =
3651                        Node::compute_attribute_value_with_style_fast_path(attr, node_elem);
3652                    // Step 2.5.2. Append copyAttribute to copy.
3653                    copy_elem.push_new_attribute(
3654                        attr.local_name().clone(),
3655                        new_value,
3656                        attr.name().clone(),
3657                        attr.namespace().clone(),
3658                        attr.prefix().cloned(),
3659                        AttributeMutationReason::ByCloning,
3660                        CanGc::from_cx(cx),
3661                    );
3662                }
3663            },
3664            _ => (),
3665        }
3666
3667        // Step 5: Run any cloning steps defined for node in other applicable specifications and pass copy,
3668        // node, document, and the clone children flag if set, as parameters.
3669        vtable_for(node).cloning_steps(cx, &copy, maybe_doc, clone_children);
3670
3671        // Step 6. If the clone children flag is set, then for each child child of node, in tree order: append the
3672        // result of cloning child with document and the clone children flag set, to copy.
3673        if clone_children == CloneChildrenFlag::CloneChildren {
3674            for child in node.children() {
3675                let child_copy = Node::clone(cx, &child, Some(&document), clone_children, None);
3676                let _inserted_node = Node::pre_insert(cx, &child_copy, &copy, None);
3677            }
3678        }
3679
3680        // Step 7. If node is a shadow host whose shadow root’s clonable is true:
3681        // NOTE: Only elements can be shadow hosts
3682        if matches!(node.type_id(), NodeTypeId::Element(_)) {
3683            let node_elem = node.downcast::<Element>().unwrap();
3684            let copy_elem = copy.downcast::<Element>().unwrap();
3685
3686            if let Some(shadow_root) = node_elem.shadow_root().filter(|r| r.Clonable()) {
3687                // Step 7.1 Assert: copy is not a shadow host.
3688                assert!(!copy_elem.is_shadow_host());
3689
3690                // Step 7.2 Run attach a shadow root with copy, node’s shadow root’s mode, true,
3691                // node’s shadow root’s serializable, node’s shadow root’s delegates focus,
3692                // and node’s shadow root’s slot assignment.
3693                let copy_shadow_root =
3694                    copy_elem.attach_shadow(
3695                        cx,
3696                        IsUserAgentWidget::No,
3697                        shadow_root.Mode(),
3698                        shadow_root.Clonable(),
3699                        shadow_root.Serializable(),
3700                        shadow_root.DelegatesFocus(),
3701                        shadow_root.SlotAssignment(),
3702                    )
3703                    .expect("placement of attached shadow root must be valid, as this is a copy of an existing one");
3704
3705                // Step 7.3 Set copy’s shadow root’s declarative to node’s shadow root’s declarative.
3706                copy_shadow_root.set_declarative(shadow_root.is_declarative());
3707
3708                // Step 7.4 For each child child of node’s shadow root, in tree order: append the result of
3709                // cloning child with document and the clone children flag set, to copy’s shadow root.
3710                for child in shadow_root.upcast::<Node>().children() {
3711                    let child_copy = Node::clone(
3712                        cx,
3713                        &child,
3714                        Some(&document),
3715                        CloneChildrenFlag::CloneChildren,
3716                        None,
3717                    );
3718
3719                    // TODO: Should we handle the error case here and in step 6?
3720                    let _inserted_node =
3721                        Node::pre_insert(cx, &child_copy, copy_shadow_root.upcast::<Node>(), None);
3722                }
3723            }
3724        }
3725
3726        // Step 8. Return copy.
3727        copy
3728    }
3729
3730    /// <https://html.spec.whatwg.org/multipage/#child-text-content>
3731    pub(crate) fn child_text_content(&self) -> DOMString {
3732        Node::collect_text_contents(self.children())
3733    }
3734
3735    /// <https://html.spec.whatwg.org/multipage/#descendant-text-content>
3736    pub(crate) fn descendant_text_content(&self) -> DOMString {
3737        Node::collect_text_contents(self.traverse_preorder(ShadowIncluding::No))
3738    }
3739
3740    pub(crate) fn collect_text_contents<T: Iterator<Item = DomRoot<Node>>>(
3741        iterator: T,
3742    ) -> DOMString {
3743        let mut content = String::new();
3744        for node in iterator {
3745            if let Some(text) = node.downcast::<Text>() {
3746                content.push_str(&text.upcast::<CharacterData>().data());
3747            }
3748        }
3749        DOMString::from(content)
3750    }
3751
3752    /// <https://dom.spec.whatwg.org/#string-replace-all>
3753    pub(crate) fn set_text_content_for_element(
3754        &self,
3755        cx: &mut JSContext,
3756        value: Option<DOMString>,
3757    ) {
3758        // This should only be called for elements and document fragments when setting the
3759        // text content: https://dom.spec.whatwg.org/#set-text-content
3760        assert!(matches!(
3761            self.type_id(),
3762            NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..)
3763        ));
3764        let value = value.unwrap_or_default();
3765        let node = if value.is_empty() {
3766            // Step 1. Let node be null.
3767            None
3768        } else {
3769            // Step 2. If string is not the empty string, then set node to
3770            // a new Text node whose data is string and node document is parent’s node document.
3771            Some(DomRoot::upcast(self.owner_doc().CreateTextNode(cx, value)))
3772        };
3773
3774        // Step 3. Replace all with node within parent.
3775        Self::replace_all(cx, node.as_deref(), self);
3776    }
3777
3778    pub(crate) fn namespace_to_string(namespace: Namespace) -> Option<DOMString> {
3779        match namespace {
3780            ns!() => None,
3781            // FIXME(ajeffrey): convert directly from Namespace to DOMString
3782            _ => Some(DOMString::from(&*namespace)),
3783        }
3784    }
3785
3786    /// <https://dom.spec.whatwg.org/#locate-a-namespace>
3787    pub(crate) fn locate_namespace(node: &Node, prefix: Option<DOMString>) -> Namespace {
3788        match node.type_id() {
3789            NodeTypeId::Element(_) => node.downcast::<Element>().unwrap().locate_namespace(prefix),
3790            NodeTypeId::Attr => node
3791                .downcast::<Attr>()
3792                .unwrap()
3793                .GetOwnerElement()
3794                .as_ref()
3795                .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
3796            NodeTypeId::Document(_) => node
3797                .downcast::<Document>()
3798                .unwrap()
3799                .GetDocumentElement()
3800                .as_ref()
3801                .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
3802            NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => ns!(),
3803            _ => node
3804                .GetParentElement()
3805                .as_ref()
3806                .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
3807        }
3808    }
3809
3810    /// If the given untrusted node address represents a valid DOM node in the given runtime,
3811    /// returns it.
3812    ///
3813    /// # Safety
3814    ///
3815    /// Callers should ensure they pass an UntrustedNodeAddress that points to a valid [`JSObject`]
3816    /// in memory that represents a [`Node`].
3817    #[expect(unsafe_code)]
3818    pub(crate) unsafe fn from_untrusted_node_address(
3819        candidate: UntrustedNodeAddress,
3820    ) -> &'static Self {
3821        // https://github.com/servo/servo/issues/6383
3822        let candidate = candidate.0 as usize;
3823        let object = candidate as *mut JSObject;
3824        if object.is_null() {
3825            panic!("Attempted to create a `Node` from an invalid pointer!")
3826        }
3827
3828        unsafe { &*(conversions::private_from_object(object) as *const Self) }
3829    }
3830
3831    pub(crate) fn html_serialize(
3832        &self,
3833        cx: &mut js::context::JSContext,
3834        traversal_scope: html_serialize::TraversalScope,
3835        serialize_shadow_roots: bool,
3836        shadow_roots: Vec<DomRoot<ShadowRoot>>,
3837    ) -> DOMString {
3838        let mut writer = vec![];
3839        let mut serializer = HtmlSerializer::new(
3840            &mut writer,
3841            html_serialize::SerializeOpts {
3842                traversal_scope: traversal_scope.clone(),
3843                ..Default::default()
3844            },
3845        );
3846
3847        serialize_html_fragment(
3848            cx,
3849            self,
3850            &mut serializer,
3851            traversal_scope,
3852            serialize_shadow_roots,
3853            shadow_roots,
3854        )
3855        .expect("Serializing node failed");
3856
3857        // FIXME(ajeffrey): Directly convert UTF8 to DOMString
3858        DOMString::from(String::from_utf8(writer).unwrap())
3859    }
3860
3861    /// <https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization>
3862    pub(crate) fn xml_serialize(
3863        &self,
3864        traversal_scope: xml_serialize::TraversalScope,
3865    ) -> Fallible<DOMString> {
3866        let mut writer = vec![];
3867        xml_serialize::serialize(
3868            &mut writer,
3869            &HtmlSerialize::new(self),
3870            xml_serialize::SerializeOpts { traversal_scope },
3871        )
3872        .map_err(|error| {
3873            error!("Cannot serialize node: {error}");
3874            Error::InvalidState(None)
3875        })?;
3876
3877        // FIXME(ajeffrey): Directly convert UTF8 to DOMString
3878        let string = DOMString::from(String::from_utf8(writer).map_err(|error| {
3879            error!("Cannot serialize node: {error}");
3880            Error::InvalidState(None)
3881        })?);
3882
3883        Ok(string)
3884    }
3885
3886    /// <https://html.spec.whatwg.org/multipage/#fragment-serializing-algorithm-steps>
3887    pub(crate) fn fragment_serialization_algorithm(
3888        &self,
3889        cx: &mut js::context::JSContext,
3890        require_well_formed: bool,
3891    ) -> Fallible<DOMString> {
3892        // Step 1. Let context document be node's node document.
3893        let context_document = self.owner_document();
3894
3895        // Step 2. If context document is an HTML document, return the result of HTML fragment serialization algorithm
3896        // with node, false, and « ».
3897        if context_document.is_html_document() {
3898            return Ok(self.html_serialize(
3899                cx,
3900                html_serialize::TraversalScope::ChildrenOnly(None),
3901                false,
3902                vec![],
3903            ));
3904        }
3905
3906        // Step 3. Return the XML serialization of node given require well-formed.
3907        // TODO: xml5ever doesn't seem to want require_well_formed
3908        let _ = require_well_formed;
3909        self.xml_serialize(xml_serialize::TraversalScope::ChildrenOnly(None))
3910    }
3911
3912    fn get_next_sibling_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, Node>> {
3913        self.next_sibling.get_unrooted(no_gc)
3914    }
3915
3916    fn get_previous_sibling_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, Node>> {
3917        self.prev_sibling.get_unrooted(no_gc)
3918    }
3919
3920    fn get_first_child_unrooted<'a>(&self, no_gc: &'a NoGC) -> Option<UnrootedDom<'a, Node>> {
3921        self.first_child.get_unrooted(no_gc)
3922    }
3923}
3924
3925impl NodeMethods<crate::DomTypeHolder> for Node {
3926    /// <https://dom.spec.whatwg.org/#dom-node-nodetype>
3927    fn NodeType(&self) -> u16 {
3928        match self.type_id() {
3929            NodeTypeId::Attr => NodeConstants::ATTRIBUTE_NODE,
3930            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
3931                NodeConstants::TEXT_NODE
3932            },
3933            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => {
3934                NodeConstants::CDATA_SECTION_NODE
3935            },
3936            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
3937                NodeConstants::PROCESSING_INSTRUCTION_NODE
3938            },
3939            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => NodeConstants::COMMENT_NODE,
3940            NodeTypeId::Document(_) => NodeConstants::DOCUMENT_NODE,
3941            NodeTypeId::DocumentType => NodeConstants::DOCUMENT_TYPE_NODE,
3942            NodeTypeId::DocumentFragment(_) => NodeConstants::DOCUMENT_FRAGMENT_NODE,
3943            NodeTypeId::Element(_) => NodeConstants::ELEMENT_NODE,
3944        }
3945    }
3946
3947    /// <https://dom.spec.whatwg.org/#dom-node-nodename>
3948    fn NodeName(&self) -> DOMString {
3949        match self.type_id() {
3950            NodeTypeId::Attr => self.downcast::<Attr>().unwrap().qualified_name(),
3951            NodeTypeId::Element(..) => self.downcast::<Element>().unwrap().TagName(),
3952            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
3953                DOMString::from("#text")
3954            },
3955            NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => {
3956                DOMString::from("#cdata-section")
3957            },
3958            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
3959                self.downcast::<ProcessingInstruction>().unwrap().Target()
3960            },
3961            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => DOMString::from("#comment"),
3962            NodeTypeId::DocumentType => self.downcast::<DocumentType>().unwrap().name().clone(),
3963            NodeTypeId::DocumentFragment(_) => DOMString::from("#document-fragment"),
3964            NodeTypeId::Document(_) => DOMString::from("#document"),
3965        }
3966    }
3967
3968    /// <https://dom.spec.whatwg.org/#dom-node-baseuri>
3969    fn BaseURI(&self) -> USVString {
3970        USVString(String::from(self.owner_doc().base_url().as_str()))
3971    }
3972
3973    /// <https://dom.spec.whatwg.org/#dom-node-isconnected>
3974    fn IsConnected(&self) -> bool {
3975        self.is_connected()
3976    }
3977
3978    /// <https://dom.spec.whatwg.org/#dom-node-ownerdocument>
3979    fn GetOwnerDocument(&self) -> Option<DomRoot<Document>> {
3980        match self.type_id() {
3981            NodeTypeId::Document(_) => None,
3982            _ => Some(self.owner_doc()),
3983        }
3984    }
3985
3986    /// <https://dom.spec.whatwg.org/#dom-node-getrootnode>
3987    fn GetRootNode(&self, options: &GetRootNodeOptions) -> DomRoot<Node> {
3988        if !options.composed {
3989            if let Some(shadow_root) = self.containing_shadow_root() {
3990                return DomRoot::upcast(shadow_root);
3991            }
3992        }
3993
3994        if self.is_connected() {
3995            DomRoot::from_ref(self.owner_doc().upcast::<Node>())
3996        } else {
3997            self.inclusive_ancestors(ShadowIncluding::Yes)
3998                .last()
3999                .unwrap()
4000        }
4001    }
4002
4003    /// <https://dom.spec.whatwg.org/#dom-node-parentnode>
4004    fn GetParentNode(&self) -> Option<DomRoot<Node>> {
4005        self.parent_node.get()
4006    }
4007
4008    /// <https://dom.spec.whatwg.org/#dom-node-parentelement>
4009    fn GetParentElement(&self) -> Option<DomRoot<Element>> {
4010        self.GetParentNode().and_then(DomRoot::downcast)
4011    }
4012
4013    /// <https://dom.spec.whatwg.org/#dom-node-haschildnodes>
4014    fn HasChildNodes(&self) -> bool {
4015        self.first_child.get().is_some()
4016    }
4017
4018    /// <https://dom.spec.whatwg.org/#dom-node-childnodes>
4019    fn ChildNodes(&self, can_gc: CanGc) -> DomRoot<NodeList> {
4020        if let Some(list) = self.ensure_rare_data().child_list.get() {
4021            return list;
4022        }
4023
4024        let doc = self.owner_doc();
4025        let window = doc.window();
4026        let list = NodeList::new_child_list(window, self, can_gc);
4027        self.ensure_rare_data().child_list.set(Some(&list));
4028        list
4029    }
4030
4031    /// <https://dom.spec.whatwg.org/#dom-node-firstchild>
4032    fn GetFirstChild(&self) -> Option<DomRoot<Node>> {
4033        self.first_child.get()
4034    }
4035
4036    /// <https://dom.spec.whatwg.org/#dom-node-lastchild>
4037    fn GetLastChild(&self) -> Option<DomRoot<Node>> {
4038        self.last_child.get()
4039    }
4040
4041    /// <https://dom.spec.whatwg.org/#dom-node-previoussibling>
4042    fn GetPreviousSibling(&self) -> Option<DomRoot<Node>> {
4043        self.prev_sibling.get()
4044    }
4045
4046    /// <https://dom.spec.whatwg.org/#dom-node-nextsibling>
4047    fn GetNextSibling(&self) -> Option<DomRoot<Node>> {
4048        self.next_sibling.get()
4049    }
4050
4051    /// <https://dom.spec.whatwg.org/#dom-node-nodevalue>
4052    fn GetNodeValue(&self) -> Option<DOMString> {
4053        match self.type_id() {
4054            NodeTypeId::Attr => Some(self.downcast::<Attr>().unwrap().Value()),
4055            NodeTypeId::CharacterData(_) => {
4056                self.downcast::<CharacterData>().map(CharacterData::Data)
4057            },
4058            _ => None,
4059        }
4060    }
4061
4062    /// <https://dom.spec.whatwg.org/#dom-node-nodevalue>
4063    fn SetNodeValue(&self, cx: &mut JSContext, val: Option<DOMString>) -> Fallible<()> {
4064        match self.type_id() {
4065            NodeTypeId::Attr => {
4066                let attr = self.downcast::<Attr>().unwrap();
4067                attr.SetValue(cx, val.unwrap_or_default())?;
4068            },
4069            NodeTypeId::CharacterData(_) => {
4070                let character_data = self.downcast::<CharacterData>().unwrap();
4071                character_data.SetData(val.unwrap_or_default());
4072            },
4073            _ => {},
4074        };
4075        Ok(())
4076    }
4077
4078    /// <https://dom.spec.whatwg.org/#dom-node-textcontent>
4079    fn GetTextContent(&self) -> Option<DOMString> {
4080        match self.type_id() {
4081            NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
4082                let content =
4083                    Node::collect_text_contents(self.traverse_preorder(ShadowIncluding::No));
4084                Some(content)
4085            },
4086            NodeTypeId::Attr => Some(self.downcast::<Attr>().unwrap().Value()),
4087            NodeTypeId::CharacterData(..) => {
4088                let characterdata = self.downcast::<CharacterData>().unwrap();
4089                Some(characterdata.Data())
4090            },
4091            NodeTypeId::DocumentType | NodeTypeId::Document(_) => None,
4092        }
4093    }
4094
4095    /// <https://dom.spec.whatwg.org/#set-text-content>
4096    fn SetTextContent(&self, cx: &mut JSContext, value: Option<DOMString>) -> Fallible<()> {
4097        match self.type_id() {
4098            NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
4099                self.set_text_content_for_element(cx, value);
4100            },
4101            NodeTypeId::Attr => {
4102                let attr = self.downcast::<Attr>().unwrap();
4103                attr.SetValue(cx, value.unwrap_or_default())?;
4104            },
4105            NodeTypeId::CharacterData(..) => {
4106                let characterdata = self.downcast::<CharacterData>().unwrap();
4107                characterdata.SetData(value.unwrap_or_default());
4108            },
4109            NodeTypeId::DocumentType | NodeTypeId::Document(_) => {},
4110        };
4111        Ok(())
4112    }
4113
4114    /// <https://dom.spec.whatwg.org/#dom-node-insertbefore>
4115    fn InsertBefore(
4116        &self,
4117        cx: &mut JSContext,
4118        node: &Node,
4119        child: Option<&Node>,
4120    ) -> Fallible<DomRoot<Node>> {
4121        Node::pre_insert(cx, node, self, child)
4122    }
4123
4124    /// <https://dom.spec.whatwg.org/#dom-node-appendchild>
4125    fn AppendChild(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
4126        Node::pre_insert(cx, node, self, None)
4127    }
4128
4129    /// <https://dom.spec.whatwg.org/#concept-node-replace>
4130    fn ReplaceChild(
4131        &self,
4132        cx: &mut JSContext,
4133        node: &Node,
4134        child: &Node,
4135    ) -> Fallible<DomRoot<Node>> {
4136        // Step 1. If parent is not a Document, DocumentFragment, or Element node,
4137        // then throw a "HierarchyRequestError" DOMException.
4138        match self.type_id() {
4139            NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
4140            },
4141            _ => return Err(Error::HierarchyRequest(None)),
4142        }
4143
4144        // Step 2. If node is a host-including inclusive ancestor of parent,
4145        // then throw a "HierarchyRequestError" DOMException.
4146        if node.is_inclusive_ancestor_of(self) {
4147            return Err(Error::HierarchyRequest(None));
4148        }
4149
4150        // Step 3. If child’s parent is not parent, then throw a "NotFoundError" DOMException.
4151        if !self.is_parent_of(child) {
4152            return Err(Error::NotFound(None));
4153        }
4154
4155        // Step 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node,
4156        // then throw a "HierarchyRequestError" DOMException.
4157        // Step 5. If either node is a Text node and parent is a document,
4158        // or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
4159        match node.type_id() {
4160            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) if self.is::<Document>() => {
4161                return Err(Error::HierarchyRequest(None));
4162            },
4163            NodeTypeId::DocumentType if !self.is::<Document>() => {
4164                return Err(Error::HierarchyRequest(None));
4165            },
4166            NodeTypeId::Document(_) | NodeTypeId::Attr => {
4167                return Err(Error::HierarchyRequest(None));
4168            },
4169            _ => (),
4170        }
4171
4172        // Step 6. If parent is a document, and any of the statements below, switched on the interface node implements,
4173        // are true, then throw a "HierarchyRequestError" DOMException.
4174        if self.is::<Document>() {
4175            match node.type_id() {
4176                // Step 6.1
4177                NodeTypeId::DocumentFragment(_) => {
4178                    // Step 6.1.1(b)
4179                    if node.children().any(|c| c.is::<Text>()) {
4180                        return Err(Error::HierarchyRequest(None));
4181                    }
4182                    match node.child_elements().count() {
4183                        0 => (),
4184                        // Step 6.1.2
4185                        1 => {
4186                            if self.child_elements().any(|c| c.upcast::<Node>() != child) {
4187                                return Err(Error::HierarchyRequest(None));
4188                            }
4189                            if child.following_siblings().any(|child| child.is_doctype()) {
4190                                return Err(Error::HierarchyRequest(None));
4191                            }
4192                        },
4193                        // Step 6.1.1(a)
4194                        _ => return Err(Error::HierarchyRequest(None)),
4195                    }
4196                },
4197                // Step 6.2
4198                NodeTypeId::Element(..) => {
4199                    if self.child_elements().any(|c| c.upcast::<Node>() != child) {
4200                        return Err(Error::HierarchyRequest(None));
4201                    }
4202                    if child.following_siblings().any(|child| child.is_doctype()) {
4203                        return Err(Error::HierarchyRequest(None));
4204                    }
4205                },
4206                // Step 6.3
4207                NodeTypeId::DocumentType => {
4208                    if self.children().any(|c| c.is_doctype() && &*c != child) {
4209                        return Err(Error::HierarchyRequest(None));
4210                    }
4211                    if self
4212                        .children()
4213                        .take_while(|c| &**c != child)
4214                        .any(|c| c.is::<Element>())
4215                    {
4216                        return Err(Error::HierarchyRequest(None));
4217                    }
4218                },
4219                NodeTypeId::CharacterData(..) => (),
4220                // Because Document and Attr should already throw `HierarchyRequest`
4221                // error, both of them are unreachable here.
4222                NodeTypeId::Document(_) => unreachable!(),
4223                NodeTypeId::Attr => unreachable!(),
4224            }
4225        }
4226
4227        // Step 7. Let referenceChild be child’s next sibling.
4228        // Step 8. If referenceChild is node, then set referenceChild to node’s next sibling.
4229        let child_next_sibling = child.GetNextSibling();
4230        let node_next_sibling = node.GetNextSibling();
4231        let reference_child = if child_next_sibling.as_deref() == Some(node) {
4232            node_next_sibling.as_deref()
4233        } else {
4234            child_next_sibling.as_deref()
4235        };
4236
4237        // Step 9. Let previousSibling be child’s previous sibling.
4238        let previous_sibling = child.GetPreviousSibling();
4239
4240        // NOTE: All existing browsers assume that adoption is performed here, which does not follow the DOM spec.
4241        // However, if we follow the spec and delay adoption to inside `Node::insert()`, then the mutation records will
4242        // be different, and we will fail WPT dom/nodes/MutationObserver-childList.html.
4243        let document = self.owner_document();
4244        Node::adopt(cx, node, &document);
4245
4246        // Step 10. Let removedNodes be the empty set.
4247        // Step 11. If child’s parent is non-null:
4248        //     1. Set removedNodes to « child ».
4249        //     2. Remove child with the suppress observers flag set.
4250        let removed_child = if node != child {
4251            // Step 11.
4252            Node::remove(cx, child, self, SuppressObserver::Suppressed);
4253            Some(child)
4254        } else {
4255            None
4256        };
4257
4258        // Step 12. Let nodes be node’s children if node is a DocumentFragment node; otherwise « node ».
4259        rooted_vec!(let mut nodes);
4260        let nodes = if node.type_id() ==
4261            NodeTypeId::DocumentFragment(DocumentFragmentTypeId::DocumentFragment) ||
4262            node.type_id() == NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
4263        {
4264            nodes.extend(node.children().map(|node| Dom::from_ref(&*node)));
4265            nodes.r()
4266        } else {
4267            from_ref(&node)
4268        };
4269
4270        // Step 13. Insert node into parent before referenceChild with the suppress observers flag set.
4271        Node::insert(
4272            cx,
4273            node,
4274            self,
4275            reference_child,
4276            SuppressObserver::Suppressed,
4277        );
4278
4279        vtable_for(self).children_changed(
4280            cx,
4281            &ChildrenMutation::replace(
4282                previous_sibling.as_deref(),
4283                &removed_child,
4284                nodes,
4285                reference_child,
4286            ),
4287        );
4288
4289        // Step 14. Queue a tree mutation record for parent with nodes, removedNodes,
4290        // previousSibling, and referenceChild.
4291        let removed = removed_child.map(|r| [r]);
4292        let mutation = LazyCell::new(|| Mutation::ChildList {
4293            added: Some(nodes),
4294            removed: removed.as_ref().map(|r| &r[..]),
4295            prev: previous_sibling.as_deref(),
4296            next: reference_child,
4297        });
4298
4299        MutationObserver::queue_a_mutation_record(self, mutation);
4300
4301        // Step 15. Return child.
4302        Ok(DomRoot::from_ref(child))
4303    }
4304
4305    /// <https://dom.spec.whatwg.org/#dom-node-removechild>
4306    fn RemoveChild(&self, cx: &mut JSContext, node: &Node) -> Fallible<DomRoot<Node>> {
4307        Node::pre_remove(cx, node, self)
4308    }
4309
4310    /// <https://dom.spec.whatwg.org/#dom-node-normalize>
4311    fn Normalize(&self, cx: &mut JSContext) {
4312        let mut children = self.children().enumerate().peekable();
4313        while let Some((_, node)) = children.next() {
4314            if let Some(text) = node.downcast::<Text>() {
4315                if text.is::<CDATASection>() {
4316                    continue;
4317                }
4318                let cdata = text.upcast::<CharacterData>();
4319                let mut length = cdata.Length();
4320                if length == 0 {
4321                    Node::remove(cx, &node, self, SuppressObserver::Unsuppressed);
4322                    continue;
4323                }
4324                while children.peek().is_some_and(|(_, sibling)| {
4325                    sibling.is::<Text>() && !sibling.is::<CDATASection>()
4326                }) {
4327                    let (index, sibling) = children.next().unwrap();
4328                    sibling
4329                        .ranges()
4330                        .drain_to_preceding_text_sibling(&sibling, &node, length);
4331                    self.ranges()
4332                        .move_to_text_child_at(self, index as u32, &node, length);
4333                    let sibling_cdata = sibling.downcast::<CharacterData>().unwrap();
4334                    length += sibling_cdata.Length();
4335                    cdata.append_data(&sibling_cdata.data());
4336                    Node::remove(cx, &sibling, self, SuppressObserver::Unsuppressed);
4337                }
4338            } else {
4339                node.Normalize(cx);
4340            }
4341        }
4342    }
4343
4344    /// <https://dom.spec.whatwg.org/#dom-node-clonenode>
4345    fn CloneNode(&self, cx: &mut JSContext, subtree: bool) -> Fallible<DomRoot<Node>> {
4346        // Step 1. If this is a shadow root, then throw a "NotSupportedError" DOMException.
4347        if self.is::<ShadowRoot>() {
4348            return Err(Error::NotSupported(None));
4349        }
4350
4351        // Step 2. Return the result of cloning a node given this with subtree set to subtree.
4352        let result = Node::clone(
4353            cx,
4354            self,
4355            None,
4356            if subtree {
4357                CloneChildrenFlag::CloneChildren
4358            } else {
4359                CloneChildrenFlag::DoNotCloneChildren
4360            },
4361            None,
4362        );
4363        Ok(result)
4364    }
4365
4366    /// <https://dom.spec.whatwg.org/#dom-node-isequalnode>
4367    fn IsEqualNode(&self, maybe_node: Option<&Node>) -> bool {
4368        fn is_equal_doctype(node: &Node, other: &Node) -> bool {
4369            let doctype = node.downcast::<DocumentType>().unwrap();
4370            let other_doctype = other.downcast::<DocumentType>().unwrap();
4371            (*doctype.name() == *other_doctype.name()) &&
4372                (*doctype.public_id() == *other_doctype.public_id()) &&
4373                (*doctype.system_id() == *other_doctype.system_id())
4374        }
4375        fn is_equal_element(node: &Node, other: &Node) -> bool {
4376            let element = node.downcast::<Element>().unwrap();
4377            let other_element = other.downcast::<Element>().unwrap();
4378            (*element.namespace() == *other_element.namespace()) &&
4379                (*element.prefix() == *other_element.prefix()) &&
4380                (*element.local_name() == *other_element.local_name()) &&
4381                (element.attrs().len() == other_element.attrs().len())
4382        }
4383        fn is_equal_processinginstruction(node: &Node, other: &Node) -> bool {
4384            let pi = node.downcast::<ProcessingInstruction>().unwrap();
4385            let other_pi = other.downcast::<ProcessingInstruction>().unwrap();
4386            (*pi.target() == *other_pi.target()) &&
4387                (*pi.upcast::<CharacterData>().data() ==
4388                    *other_pi.upcast::<CharacterData>().data())
4389        }
4390        fn is_equal_characterdata(node: &Node, other: &Node) -> bool {
4391            let characterdata = node.downcast::<CharacterData>().unwrap();
4392            let other_characterdata = other.downcast::<CharacterData>().unwrap();
4393            *characterdata.data() == *other_characterdata.data()
4394        }
4395        fn is_equal_attr(node: &Node, other: &Node) -> bool {
4396            let attr = node.downcast::<Attr>().unwrap();
4397            let other_attr = other.downcast::<Attr>().unwrap();
4398            (*attr.namespace() == *other_attr.namespace()) &&
4399                (attr.local_name() == other_attr.local_name()) &&
4400                (**attr.value() == **other_attr.value())
4401        }
4402        fn is_equal_element_attrs(node: &Node, other: &Node) -> bool {
4403            let element = node.downcast::<Element>().unwrap();
4404            let other_element = other.downcast::<Element>().unwrap();
4405            assert!(element.attrs().len() == other_element.attrs().len());
4406            element.attrs().iter().all(|attr| {
4407                other_element.attrs().iter().any(|other_attr| {
4408                    (*attr.namespace() == *other_attr.namespace()) &&
4409                        (attr.local_name() == other_attr.local_name()) &&
4410                        (**attr.value() == **other_attr.value())
4411                })
4412            })
4413        }
4414
4415        fn is_equal_node(this: &Node, node: &Node) -> bool {
4416            // Step 2.
4417            if this.NodeType() != node.NodeType() {
4418                return false;
4419            }
4420
4421            match node.type_id() {
4422                // Step 3.
4423                NodeTypeId::DocumentType if !is_equal_doctype(this, node) => return false,
4424                NodeTypeId::Element(..) if !is_equal_element(this, node) => return false,
4425                NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)
4426                    if !is_equal_processinginstruction(this, node) =>
4427                {
4428                    return false;
4429                },
4430                NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) |
4431                NodeTypeId::CharacterData(CharacterDataTypeId::Comment)
4432                    if !is_equal_characterdata(this, node) =>
4433                {
4434                    return false;
4435                },
4436                // Step 4.
4437                NodeTypeId::Element(..) if !is_equal_element_attrs(this, node) => return false,
4438                NodeTypeId::Attr if !is_equal_attr(this, node) => return false,
4439
4440                _ => (),
4441            }
4442
4443            // Step 5.
4444            if this.children_count() != node.children_count() {
4445                return false;
4446            }
4447
4448            // Step 6.
4449            this.children()
4450                .zip(node.children())
4451                .all(|(child, other_child)| is_equal_node(&child, &other_child))
4452        }
4453        match maybe_node {
4454            // Step 1.
4455            None => false,
4456            // Step 2-6.
4457            Some(node) => is_equal_node(self, node),
4458        }
4459    }
4460
4461    /// <https://dom.spec.whatwg.org/#dom-node-issamenode>
4462    fn IsSameNode(&self, other_node: Option<&Node>) -> bool {
4463        match other_node {
4464            Some(node) => self == node,
4465            None => false,
4466        }
4467    }
4468
4469    /// <https://dom.spec.whatwg.org/#dom-node-comparedocumentposition>
4470    fn CompareDocumentPosition(&self, other: &Node) -> u16 {
4471        // step 1.
4472        if self == other {
4473            return 0;
4474        }
4475
4476        // step 2
4477        let mut node1 = Some(other);
4478        let mut node2 = Some(self);
4479
4480        // step 3
4481        let mut attr1: Option<&Attr> = None;
4482        let mut attr2: Option<&Attr> = None;
4483
4484        // step 4: spec says to operate on node1 here,
4485        // node1 is definitely Some(other) going into this step
4486        // The compiler doesn't know the lifetime of attr1.GetOwnerElement
4487        // is guaranteed by the lifetime of attr1, so we hold it explicitly
4488        let attr1owner;
4489        if let Some(a) = other.downcast::<Attr>() {
4490            attr1 = Some(a);
4491            attr1owner = a.GetOwnerElement();
4492            node1 = match attr1owner {
4493                Some(ref e) => Some(e.upcast()),
4494                None => None,
4495            }
4496        }
4497
4498        // step 5.1: spec says to operate on node2 here,
4499        // node2 is definitely just Some(self) going into this step
4500        let attr2owner;
4501        if let Some(a) = self.downcast::<Attr>() {
4502            attr2 = Some(a);
4503            attr2owner = a.GetOwnerElement();
4504            node2 = match attr2owner {
4505                Some(ref e) => Some(e.upcast()),
4506                None => None,
4507            }
4508        }
4509
4510        // Step 5.2
4511        // This substep seems lacking in test coverage.
4512        // We hit this when comparing two attributes that have the
4513        // same owner element.
4514        if let Some(node2) = node2 {
4515            if Some(node2) == node1 {
4516                if let (Some(a1), Some(a2)) = (attr1, attr2) {
4517                    let attrs = node2.downcast::<Element>().unwrap().attrs();
4518                    // go through the attrs in order to see if self
4519                    // or other is first; spec is clear that we
4520                    // want value-equality, not reference-equality
4521                    for attr in attrs.iter() {
4522                        if (*attr.namespace() == *a1.namespace()) &&
4523                            (attr.local_name() == a1.local_name()) &&
4524                            (**attr.value() == **a1.value())
4525                        {
4526                            return NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
4527                                NodeConstants::DOCUMENT_POSITION_PRECEDING;
4528                        }
4529                        if (*attr.namespace() == *a2.namespace()) &&
4530                            (attr.local_name() == a2.local_name()) &&
4531                            (**attr.value() == **a2.value())
4532                        {
4533                            return NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
4534                                NodeConstants::DOCUMENT_POSITION_FOLLOWING;
4535                        }
4536                    }
4537                    // both attrs have node2 as their owner element, so
4538                    // we can't have left the loop without seeing them
4539                    unreachable!();
4540                }
4541            }
4542        }
4543
4544        // Step 6
4545        match (node1, node2) {
4546            (None, _) => {
4547                // node1 is null
4548                NodeConstants::DOCUMENT_POSITION_FOLLOWING +
4549                    NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
4550                    NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
4551            },
4552            (_, None) => {
4553                // node2 is null
4554                NodeConstants::DOCUMENT_POSITION_PRECEDING +
4555                    NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
4556                    NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
4557            },
4558            (Some(node1), Some(node2)) => {
4559                // still step 6, testing if node1 and 2 share a root
4560                let mut self_and_ancestors = node2
4561                    .inclusive_ancestors(ShadowIncluding::No)
4562                    .collect::<SmallVec<[_; 20]>>();
4563                let mut other_and_ancestors = node1
4564                    .inclusive_ancestors(ShadowIncluding::No)
4565                    .collect::<SmallVec<[_; 20]>>();
4566
4567                if self_and_ancestors.last() != other_and_ancestors.last() {
4568                    let random = as_uintptr(self_and_ancestors.last().unwrap()) <
4569                        as_uintptr(other_and_ancestors.last().unwrap());
4570                    let random = if random {
4571                        NodeConstants::DOCUMENT_POSITION_FOLLOWING
4572                    } else {
4573                        NodeConstants::DOCUMENT_POSITION_PRECEDING
4574                    };
4575
4576                    // Disconnected.
4577                    return random +
4578                        NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
4579                        NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
4580                }
4581                // steps 7-10
4582                let mut parent = self_and_ancestors.pop().unwrap();
4583                other_and_ancestors.pop().unwrap();
4584
4585                let mut current_position =
4586                    cmp::min(self_and_ancestors.len(), other_and_ancestors.len());
4587
4588                while current_position > 0 {
4589                    current_position -= 1;
4590                    let child_1 = self_and_ancestors.pop().unwrap();
4591                    let child_2 = other_and_ancestors.pop().unwrap();
4592
4593                    if child_1 != child_2 {
4594                        for child in parent.children() {
4595                            if child == child_1 {
4596                                // `other` is following `self`.
4597                                return NodeConstants::DOCUMENT_POSITION_FOLLOWING;
4598                            }
4599                            if child == child_2 {
4600                                // `other` is preceding `self`.
4601                                return NodeConstants::DOCUMENT_POSITION_PRECEDING;
4602                            }
4603                        }
4604                    }
4605
4606                    parent = child_1;
4607                }
4608
4609                // We hit the end of one of the parent chains, so one node needs to be
4610                // contained in the other.
4611                //
4612                // If we're the container, return that `other` is contained by us.
4613                if self_and_ancestors.len() < other_and_ancestors.len() {
4614                    NodeConstants::DOCUMENT_POSITION_FOLLOWING +
4615                        NodeConstants::DOCUMENT_POSITION_CONTAINED_BY
4616                } else {
4617                    NodeConstants::DOCUMENT_POSITION_PRECEDING +
4618                        NodeConstants::DOCUMENT_POSITION_CONTAINS
4619                }
4620            },
4621        }
4622    }
4623
4624    /// <https://dom.spec.whatwg.org/#dom-node-contains>
4625    fn Contains(&self, maybe_other: Option<&Node>) -> bool {
4626        match maybe_other {
4627            None => false,
4628            Some(other) => self.is_inclusive_ancestor_of(other),
4629        }
4630    }
4631
4632    /// <https://dom.spec.whatwg.org/#dom-node-lookupprefix>
4633    fn LookupPrefix(&self, namespace: Option<DOMString>) -> Option<DOMString> {
4634        let namespace = namespace_from_domstring(namespace);
4635
4636        // Step 1.
4637        if namespace == ns!() {
4638            return None;
4639        }
4640
4641        // Step 2.
4642        match self.type_id() {
4643            NodeTypeId::Element(..) => self.downcast::<Element>().unwrap().lookup_prefix(namespace),
4644            NodeTypeId::Document(_) => self
4645                .downcast::<Document>()
4646                .unwrap()
4647                .GetDocumentElement()
4648                .and_then(|element| element.lookup_prefix(namespace)),
4649            NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => None,
4650            NodeTypeId::Attr => self
4651                .downcast::<Attr>()
4652                .unwrap()
4653                .GetOwnerElement()
4654                .and_then(|element| element.lookup_prefix(namespace)),
4655            _ => self
4656                .GetParentElement()
4657                .and_then(|element| element.lookup_prefix(namespace)),
4658        }
4659    }
4660
4661    /// <https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri>
4662    fn LookupNamespaceURI(&self, prefix: Option<DOMString>) -> Option<DOMString> {
4663        // Step 1. If prefix is the empty string, then set it to null.
4664        let prefix = prefix.filter(|prefix| !prefix.is_empty());
4665
4666        // Step 2. Return the result of running locate a namespace for this using prefix.
4667        Node::namespace_to_string(Node::locate_namespace(self, prefix))
4668    }
4669
4670    /// <https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace>
4671    fn IsDefaultNamespace(&self, namespace: Option<DOMString>) -> bool {
4672        // Step 1.
4673        let namespace = namespace_from_domstring(namespace);
4674        // Steps 2 and 3.
4675        Node::locate_namespace(self, None) == namespace
4676    }
4677}
4678
4679pub(crate) trait NodeTraits {
4680    /// Get the [`Document`] that owns this node. Note that this may differ from the
4681    /// [`Document`] that the node was created in if it was adopted by a different
4682    /// [`Document`] (the owner).
4683    fn owner_document(&self) -> DomRoot<Document>;
4684    /// Get the [`Window`] of the [`Document`] that owns this node. Note that this may
4685    /// differ from the [`Document`] that the node was created in if it was adopted by a
4686    /// different [`Document`] (the owner).
4687    fn owner_window(&self) -> DomRoot<Window>;
4688    /// Get the [`GlobalScope`] of the [`Document`] that owns this node. Note that this may
4689    /// differ from the [`GlobalScope`] that the node was created in if it was adopted by a
4690    /// different [`Document`] (the owner).
4691    fn owner_global(&self) -> DomRoot<GlobalScope>;
4692    /// If this [`Node`] is contained in a [`ShadowRoot`] return it, otherwise `None`.
4693    fn containing_shadow_root(&self) -> Option<DomRoot<ShadowRoot>>;
4694    /// Get the stylesheet owner for this node: either the [`Document`] or the [`ShadowRoot`]
4695    /// of the node.
4696    fn stylesheet_list_owner(&self) -> StyleSheetListOwner;
4697}
4698
4699impl<T: DerivedFrom<Node> + DomObject> NodeTraits for T {
4700    fn owner_document(&self) -> DomRoot<Document> {
4701        self.upcast().owner_doc()
4702    }
4703
4704    fn owner_window(&self) -> DomRoot<Window> {
4705        DomRoot::from_ref(self.owner_document().window())
4706    }
4707
4708    fn owner_global(&self) -> DomRoot<GlobalScope> {
4709        DomRoot::from_ref(self.owner_window().upcast())
4710    }
4711
4712    fn containing_shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
4713        Node::containing_shadow_root(self.upcast())
4714    }
4715
4716    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
4717    fn stylesheet_list_owner(&self) -> StyleSheetListOwner {
4718        self.containing_shadow_root()
4719            .map(|shadow_root| StyleSheetListOwner::ShadowRoot(Dom::from_ref(&*shadow_root)))
4720            .unwrap_or_else(|| {
4721                StyleSheetListOwner::Document(Dom::from_ref(&*self.owner_document()))
4722            })
4723    }
4724}
4725
4726impl VirtualMethods for Node {
4727    fn super_type(&self) -> Option<&dyn VirtualMethods> {
4728        Some(self.upcast::<EventTarget>() as &dyn VirtualMethods)
4729    }
4730
4731    fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
4732        if let Some(s) = self.super_type() {
4733            s.children_changed(cx, mutation);
4734        }
4735
4736        if let Some(data) = self.rare_data().as_ref() {
4737            if let Some(list) = data.child_list.get() {
4738                list.as_children_list().children_changed(mutation);
4739            }
4740        }
4741
4742        self.owner_doc().content_and_heritage_changed(self);
4743    }
4744
4745    // This handles the ranges mentioned in steps 2-3 when removing a node.
4746    /// <https://dom.spec.whatwg.org/#concept-node-remove>
4747    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
4748        self.super_type().unwrap().unbind_from_tree(context, can_gc);
4749
4750        // Ranges should only drain to the parent from inclusive non-shadow
4751        // including descendants. If we're in a shadow tree at this point then the
4752        // unbind operation happened further up in the tree and we should not
4753        // drain any ranges.
4754        if !self.is_in_a_shadow_tree() && !self.ranges_is_empty() {
4755            self.ranges()
4756                .drain_to_parent(context.parent, context.index(), self);
4757        }
4758    }
4759
4760    fn moving_steps(&self, context: &MoveContext, can_gc: CanGc) {
4761        if let Some(super_type) = self.super_type() {
4762            super_type.moving_steps(context, can_gc);
4763        }
4764
4765        // Ranges should only drain to the parent from inclusive non-shadow
4766        // including descendants. If we're in a shadow tree at this point then the
4767        // unbind operation happened further up in the tree and we should not
4768        // drain any ranges.
4769        if let Some(old_parent) = context.old_parent {
4770            if !self.is_in_a_shadow_tree() && !self.ranges_is_empty() {
4771                self.ranges()
4772                    .drain_to_parent(old_parent, context.index(), self);
4773            }
4774        }
4775
4776        self.owner_doc().content_and_heritage_changed(self);
4777    }
4778
4779    fn handle_event(&self, event: &Event, can_gc: CanGc) {
4780        if event.DefaultPrevented() || event.flags().contains(EventFlags::Handled) {
4781            return;
4782        }
4783
4784        if let Some(event) = event.downcast::<KeyboardEvent>() {
4785            self.owner_document()
4786                .event_handler()
4787                .run_default_keyboard_event_handler(self, event, can_gc);
4788        }
4789    }
4790}
4791
4792/// A summary of the changes that happened to a node.
4793#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
4794pub(crate) enum NodeDamage {
4795    /// The node's `style` attribute changed.
4796    Style,
4797    /// The node's content or heritage changed, such as the addition or removal of
4798    /// children.
4799    ContentOrHeritage,
4800    /// Other parts of a node changed; attributes, text content, etc.
4801    Other,
4802}
4803
4804pub(crate) enum ChildrenMutation<'a> {
4805    Append {
4806        prev: &'a Node,
4807        added: &'a [&'a Node],
4808    },
4809    Insert {
4810        prev: &'a Node,
4811        added: &'a [&'a Node],
4812        next: &'a Node,
4813    },
4814    Prepend {
4815        added: &'a [&'a Node],
4816        next: &'a Node,
4817    },
4818    Replace {
4819        prev: Option<&'a Node>,
4820        removed: &'a Node,
4821        added: &'a [&'a Node],
4822        next: Option<&'a Node>,
4823    },
4824    ReplaceAll {
4825        removed: &'a [&'a Node],
4826        added: &'a [&'a Node],
4827    },
4828    /// Mutation for when a Text node's data is modified.
4829    /// This doesn't change the structure of the list, which is what the other
4830    /// variants' fields are stored for at the moment, so this can just have no
4831    /// fields.
4832    ChangeText,
4833}
4834
4835impl<'a> ChildrenMutation<'a> {
4836    fn insert(
4837        prev: Option<&'a Node>,
4838        added: &'a [&'a Node],
4839        next: Option<&'a Node>,
4840    ) -> ChildrenMutation<'a> {
4841        match (prev, next) {
4842            (None, None) => ChildrenMutation::ReplaceAll {
4843                removed: &[],
4844                added,
4845            },
4846            (Some(prev), None) => ChildrenMutation::Append { prev, added },
4847            (None, Some(next)) => ChildrenMutation::Prepend { added, next },
4848            (Some(prev), Some(next)) => ChildrenMutation::Insert { prev, added, next },
4849        }
4850    }
4851
4852    fn replace(
4853        prev: Option<&'a Node>,
4854        removed: &'a Option<&'a Node>,
4855        added: &'a [&'a Node],
4856        next: Option<&'a Node>,
4857    ) -> ChildrenMutation<'a> {
4858        if let Some(ref removed) = *removed {
4859            if let (None, None) = (prev, next) {
4860                ChildrenMutation::ReplaceAll {
4861                    removed: from_ref(removed),
4862                    added,
4863                }
4864            } else {
4865                ChildrenMutation::Replace {
4866                    prev,
4867                    removed,
4868                    added,
4869                    next,
4870                }
4871            }
4872        } else {
4873            ChildrenMutation::insert(prev, added, next)
4874        }
4875    }
4876
4877    fn replace_all(removed: &'a [&'a Node], added: &'a [&'a Node]) -> ChildrenMutation<'a> {
4878        ChildrenMutation::ReplaceAll { removed, added }
4879    }
4880
4881    /// Get the child that follows the added or removed children.
4882    /// Currently only used when this mutation might force us to
4883    /// restyle later children (see HAS_SLOW_SELECTOR_LATER_SIBLINGS and
4884    /// Element's implementation of VirtualMethods::children_changed).
4885    pub(crate) fn next_child(&self) -> Option<&Node> {
4886        match *self {
4887            ChildrenMutation::Append { .. } => None,
4888            ChildrenMutation::Insert { next, .. } => Some(next),
4889            ChildrenMutation::Prepend { next, .. } => Some(next),
4890            ChildrenMutation::Replace { next, .. } => next,
4891            ChildrenMutation::ReplaceAll { .. } => None,
4892            ChildrenMutation::ChangeText => None,
4893        }
4894    }
4895
4896    /// If nodes were added or removed at the start or end of a container, return any
4897    /// previously-existing child whose ":first-child" or ":last-child" status *may* have changed.
4898    ///
4899    /// NOTE: This does not check whether the inserted/removed nodes were elements, so in some
4900    /// cases it will return a false positive.  This doesn't matter for correctness, because at
4901    /// worst the returned element will be restyled unnecessarily.
4902    pub(crate) fn modified_edge_element(&self) -> Option<DomRoot<Node>> {
4903        match *self {
4904            // Add/remove at start of container: Return the first following element.
4905            ChildrenMutation::Prepend { next, .. } |
4906            ChildrenMutation::Replace {
4907                prev: None,
4908                next: Some(next),
4909                ..
4910            } => next
4911                .inclusively_following_siblings()
4912                .find(|node| node.is::<Element>()),
4913            // Add/remove at end of container: Return the last preceding element.
4914            ChildrenMutation::Append { prev, .. } |
4915            ChildrenMutation::Replace {
4916                prev: Some(prev),
4917                next: None,
4918                ..
4919            } => prev
4920                .inclusively_preceding_siblings()
4921                .find(|node| node.is::<Element>()),
4922            // Insert or replace in the middle:
4923            ChildrenMutation::Insert { prev, next, .. } |
4924            ChildrenMutation::Replace {
4925                prev: Some(prev),
4926                next: Some(next),
4927                ..
4928            } => {
4929                if prev
4930                    .inclusively_preceding_siblings()
4931                    .all(|node| !node.is::<Element>())
4932                {
4933                    // Before the first element: Return the first following element.
4934                    next.inclusively_following_siblings()
4935                        .find(|node| node.is::<Element>())
4936                } else if next
4937                    .inclusively_following_siblings()
4938                    .all(|node| !node.is::<Element>())
4939                {
4940                    // After the last element: Return the last preceding element.
4941                    prev.inclusively_preceding_siblings()
4942                        .find(|node| node.is::<Element>())
4943                } else {
4944                    None
4945                }
4946            },
4947
4948            ChildrenMutation::Replace {
4949                prev: None,
4950                next: None,
4951                ..
4952            } => unreachable!(),
4953            ChildrenMutation::ReplaceAll { .. } => None,
4954            ChildrenMutation::ChangeText => None,
4955        }
4956    }
4957}
4958
4959/// The context of the binding to tree of a node.
4960pub(crate) struct BindContext<'a> {
4961    /// The parent of the inclusive ancestor that was inserted.
4962    pub(crate) parent: &'a Node,
4963
4964    /// Whether the tree is connected.
4965    ///
4966    /// <https://dom.spec.whatwg.org/#connected>
4967    pub(crate) tree_connected: bool,
4968
4969    /// Whether the tree's root is a document.
4970    ///
4971    /// <https://dom.spec.whatwg.org/#in-a-document-tree>
4972    pub(crate) tree_is_in_a_document_tree: bool,
4973
4974    /// Whether the tree's root is a shadow root
4975    pub(crate) tree_is_in_a_shadow_tree: bool,
4976
4977    /// Whether the root of the subtree that is being bound to the parent is a shadow root.
4978    ///
4979    /// This implies that all elements whose "bind_to_tree" method are called were already
4980    /// in a shadow tree beforehand.
4981    pub(crate) is_shadow_tree: IsShadowTree,
4982}
4983
4984#[derive(Debug, Eq, PartialEq)]
4985pub(crate) enum IsShadowTree {
4986    Yes,
4987    No,
4988}
4989
4990impl<'a> BindContext<'a> {
4991    /// Create a new `BindContext` value.
4992    pub(crate) fn new(parent: &'a Node, is_shadow_tree: IsShadowTree) -> Self {
4993        BindContext {
4994            parent,
4995            tree_connected: parent.is_connected(),
4996            tree_is_in_a_document_tree: parent.is_in_a_document_tree(),
4997            tree_is_in_a_shadow_tree: parent.is_in_a_shadow_tree(),
4998            is_shadow_tree,
4999        }
5000    }
5001
5002    /// Return true iff the tree is inside either a document- or a shadow tree.
5003    pub(crate) fn is_in_tree(&self) -> bool {
5004        self.tree_is_in_a_document_tree || self.tree_is_in_a_shadow_tree
5005    }
5006}
5007
5008/// The context of the unbinding from a tree of a node when one of its
5009/// inclusive ancestors is removed.
5010pub(crate) struct UnbindContext<'a> {
5011    /// The index of the inclusive ancestor that was removed.
5012    index: Cell<Option<u32>>,
5013    /// The parent of the inclusive ancestor that was removed.
5014    pub(crate) parent: &'a Node,
5015    /// The previous sibling of the inclusive ancestor that was removed.
5016    prev_sibling: Option<&'a Node>,
5017    /// The next sibling of the inclusive ancestor that was removed.
5018    pub(crate) next_sibling: Option<&'a Node>,
5019
5020    /// Whether the tree is connected.
5021    ///
5022    /// <https://dom.spec.whatwg.org/#connected>
5023    pub(crate) tree_connected: bool,
5024
5025    /// Whether the tree's root is a document.
5026    ///
5027    /// <https://dom.spec.whatwg.org/#in-a-document-tree>
5028    pub(crate) tree_is_in_a_document_tree: bool,
5029
5030    /// Whether the tree's root is a shadow root
5031    pub(crate) tree_is_in_a_shadow_tree: bool,
5032}
5033
5034impl<'a> UnbindContext<'a> {
5035    /// Create a new `UnbindContext` value.
5036    pub(crate) fn new(
5037        parent: &'a Node,
5038        prev_sibling: Option<&'a Node>,
5039        next_sibling: Option<&'a Node>,
5040        cached_index: Option<u32>,
5041    ) -> Self {
5042        UnbindContext {
5043            index: Cell::new(cached_index),
5044            parent,
5045            prev_sibling,
5046            next_sibling,
5047            tree_connected: parent.is_connected(),
5048            tree_is_in_a_document_tree: parent.is_in_a_document_tree(),
5049            tree_is_in_a_shadow_tree: parent.is_in_a_shadow_tree(),
5050        }
5051    }
5052
5053    /// The index of the inclusive ancestor that was removed from the tree.
5054    pub(crate) fn index(&self) -> u32 {
5055        if let Some(index) = self.index.get() {
5056            return index;
5057        }
5058        let index = self.prev_sibling.map_or(0, |sibling| sibling.index() + 1);
5059        self.index.set(Some(index));
5060        index
5061    }
5062}
5063
5064/// The context of the moving from a tree of a node when one of its
5065/// inclusive ancestors is moved.
5066pub(crate) struct MoveContext<'a> {
5067    /// The index of the inclusive ancestor that was moved.
5068    index: Cell<Option<u32>>,
5069    /// The old parent, if any, of the inclusive ancestor that was moved.
5070    pub(crate) old_parent: Option<&'a Node>,
5071    /// The previous sibling of the inclusive ancestor that was moved.
5072    prev_sibling: Option<&'a Node>,
5073    /// The next sibling of the inclusive ancestor that was moved.
5074    pub(crate) next_sibling: Option<&'a Node>,
5075}
5076
5077impl<'a> MoveContext<'a> {
5078    /// Create a new `MoveContext` value.
5079    pub(crate) fn new(
5080        old_parent: Option<&'a Node>,
5081        prev_sibling: Option<&'a Node>,
5082        next_sibling: Option<&'a Node>,
5083        cached_index: Option<u32>,
5084    ) -> Self {
5085        MoveContext {
5086            index: Cell::new(cached_index),
5087            old_parent,
5088            prev_sibling,
5089            next_sibling,
5090        }
5091    }
5092
5093    /// The index of the inclusive ancestor that was moved from the tree.
5094    pub(crate) fn index(&self) -> u32 {
5095        if let Some(index) = self.index.get() {
5096            return index;
5097        }
5098        let index = self.prev_sibling.map_or(0, |sibling| sibling.index() + 1);
5099        self.index.set(Some(index));
5100        index
5101    }
5102}
5103
5104/// A node's unique ID, for devtools.
5105pub(crate) struct UniqueId {
5106    cell: UnsafeCell<Option<Box<Uuid>>>,
5107}
5108
5109unsafe_no_jsmanaged_fields!(UniqueId);
5110
5111impl MallocSizeOf for UniqueId {
5112    #[expect(unsafe_code)]
5113    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
5114        if let Some(uuid) = unsafe { &*self.cell.get() } {
5115            unsafe { ops.malloc_size_of(&**uuid) }
5116        } else {
5117            0
5118        }
5119    }
5120}
5121
5122impl UniqueId {
5123    /// Create a new `UniqueId` value. The underlying `Uuid` is lazily created.
5124    fn new() -> UniqueId {
5125        UniqueId {
5126            cell: UnsafeCell::new(None),
5127        }
5128    }
5129
5130    /// The Uuid of that unique ID.
5131    #[expect(unsafe_code)]
5132    fn borrow(&self) -> &Uuid {
5133        unsafe {
5134            let ptr = self.cell.get();
5135            if (*ptr).is_none() {
5136                *ptr = Some(Box::new(Uuid::new_v4()));
5137            }
5138            (*ptr).as_ref().unwrap()
5139        }
5140    }
5141}
5142
5143pub(crate) struct NodeTypeIdWrapper(pub(crate) NodeTypeId);
5144
5145impl From<NodeTypeIdWrapper> for LayoutNodeType {
5146    #[inline(always)]
5147    fn from(node_type: NodeTypeIdWrapper) -> LayoutNodeType {
5148        match node_type.0 {
5149            NodeTypeId::Element(e) => LayoutNodeType::Element(ElementTypeIdWrapper(e).into()),
5150            NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
5151            x => unreachable!("Layout should not traverse nodes of type {:?}", x),
5152        }
5153    }
5154}
5155
5156struct ElementTypeIdWrapper(ElementTypeId);
5157
5158impl From<ElementTypeIdWrapper> for LayoutElementType {
5159    #[inline(always)]
5160    fn from(element_type: ElementTypeIdWrapper) -> LayoutElementType {
5161        match element_type.0 {
5162            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
5163                LayoutElementType::HTMLBodyElement
5164            },
5165            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
5166                LayoutElementType::HTMLBRElement
5167            },
5168            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
5169                LayoutElementType::HTMLCanvasElement
5170            },
5171            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
5172                LayoutElementType::HTMLHtmlElement
5173            },
5174            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
5175                LayoutElementType::HTMLIFrameElement
5176            },
5177            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
5178                LayoutElementType::HTMLImageElement
5179            },
5180            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
5181                LayoutElementType::HTMLMediaElement
5182            },
5183            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
5184                LayoutElementType::HTMLInputElement
5185            },
5186            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement) => {
5187                LayoutElementType::HTMLOptGroupElement
5188            },
5189            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement) => {
5190                LayoutElementType::HTMLOptionElement
5191            },
5192            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
5193                LayoutElementType::HTMLObjectElement
5194            },
5195            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
5196                LayoutElementType::HTMLParagraphElement
5197            },
5198            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLPreElement) => {
5199                LayoutElementType::HTMLPreElement
5200            },
5201            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement) => {
5202                LayoutElementType::HTMLSelectElement
5203            },
5204            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
5205                LayoutElementType::HTMLTableCellElement
5206            },
5207            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
5208                LayoutElementType::HTMLTableColElement
5209            },
5210            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
5211                LayoutElementType::HTMLTableElement
5212            },
5213            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
5214                LayoutElementType::HTMLTableRowElement
5215            },
5216            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
5217                LayoutElementType::HTMLTableSectionElement
5218            },
5219            ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
5220                LayoutElementType::HTMLTextAreaElement
5221            },
5222            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
5223                SVGGraphicsElementTypeId::SVGImageElement,
5224            )) => LayoutElementType::SVGImageElement,
5225            ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
5226                SVGGraphicsElementTypeId::SVGSVGElement,
5227            )) => LayoutElementType::SVGSVGElement,
5228            _ => LayoutElementType::Element,
5229        }
5230    }
5231}
5232
5233/// Helper trait to insert an element into vector whose elements
5234/// are maintained in tree order
5235pub(crate) trait VecPreOrderInsertionHelper<T> {
5236    fn insert_pre_order(&mut self, elem: &T, tree_root: &Node);
5237}
5238
5239impl<T> VecPreOrderInsertionHelper<T> for Vec<Dom<T>>
5240where
5241    T: DerivedFrom<Node> + DomObject,
5242{
5243    /// This algorithm relies on the following assumptions:
5244    /// * any elements inserted in this vector share the same tree root
5245    /// * any time an element is removed from the tree root, it is also removed from this array
5246    /// * any time an element is moved within the tree, it is removed from this array and re-inserted
5247    ///
5248    /// Under these assumptions, an element's tree-order position in this array can be determined by
5249    /// performing a [preorder traversal](https://dom.spec.whatwg.org/#concept-tree-order) of the tree root's children,
5250    /// and increasing the destination index in the array every time a node in the array is encountered during
5251    /// the traversal.
5252    fn insert_pre_order(&mut self, elem: &T, tree_root: &Node) {
5253        if self.is_empty() {
5254            self.push(Dom::from_ref(elem));
5255            return;
5256        }
5257
5258        let elem_node = elem.upcast::<Node>();
5259        let mut head: usize = 0;
5260        for node in tree_root.traverse_preorder(ShadowIncluding::No) {
5261            let head_node = DomRoot::upcast::<Node>(DomRoot::from_ref(&*self[head]));
5262            if head_node == node {
5263                head += 1;
5264            }
5265            if elem_node == &*node || head == self.len() {
5266                break;
5267            }
5268        }
5269        self.insert(head, Dom::from_ref(elem));
5270    }
5271}