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