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