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