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