Skip to main content

script/dom/node/
node.rs

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