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