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