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