Skip to main content

script/dom/node/
node.rs

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