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