Skip to main content

script/dom/node/
node.rs

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