2020-01-18 09:38:21 +01:00
/*
2022-02-05 13:17:01 +01:00
* Copyright ( c ) 2018 - 2022 , Andreas Kling < kling @ serenityos . org >
2022-03-22 12:39:16 +00:00
* Copyright ( c ) 2021 - 2022 , Linus Groh < linusg @ serenityos . org >
2021-05-07 00:52:23 +01:00
* Copyright ( c ) 2021 , Luke Wilde < lukew @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2021-08-30 15:52:08 +01:00
# include <AK/IDAllocator.h>
2019-09-29 16:22:15 +02:00
# include <AK/StringBuilder.h>
2020-03-18 15:22:31 +01:00
# include <LibJS/AST.h>
2021-06-27 21:48:34 +02:00
# include <LibJS/Runtime/FunctionObject.h>
2020-03-21 18:17:18 +01:00
# include <LibWeb/Bindings/EventWrapper.h>
2022-07-11 16:39:14 +01:00
# include <LibWeb/Bindings/MainThreadVM.h>
2020-03-18 15:22:31 +01:00
# include <LibWeb/Bindings/NodeWrapper.h>
2020-06-20 22:09:38 +02:00
# include <LibWeb/Bindings/NodeWrapperFactory.h>
2021-04-06 19:34:49 +01:00
# include <LibWeb/DOM/Comment.h>
# include <LibWeb/DOM/DocumentType.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Element.h>
2021-07-05 05:28:29 +01:00
# include <LibWeb/DOM/ElementFactory.h>
2020-03-21 18:17:18 +01:00
# include <LibWeb/DOM/Event.h>
2020-09-06 14:28:41 +02:00
# include <LibWeb/DOM/EventDispatcher.h>
2022-02-16 20:43:24 +01:00
# include <LibWeb/DOM/IDLEventListener.h>
2021-10-02 20:37:45 +01:00
# include <LibWeb/DOM/LiveNodeList.h>
2022-07-11 16:39:14 +01:00
# include <LibWeb/DOM/MutationType.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Node.h>
2021-04-06 19:34:49 +01:00
# include <LibWeb/DOM/ProcessingInstruction.h>
2020-11-21 18:32:39 +00:00
# include <LibWeb/DOM/ShadowRoot.h>
2022-07-11 16:39:14 +01:00
# include <LibWeb/DOM/StaticNodeList.h>
2021-11-24 19:15:04 +03:00
# include <LibWeb/HTML/BrowsingContextContainer.h>
2020-08-25 22:18:32 +04:30
# include <LibWeb/HTML/HTMLAnchorElement.h>
2021-09-25 23:15:48 +02:00
# include <LibWeb/HTML/Parser/HTMLParser.h>
2021-09-08 11:27:46 +02:00
# include <LibWeb/Layout/InitialContainingBlock.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/Node.h>
# include <LibWeb/Layout/TextNode.h>
2021-04-14 01:25:10 +02:00
# include <LibWeb/Origin.h>
2019-06-15 18:55:47 +02:00
2020-07-26 19:37:56 +02:00
namespace Web : : DOM {
2020-03-07 10:27:02 +01:00
2021-08-30 15:52:08 +01:00
static IDAllocator s_node_id_allocator ;
static HashMap < i32 , Node * > s_node_directory ;
static i32 allocate_node_id ( Node * node )
{
i32 id = s_node_id_allocator . allocate ( ) ;
s_node_directory . set ( id , node ) ;
return id ;
}
static void deallocate_node_id ( i32 node_id )
{
if ( ! s_node_directory . remove ( node_id ) )
VERIFY_NOT_REACHED ( ) ;
s_node_id_allocator . deallocate ( node_id ) ;
}
Node * Node : : from_id ( i32 node_id )
{
return s_node_directory . get ( node_id ) . value_or ( nullptr ) ;
}
2019-09-29 11:43:07 +02:00
Node : : Node ( Document & document , NodeType type )
2021-10-14 18:03:08 +01:00
: EventTarget ( )
2020-09-20 19:22:44 +02:00
, m_document ( & document )
2019-09-29 11:43:07 +02:00
, m_type ( type )
2021-08-30 15:52:08 +01:00
, m_id ( allocate_node_id ( this ) )
2019-06-15 18:55:47 +02:00
{
2020-10-22 23:38:14 +02:00
if ( ! is_document ( ) )
m_document - > ref_from_node ( { } ) ;
2019-06-15 18:55:47 +02:00
}
Node : : ~ Node ( )
{
2021-02-23 20:42:32 +01:00
VERIFY ( m_deletion_has_begun ) ;
2020-03-25 18:48:32 +01:00
if ( layout_node ( ) & & layout_node ( ) - > parent ( ) )
layout_node ( ) - > parent ( ) - > remove_child ( * layout_node ( ) ) ;
2020-10-22 23:38:14 +02:00
if ( ! is_document ( ) )
m_document - > unref_from_node ( { } ) ;
2021-08-30 15:52:08 +01:00
deallocate_node_id ( m_id ) ;
2019-06-15 18:55:47 +02:00
}
2019-09-25 12:17:29 +03:00
2022-04-12 13:20:13 -03:00
// https://dom.spec.whatwg.org/#dom-node-baseuri
String Node : : base_uri ( ) const
{
2022-06-19 16:02:48 +01:00
// Return this’ s node document’ s document base URL, serialized.
return document ( ) . base_url ( ) . to_string ( ) ;
2022-04-12 13:20:13 -03:00
}
2020-07-28 18:20:36 +02:00
const HTML : : HTMLAnchorElement * Node : : enclosing_link_element ( ) const
2019-09-29 11:59:38 +02:00
{
2019-10-19 21:21:29 +02:00
for ( auto * node = this ; node ; node = node - > parent ( ) ) {
2022-03-02 15:15:39 +01:00
if ( ! is < HTML : : HTMLAnchorElement > ( * node ) )
continue ;
auto const & anchor_element = static_cast < HTML : : HTMLAnchorElement const & > ( * node ) ;
if ( anchor_element . has_attribute ( HTML : : AttributeNames : : href ) )
return & anchor_element ;
2019-10-19 21:21:29 +02:00
}
return nullptr ;
2019-09-29 11:59:38 +02:00
}
2019-09-29 12:24:36 +02:00
2020-07-28 18:20:36 +02:00
const HTML : : HTMLElement * Node : : enclosing_html_element ( ) const
2019-09-29 12:24:36 +02:00
{
2020-07-28 18:20:36 +02:00
return first_ancestor_of_type < HTML : : HTMLElement > ( ) ;
2019-09-29 12:24:36 +02:00
}
2019-09-29 16:22:15 +02:00
2022-04-01 20:58:27 +03:00
const HTML : : HTMLElement * Node : : enclosing_html_element_with_attribute ( FlyString const & attribute ) const
2021-03-30 12:06:06 -04:00
{
for ( auto * node = this ; node ; node = node - > parent ( ) ) {
2021-06-24 19:53:42 +02:00
if ( is < HTML : : HTMLElement > ( * node ) & & verify_cast < HTML : : HTMLElement > ( * node ) . has_attribute ( attribute ) )
return verify_cast < HTML : : HTMLElement > ( node ) ;
2021-03-30 12:06:06 -04:00
}
return nullptr ;
}
2021-09-06 00:21:59 +01:00
// https://dom.spec.whatwg.org/#concept-descendant-text-content
String Node : : descendant_text_content ( ) const
2019-09-29 16:22:15 +02:00
{
StringBuilder builder ;
2021-09-06 00:21:59 +01:00
for_each_in_subtree_of_type < Text > ( [ & ] ( auto & text_node ) {
builder . append ( text_node . data ( ) ) ;
return IterationDecision : : Continue ;
} ) ;
2019-09-29 16:22:15 +02:00
return builder . to_string ( ) ;
}
2019-10-06 19:54:50 +02:00
2021-09-06 00:21:59 +01:00
// https://dom.spec.whatwg.org/#dom-node-textcontent
String Node : : text_content ( ) const
2020-08-17 12:36:00 -04:00
{
2022-04-20 00:57:06 +02:00
// The textContent getter steps are to return the following, switching on the interface this implements:
// If DocumentFragment or Element, return the descendant text content of this.
2021-09-06 00:21:59 +01:00
if ( is < DocumentFragment > ( this ) | | is < Element > ( this ) )
return descendant_text_content ( ) ;
else if ( is < CharacterData > ( this ) )
2022-04-20 00:57:06 +02:00
// If CharacterData, return this’ s data.
2021-09-06 00:21:59 +01:00
return verify_cast < CharacterData > ( this ) - > data ( ) ;
2022-04-20 00:57:06 +02:00
// FIXME: If this is an Attr node, return this's value.
2021-09-06 00:21:59 +01:00
2022-04-20 00:57:06 +02:00
// Otherwise, return null
2021-09-06 00:21:59 +01:00
return { } ;
}
// https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent%E2%91%A0
void Node : : set_text_content ( String const & content )
{
2022-04-20 00:57:06 +02:00
// The textContent setter steps are to, if the given value is null, act as if it was the empty string instead,
// and then do as described below, switching on the interface this implements:
// If DocumentFragment or Element, string replace all with the given value within this.
2021-09-06 00:21:59 +01:00
if ( is < DocumentFragment > ( this ) | | is < Element > ( this ) ) {
string_replace_all ( content ) ;
} else if ( is < CharacterData > ( this ) ) {
2022-04-20 00:57:06 +02:00
// If CharacterData, replace data with node this, offset 0, count this’ s length, and data the given value.
2021-09-06 00:21:59 +01:00
auto * character_data_node = verify_cast < CharacterData > ( this ) ;
character_data_node - > set_data ( content ) ;
2022-04-20 00:57:06 +02:00
// FIXME: CharacterData::set_data is not spec compliant. Make this match the spec when set_data becomes spec compliant.
// Do note that this will make this function able to throw an exception.
} else {
// FIXME: If this is an Attr node, set an existing attribute value with this and the given value.
2021-09-06 00:21:59 +01:00
return ;
2020-08-17 12:36:00 -04:00
}
2022-04-20 00:57:06 +02:00
// Otherwise, do nothing.
2020-08-17 12:36:00 -04:00
set_needs_style_update ( true ) ;
}
2022-02-18 22:11:43 +00:00
// https://dom.spec.whatwg.org/#dom-node-nodevalue
String Node : : node_value ( ) const
{
2022-04-20 00:57:06 +02:00
// The nodeValue getter steps are to return the following, switching on the interface this implements:
// If Attr, return this’ s value.
2022-02-18 22:11:43 +00:00
if ( is < Attribute > ( this ) ) {
return verify_cast < Attribute > ( this ) - > value ( ) ;
}
2022-04-20 00:57:06 +02:00
// If CharacterData, return this’ s data.
2022-02-18 22:11:43 +00:00
if ( is < CharacterData > ( this ) ) {
return verify_cast < CharacterData > ( this ) - > data ( ) ;
}
2022-04-20 00:57:06 +02:00
// Otherwise, return null.
2022-02-18 22:11:43 +00:00
return { } ;
}
// https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue%E2%91%A0
2022-04-01 20:58:27 +03:00
void Node : : set_node_value ( String const & value )
2022-02-18 22:11:43 +00:00
{
2022-04-20 00:57:06 +02:00
// The nodeValue setter steps are to, if the given value is null, act as if it was the empty string instead,
// and then do as described below, switching on the interface this implements:
2022-02-18 22:11:43 +00:00
2022-04-20 00:57:06 +02:00
// If Attr, set an existing attribute value with this and the given value.
2022-02-18 22:11:43 +00:00
if ( is < Attribute > ( this ) ) {
verify_cast < Attribute > ( this ) - > set_value ( value ) ;
} else if ( is < CharacterData > ( this ) ) {
2022-04-20 00:57:06 +02:00
// If CharacterData, replace data with node this, offset 0, count this’ s length, and data the given value.
2022-02-18 22:11:43 +00:00
verify_cast < CharacterData > ( this ) - > set_data ( value ) ;
}
2022-04-20 00:57:06 +02:00
// Otherwise, do nothing.
2022-02-18 22:11:43 +00:00
}
2019-10-14 18:32:02 +02:00
void Node : : invalidate_style ( )
{
2022-03-19 18:10:59 +01:00
if ( is_document ( ) ) {
auto & document = static_cast < DOM : : Document & > ( * this ) ;
document . set_needs_full_style_update ( true ) ;
document . schedule_style_update ( ) ;
return ;
}
2022-03-14 21:40:34 +01:00
for_each_in_inclusive_subtree ( [ & ] ( Node & node ) {
node . m_needs_style_update = true ;
if ( node . has_children ( ) )
node . m_child_needs_style_update = true ;
if ( auto * shadow_root = node . is_element ( ) ? static_cast < DOM : : Element & > ( node ) . shadow_root ( ) : nullptr ) {
2022-03-15 20:45:58 +01:00
node . m_child_needs_style_update = true ;
2022-03-14 21:40:34 +01:00
shadow_root - > m_needs_style_update = true ;
if ( shadow_root - > has_children ( ) )
shadow_root - > m_child_needs_style_update = true ;
}
2019-10-21 12:01:30 +02:00
return IterationDecision : : Continue ;
2019-10-19 18:57:02 +02:00
} ) ;
2022-03-15 21:01:26 +01:00
for ( auto * ancestor = parent_or_shadow_host ( ) ; ancestor ; ancestor = ancestor - > parent_or_shadow_host ( ) )
2022-03-14 21:40:34 +01:00
ancestor - > m_child_needs_style_update = true ;
2019-10-19 18:57:02 +02:00
document ( ) . schedule_style_update ( ) ;
2019-10-14 18:32:02 +02:00
}
2019-10-19 21:21:29 +02:00
bool Node : : is_link ( ) const
{
2020-06-13 00:22:41 +02:00
return enclosing_link_element ( ) ;
2019-10-19 21:21:29 +02:00
}
2020-03-07 10:27:02 +01:00
2020-05-24 21:59:24 +02:00
String Node : : child_text_content ( ) const
{
if ( ! is < ParentNode > ( * this ) )
return String : : empty ( ) ;
StringBuilder builder ;
2021-06-24 19:53:42 +02:00
verify_cast < ParentNode > ( * this ) . for_each_child ( [ & ] ( auto & child ) {
2020-05-24 21:59:24 +02:00
if ( is < Text > ( child ) )
2021-06-24 19:53:42 +02:00
builder . append ( verify_cast < Text > ( child ) . text_content ( ) ) ;
2020-05-24 21:59:24 +02:00
} ) ;
return builder . build ( ) ;
}
2021-09-02 19:27:42 +01:00
// https://dom.spec.whatwg.org/#concept-tree-root
Node & Node : : root ( )
2020-05-24 21:59:24 +02:00
{
2022-04-20 00:57:06 +02:00
// The root of an object is itself, if its parent is null, or else it is the root of its parent.
// The root of a tree is any object participating in that tree whose parent is null.
2020-11-21 18:32:39 +00:00
Node * root = this ;
2020-05-24 21:59:24 +02:00
while ( root - > parent ( ) )
root = root - > parent ( ) ;
2021-09-02 19:27:42 +01:00
return * root ;
2020-05-24 21:59:24 +02:00
}
2021-09-02 19:27:42 +01:00
// https://dom.spec.whatwg.org/#concept-shadow-including-root
Node & Node : : shadow_including_root ( )
2020-11-21 18:32:39 +00:00
{
2022-04-20 00:57:06 +02:00
// The shadow-including root of an object is its root’ s host’ s shadow-including root,
// if the object’ s root is a shadow root; otherwise its root.
2021-09-02 19:27:42 +01:00
auto & node_root = root ( ) ;
2020-11-21 18:32:39 +00:00
if ( is < ShadowRoot > ( node_root ) )
2021-09-02 19:27:42 +01:00
return verify_cast < ShadowRoot > ( node_root ) . host ( ) - > shadow_including_root ( ) ;
2020-11-21 18:32:39 +00:00
return node_root ;
}
2021-09-02 19:27:42 +01:00
// https://dom.spec.whatwg.org/#connected
2020-05-24 21:59:24 +02:00
bool Node : : is_connected ( ) const
{
2022-04-20 00:57:06 +02:00
// An element is connected if its shadow-including root is a document.
2021-09-02 19:27:42 +01:00
return shadow_including_root ( ) . is_document ( ) ;
2020-05-24 21:59:24 +02:00
}
2020-06-20 22:26:54 +02:00
Element * Node : : parent_element ( )
{
if ( ! parent ( ) | | ! is < Element > ( parent ( ) ) )
return nullptr ;
2021-06-24 19:53:42 +02:00
return verify_cast < Element > ( parent ( ) ) ;
2020-06-20 22:26:54 +02:00
}
2022-04-01 20:58:27 +03:00
Element const * Node : : parent_element ( ) const
2020-06-20 22:26:54 +02:00
{
if ( ! parent ( ) | | ! is < Element > ( parent ( ) ) )
return nullptr ;
2021-06-24 19:53:42 +02:00
return verify_cast < Element > ( parent ( ) ) ;
2020-06-20 22:26:54 +02:00
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
ExceptionOr < void > Node : : ensure_pre_insertion_validity ( NonnullRefPtr < Node > node , RefPtr < Node > child ) const
2020-06-21 01:00:58 +02:00
{
2022-04-20 00:57:06 +02:00
// 1. If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
2021-04-06 19:34:49 +01:00
if ( ! is < Document > ( this ) & & ! is < DocumentFragment > ( this ) & & ! is < Element > ( this ) )
return DOM : : HierarchyRequestError : : create ( " Can only insert into a document, document fragment or element " ) ;
2022-04-20 00:57:06 +02:00
// 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException.
2021-04-06 19:34:49 +01:00
if ( node - > is_host_including_inclusive_ancestor_of ( * this ) )
return DOM : : HierarchyRequestError : : create ( " New node is an ancestor of this node " ) ;
2022-04-20 00:57:06 +02:00
// 3. If child is non-null and its parent is not parent, then throw a "NotFoundError" DOMException.
2021-04-06 19:34:49 +01:00
if ( child & & child - > parent ( ) ! = this )
return DOM : : NotFoundError : : create ( " This node is not the parent of the given child " ) ;
// FIXME: All the following "Invalid node type for insertion" messages could be more descriptive.
2022-04-20 00:57:06 +02:00
// 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
2021-04-06 19:34:49 +01:00
if ( ! is < DocumentFragment > ( * node ) & & ! is < DocumentType > ( * node ) & & ! is < Element > ( * node ) & & ! is < Text > ( * node ) & & ! is < Comment > ( * node ) & & ! is < ProcessingInstruction > ( * node ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
2022-04-20 00:57:06 +02:00
// 5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
2021-04-06 19:34:49 +01:00
if ( ( is < Text > ( * node ) & & is < Document > ( this ) ) | | ( is < DocumentType > ( * node ) & & ! is < Document > ( this ) ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
2022-04-20 00:57:06 +02:00
// 6. If parent is a document, and any of the statements below, switched on the interface node implements, are true, then throw a "HierarchyRequestError" DOMException.
2021-04-06 19:34:49 +01:00
if ( is < Document > ( this ) ) {
2022-04-20 00:57:06 +02:00
// DocumentFragment
2021-04-06 19:34:49 +01:00
if ( is < DocumentFragment > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// If node has more than one element child or has a Text node child.
// Otherwise, if node has one element child and either parent has an element child, child is a doctype, or child is non-null and a doctype is following child.
2021-06-24 19:53:42 +02:00
auto node_element_child_count = verify_cast < DocumentFragment > ( * node ) . child_element_count ( ) ;
2021-04-06 19:34:49 +01:00
if ( ( node_element_child_count > 1 | | node - > has_child_of_type < Text > ( ) )
2021-09-07 08:15:34 +01:00
| | ( node_element_child_count = = 1 & & ( has_child_of_type < Element > ( ) | | is < DocumentType > ( child . ptr ( ) ) | | ( child & & child - > has_following_node_of_type_in_tree_order < DocumentType > ( ) ) ) ) ) {
2021-04-06 19:34:49 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
}
} else if ( is < Element > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// Element
// If parent has an element child, child is a doctype, or child is non-null and a doctype is following child.
2021-09-07 08:15:34 +01:00
if ( has_child_of_type < Element > ( ) | | is < DocumentType > ( child . ptr ( ) ) | | ( child & & child - > has_following_node_of_type_in_tree_order < DocumentType > ( ) ) )
2021-04-06 19:34:49 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
} else if ( is < DocumentType > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// DocumentType
// parent has a doctype child, child is non-null and an element is preceding child, or child is null and parent has an element child.
2021-09-07 08:15:34 +01:00
if ( has_child_of_type < DocumentType > ( ) | | ( child & & child - > has_preceding_node_of_type_in_tree_order < Element > ( ) ) | | ( ! child & & has_child_of_type < Element > ( ) ) )
2021-04-06 19:34:49 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
}
}
return { } ;
2020-06-21 01:00:58 +02:00
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-node-insert
void Node : : insert_before ( NonnullRefPtr < Node > node , RefPtr < Node > child , bool suppress_observers )
2021-01-28 08:57:37 +01:00
{
2022-04-20 00:57:06 +02:00
// 1. Let nodes be node’ s children, if node is a DocumentFragment node; otherwise « node ».
2021-04-06 19:34:49 +01:00
NonnullRefPtrVector < Node > nodes ;
if ( is < DocumentFragment > ( * node ) )
2022-07-11 16:39:14 +01:00
nodes = node - > children_as_vector ( ) ;
2021-04-06 19:34:49 +01:00
else
nodes . append ( node ) ;
2022-04-20 00:57:06 +02:00
// 2. Let count be nodes’ s size.
2021-04-06 19:34:49 +01:00
auto count = nodes . size ( ) ;
2022-04-20 00:57:06 +02:00
// 3. If count is 0, then return.
2021-04-06 19:34:49 +01:00
if ( count = = 0 )
return ;
2022-04-20 00:57:06 +02:00
// 4. If node is a DocumentFragment node, then:
2021-04-06 19:34:49 +01:00
if ( is < DocumentFragment > ( * node ) ) {
2022-04-20 19:48:48 +02:00
// 1. Remove its children with the suppress observers flag set.
2021-04-06 19:34:49 +01:00
node - > remove_all_children ( true ) ;
2022-04-20 00:57:06 +02:00
2022-07-11 16:39:14 +01:00
// 2. Queue a tree mutation record for node with « », nodes, null, and null.
2022-04-20 00:57:06 +02:00
// NOTE: This step intentionally does not pay attention to the suppress observers flag.
2022-07-11 16:39:14 +01:00
node - > queue_tree_mutation_record ( StaticNodeList : : create ( { } ) , StaticNodeList : : create ( nodes ) , nullptr , nullptr ) ;
2021-04-06 19:34:49 +01:00
}
2022-03-21 20:26:35 +01:00
// 5. If child is non-null, then:
2021-04-06 19:34:49 +01:00
if ( child ) {
2022-04-20 19:48:48 +02:00
// 1. For each live range whose start node is parent and start offset is greater than child’ s index, increase its start offset by count.
2022-03-21 20:26:35 +01:00
for ( auto & range : Range : : live_ranges ( ) ) {
if ( range - > start_container ( ) = = this & & range - > start_offset ( ) > child - > index ( ) )
range - > set_start ( * range - > start_container ( ) , range - > start_offset ( ) + count ) ;
}
2022-04-20 19:48:48 +02:00
// 2. For each live range whose end node is parent and end offset is greater than child’ s index, increase its end offset by count.
2022-03-21 20:26:35 +01:00
for ( auto & range : Range : : live_ranges ( ) ) {
2022-03-21 21:11:38 +01:00
if ( range - > end_container ( ) = = this & & range - > end_offset ( ) > child - > index ( ) )
2022-03-21 20:26:35 +01:00
range - > set_end ( * range - > end_container ( ) , range - > end_offset ( ) + count ) ;
}
2021-04-06 19:34:49 +01:00
}
2022-07-11 16:39:14 +01:00
// 6. Let previousSibling be child’ s previous sibling or parent’ s last child if child is null.
RefPtr < Node > previous_sibling ;
if ( child )
previous_sibling = child - > previous_sibling ( ) ;
else
previous_sibling = last_child ( ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 7. For each node in nodes, in tree order:
// FIXME: In tree order
for ( auto & node_to_insert : nodes ) {
2022-04-20 19:48:48 +02:00
// 1. Adopt node into parent’ s node document.
2021-04-06 19:34:49 +01:00
document ( ) . adopt_node ( node_to_insert ) ;
2022-04-20 19:48:48 +02:00
// 2. If child is null, then append node to parent’ s children.
2021-04-06 19:34:49 +01:00
if ( ! child )
2021-05-03 02:06:11 +01:00
TreeNode < Node > : : append_child ( node_to_insert ) ;
2022-04-20 19:48:48 +02:00
// 3. Otherwise, insert node into parent’ s children before child’ s index.
2021-04-06 19:34:49 +01:00
else
2021-05-03 02:06:11 +01:00
TreeNode < Node > : : insert_before ( node_to_insert , child ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 4. If parent is a shadow host and node is a slottable, then assign a slot for node.
// FIXME: 5. If parent’ s root is a shadow root, and parent is a slot whose assigned nodes is the empty list, then run signal a slot change for parent.
// FIXME: 6. Run assign slottables for a tree with node’ s root.
2021-04-06 19:34:49 +01:00
// FIXME: This should be shadow-including.
2022-04-20 19:48:48 +02:00
// 7. For each shadow-including inclusive descendant inclusiveDescendant of node, in shadow-including tree order:
2021-04-06 19:34:49 +01:00
node_to_insert . for_each_in_inclusive_subtree ( [ & ] ( Node & inclusive_descendant ) {
2022-04-20 19:48:48 +02:00
// 1. Run the insertion steps with inclusiveDescendant.
2021-04-06 19:34:49 +01:00
inclusive_descendant . inserted ( ) ;
2022-04-20 00:57:06 +02:00
2022-04-20 19:48:48 +02:00
// 2. If inclusiveDescendant is connected, then:
2021-04-06 19:34:49 +01:00
if ( inclusive_descendant . is_connected ( ) ) {
2022-04-20 19:48:48 +02:00
// FIXME: 1. If inclusiveDescendant is custom, then enqueue a custom element callback reaction with inclusiveDescendant, callback name "connectedCallback", and an empty argument list.
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 2. Otherwise, try to upgrade inclusiveDescendant.
2022-04-20 00:57:06 +02:00
// NOTE: If this successfully upgrades inclusiveDescendant, its connectedCallback will be enqueued automatically during the upgrade an element algorithm.
2021-04-06 19:34:49 +01:00
}
return IterationDecision : : Continue ;
} ) ;
}
2022-04-20 00:57:06 +02:00
// 8. If suppress observers flag is unset, then queue a tree mutation record for parent with nodes, « », previousSibling, and child.
2022-07-11 16:39:14 +01:00
if ( ! suppress_observers )
queue_tree_mutation_record ( StaticNodeList : : create ( move ( nodes ) ) , StaticNodeList : : create ( { } ) , previous_sibling , child ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 9. Run the children changed steps for parent.
2021-04-06 19:34:49 +01:00
children_changed ( ) ;
2022-03-14 21:39:33 +01:00
document ( ) . invalidate_style ( ) ;
2021-01-28 08:57:37 +01:00
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-node-pre-insert
2021-04-13 23:03:48 +04:30
ExceptionOr < NonnullRefPtr < Node > > Node : : pre_insert ( NonnullRefPtr < Node > node , RefPtr < Node > child )
2020-06-21 16:45:21 +02:00
{
2022-04-20 00:57:06 +02:00
// 1. Ensure pre-insertion validity of node into parent before child.
2022-03-22 12:39:16 +00:00
TRY ( ensure_pre_insertion_validity ( node , child ) ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 2. Let referenceChild be child.
2021-04-06 19:34:49 +01:00
auto reference_child = child ;
2022-04-20 00:57:06 +02:00
// 3. If referenceChild is node, then set referenceChild to node’ s next sibling.
2021-04-06 19:34:49 +01:00
if ( reference_child = = node )
reference_child = node - > next_sibling ( ) ;
2022-04-20 00:57:06 +02:00
// 4. Insert node into parent before referenceChild.
2021-04-06 19:34:49 +01:00
insert_before ( node , reference_child ) ;
2022-04-20 00:57:06 +02:00
// 5. Return node.
2020-06-21 16:45:21 +02:00
return node ;
}
2022-02-21 22:21:59 +01:00
// https://dom.spec.whatwg.org/#dom-node-removechild
ExceptionOr < NonnullRefPtr < Node > > Node : : remove_child ( NonnullRefPtr < Node > child )
{
2022-04-20 00:57:06 +02:00
// The removeChild(child) method steps are to return the result of pre-removing child from this.
2022-02-21 22:21:59 +01:00
return pre_remove ( child ) ;
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-node-pre-remove
2021-04-13 23:03:48 +04:30
ExceptionOr < NonnullRefPtr < Node > > Node : : pre_remove ( NonnullRefPtr < Node > child )
2021-04-06 19:34:49 +01:00
{
2022-04-20 00:57:06 +02:00
// 1. If child’ s parent is not parent, then throw a "NotFoundError" DOMException.
2021-04-13 23:03:48 +04:30
if ( child - > parent ( ) ! = this )
return DOM : : NotFoundError : : create ( " Child does not belong to this node " ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 2. Remove child.
2021-04-06 19:34:49 +01:00
child - > remove ( ) ;
2022-04-20 00:57:06 +02:00
// 3. Return child.
2021-04-06 19:34:49 +01:00
return child ;
}
// https://dom.spec.whatwg.org/#concept-node-append
2021-04-13 23:03:48 +04:30
ExceptionOr < NonnullRefPtr < Node > > Node : : append_child ( NonnullRefPtr < Node > node )
2021-04-06 19:34:49 +01:00
{
2022-04-20 00:57:06 +02:00
// To append a node to a parent, pre-insert node into parent before null.
2021-04-06 19:34:49 +01:00
return pre_insert ( node , nullptr ) ;
}
// https://dom.spec.whatwg.org/#concept-node-remove
void Node : : remove ( bool suppress_observers )
{
2022-04-20 00:57:06 +02:00
// 1. Let parent be node’ s parent
2021-04-06 19:34:49 +01:00
auto * parent = TreeNode < Node > : : parent ( ) ;
2022-04-20 00:57:06 +02:00
// 2. Assert: parent is non-null.
2021-04-06 19:34:49 +01:00
VERIFY ( parent ) ;
2022-03-21 20:26:35 +01:00
// 3. Let index be node’ s index.
auto index = this - > index ( ) ;
// 4. For each live range whose start node is an inclusive descendant of node, set its start to (parent, index).
for ( auto & range : Range : : live_ranges ( ) ) {
if ( range - > start_container ( ) - > is_inclusive_descendant_of ( * this ) )
range - > set_start ( * parent , index ) ;
}
// 5. For each live range whose end node is an inclusive descendant of node, set its end to (parent, index).
for ( auto & range : Range : : live_ranges ( ) ) {
if ( range - > end_container ( ) - > is_inclusive_descendant_of ( * this ) )
range - > set_end ( * parent , index ) ;
}
2021-04-06 19:34:49 +01:00
2022-03-21 20:26:35 +01:00
// 6. For each live range whose start node is parent and start offset is greater than index, decrease its start offset by 1.
for ( auto & range : Range : : live_ranges ( ) ) {
2022-03-21 21:11:38 +01:00
if ( range - > start_container ( ) = = parent & & range - > start_offset ( ) > index )
2022-03-21 20:26:35 +01:00
range - > set_start ( * range - > start_container ( ) , range - > start_offset ( ) - 1 ) ;
}
// 7. For each live range whose end node is parent and end offset is greater than index, decrease its end offset by 1.
for ( auto & range : Range : : live_ranges ( ) ) {
2022-03-21 21:11:38 +01:00
if ( range - > end_container ( ) = = parent & & range - > end_offset ( ) > index )
2022-03-21 20:26:35 +01:00
range - > set_end ( * range - > end_container ( ) , range - > end_offset ( ) - 1 ) ;
}
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 8. For each NodeIterator object iterator whose root’ s node document is node’ s node document, run the NodeIterator pre-removing steps given node and iterator.
2022-03-09 16:38:44 +01:00
document ( ) . for_each_node_iterator ( [ & ] ( NodeIterator & node_iterator ) {
node_iterator . run_pre_removing_steps ( * this ) ;
} ) ;
2021-04-06 19:34:49 +01:00
2022-07-11 16:39:14 +01:00
// 9. Let oldPreviousSibling be node’ s previous sibling.
RefPtr < Node > old_previous_sibling = previous_sibling ( ) ;
// 10. Let oldNextSibling be node’ s next sibling.
RefPtr < Node > old_next_sibling = next_sibling ( ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 11. Remove node from its parent’ s children.
2022-02-21 22:21:59 +01:00
parent - > TreeNode : : remove_child ( * this ) ;
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 12. If node is assigned, then run assign slottables for node’ s assigned slot.
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 13. If parent’ s root is a shadow root, and parent is a slot whose assigned nodes is the empty list, then run signal a slot change for parent.
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 14. If node has an inclusive descendant that is a slot, then:
// 1. Run assign slottables for a tree with parent’ s root.
// 2. Run assign slottables for a tree with node.
2021-04-06 19:34:49 +01:00
2022-04-20 00:57:06 +02:00
// 15. Run the removing steps with node and parent.
2021-04-06 19:34:49 +01:00
removed_from ( parent ) ;
2022-04-20 19:48:48 +02:00
// FIXME: 16. Let isParentConnected be parent’ s connected. (Currently unused so not included)
2021-04-06 19:34:49 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 17. If node is custom and isParentConnected is true, then enqueue a custom element callback reaction with node,
2021-04-06 19:34:49 +01:00
// callback name "disconnectedCallback", and an empty argument list.
2022-04-20 00:57:06 +02:00
// NOTE: It is intentional for now that custom elements do not get parent passed. This might change in the future if there is a need.
2021-04-06 19:34:49 +01:00
// FIXME: This should be shadow-including.
2022-04-20 00:57:06 +02:00
// 18. For each shadow-including descendant descendant of node, in shadow-including tree order, then:
2021-04-06 19:34:49 +01:00
for_each_in_subtree ( [ & ] ( Node & descendant ) {
2022-04-20 19:48:48 +02:00
// 1. Run the removing steps with descendant
2021-04-06 19:34:49 +01:00
descendant . removed_from ( nullptr ) ;
2022-04-20 19:48:48 +02:00
// FIXME: 2. If descendant is custom and isParentConnected is true, then enqueue a custom element callback reaction with descendant,
2021-04-06 19:34:49 +01:00
// callback name "disconnectedCallback", and an empty argument list.
return IterationDecision : : Continue ;
} ) ;
2022-04-20 19:48:48 +02:00
// FIXME: 19. For each inclusive ancestor inclusiveAncestor of parent, and then for each registered of inclusiveAncestor’ s registered observer list,
2022-04-20 00:57:06 +02:00
// if registered’ s options["subtree"] is true, then append a new transient registered observer
// whose observer is registered’ s observer, options is registered’ s options, and source is registered to node’ s registered observer list.
// 20. If suppress observers flag is unset, then queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling.
2021-04-06 19:34:49 +01:00
if ( ! suppress_observers ) {
2022-07-11 16:39:14 +01:00
NonnullRefPtrVector < Node > removed_nodes ;
removed_nodes . append ( * this ) ;
queue_tree_mutation_record ( StaticNodeList : : create ( { } ) , StaticNodeList : : create ( move ( removed_nodes ) ) , old_previous_sibling , old_next_sibling ) ;
2021-04-06 19:34:49 +01:00
}
2022-04-20 00:57:06 +02:00
// 21. Run the children changed steps for parent.
2021-04-06 19:34:49 +01:00
parent - > children_changed ( ) ;
2022-03-09 17:54:50 +01:00
document ( ) . invalidate_style ( ) ;
2021-04-06 19:34:49 +01:00
}
2021-05-07 00:52:23 +01:00
// https://dom.spec.whatwg.org/#concept-node-replace
ExceptionOr < NonnullRefPtr < Node > > Node : : replace_child ( NonnullRefPtr < Node > node , NonnullRefPtr < Node > child )
{
2022-04-20 00:57:06 +02:00
// If parent is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
2021-05-07 00:52:23 +01:00
if ( ! is < Document > ( this ) & & ! is < DocumentFragment > ( this ) & & ! is < Element > ( this ) )
return DOM : : HierarchyRequestError : : create ( " Can only insert into a document, document fragment or element " ) ;
2022-04-20 00:57:06 +02:00
// 2. If node is a host-including inclusive ancestor of parent, then throw a "HierarchyRequestError" DOMException.
2021-05-07 00:52:23 +01:00
if ( node - > is_host_including_inclusive_ancestor_of ( * this ) )
return DOM : : HierarchyRequestError : : create ( " New node is an ancestor of this node " ) ;
2022-04-20 00:57:06 +02:00
// 3. If child’ s parent is not parent, then throw a "NotFoundError" DOMException.
2021-05-07 00:52:23 +01:00
if ( child - > parent ( ) ! = this )
return DOM : : NotFoundError : : create ( " This node is not the parent of the given child " ) ;
// FIXME: All the following "Invalid node type for insertion" messages could be more descriptive.
2022-04-20 00:57:06 +02:00
// 4. If node is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
2021-05-07 00:52:23 +01:00
if ( ! is < DocumentFragment > ( * node ) & & ! is < DocumentType > ( * node ) & & ! is < Element > ( * node ) & & ! is < Text > ( * node ) & & ! is < Comment > ( * node ) & & ! is < ProcessingInstruction > ( * node ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
2022-04-20 00:57:06 +02:00
// 5. If either node is a Text node and parent is a document, or node is a doctype and parent is not a document, then throw a "HierarchyRequestError" DOMException.
2021-05-07 00:52:23 +01:00
if ( ( is < Text > ( * node ) & & is < Document > ( this ) ) | | ( is < DocumentType > ( * node ) & & ! is < Document > ( this ) ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
2022-04-20 00:57:06 +02:00
// If parent is a document, and any of the statements below, switched on the interface node implements, are true, then throw a "HierarchyRequestError" DOMException.
2021-05-07 00:52:23 +01:00
if ( is < Document > ( this ) ) {
2022-04-20 00:57:06 +02:00
// DocumentFragment
2021-05-07 00:52:23 +01:00
if ( is < DocumentFragment > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// If node has more than one element child or has a Text node child.
// Otherwise, if node has one element child and either parent has an element child that is not child or a doctype is following child.
2021-06-24 19:53:42 +02:00
auto node_element_child_count = verify_cast < DocumentFragment > ( * node ) . child_element_count ( ) ;
2021-05-07 00:52:23 +01:00
if ( ( node_element_child_count > 1 | | node - > has_child_of_type < Text > ( ) )
2021-09-07 08:15:34 +01:00
| | ( node_element_child_count = = 1 & & ( first_child_of_type < Element > ( ) ! = child | | child - > has_following_node_of_type_in_tree_order < DocumentType > ( ) ) ) ) {
2021-05-07 00:52:23 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
}
} else if ( is < Element > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// Element
// parent has an element child that is not child or a doctype is following child.
2021-09-07 08:15:34 +01:00
if ( first_child_of_type < Element > ( ) ! = child | | child - > has_following_node_of_type_in_tree_order < DocumentType > ( ) )
2021-05-07 00:52:23 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
} else if ( is < DocumentType > ( * node ) ) {
2022-04-20 00:57:06 +02:00
// DocumentType
// parent has a doctype child that is not child, or an element is preceding child.
2021-09-07 08:15:34 +01:00
if ( first_child_of_type < DocumentType > ( ) ! = node | | child - > has_preceding_node_of_type_in_tree_order < Element > ( ) )
2021-05-07 00:52:23 +01:00
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
}
}
2022-04-20 00:57:06 +02:00
// 7. Let referenceChild be child’ s next sibling.
2022-07-11 16:39:14 +01:00
RefPtr < Node > reference_child = child - > next_sibling ( ) ;
2022-04-20 00:57:06 +02:00
// 8. If referenceChild is node, then set referenceChild to node’ s next sibling.
2021-05-07 00:52:23 +01:00
if ( reference_child = = node )
reference_child = node - > next_sibling ( ) ;
2022-07-11 16:39:14 +01:00
// 9. Let previousSibling be child’ s previous sibling.
RefPtr < Node > previous_sibling = child - > previous_sibling ( ) ;
// 10. Let removedNodes be the empty set.
NonnullRefPtrVector < Node > removed_nodes ;
2021-05-07 00:52:23 +01:00
2022-04-20 00:57:06 +02:00
// 11. If child’ s parent is non-null, then:
// NOTE: The above can only be false if child is node.
2021-05-07 00:52:23 +01:00
if ( child - > parent ( ) ) {
2022-07-11 16:39:14 +01:00
// 1. Set removedNodes to « child ».
removed_nodes . append ( child ) ;
2022-04-20 00:57:06 +02:00
2022-04-20 19:48:48 +02:00
// 2. Remove child with the suppress observers flag set.
2021-05-07 00:52:23 +01:00
child - > remove ( true ) ;
}
2022-07-11 16:39:14 +01:00
// 12. Let nodes be node’ s children if node is a DocumentFragment node; otherwise « node ».
NonnullRefPtrVector < Node > nodes ;
if ( is < DocumentFragment > ( * node ) )
nodes = node - > children_as_vector ( ) ;
else
nodes . append ( node ) ;
2021-05-07 00:52:23 +01:00
2022-04-20 00:57:06 +02:00
// 13. Insert node into parent before referenceChild with the suppress observers flag set.
2021-05-07 00:52:23 +01:00
insert_before ( node , reference_child , true ) ;
2022-07-11 16:39:14 +01:00
// 14. Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
queue_tree_mutation_record ( StaticNodeList : : create ( move ( nodes ) ) , StaticNodeList : : create ( move ( removed_nodes ) ) , previous_sibling , reference_child ) ;
2021-05-07 00:52:23 +01:00
2022-04-20 00:57:06 +02:00
// 15. Return child.
2021-05-07 00:52:23 +01:00
return child ;
}
2021-04-14 01:25:10 +02:00
// https://dom.spec.whatwg.org/#concept-node-clone
2021-07-05 05:30:24 +01:00
NonnullRefPtr < Node > Node : : clone_node ( Document * document , bool clone_children )
2021-04-14 01:25:10 +02:00
{
2022-04-20 00:57:06 +02:00
// 1. If document is not given, let document be node’ s node document.
2021-04-14 01:25:10 +02:00
if ( ! document )
document = m_document ;
RefPtr < Node > copy ;
2022-04-20 00:57:06 +02:00
// 2. If node is an element, then:
2021-04-14 01:25:10 +02:00
if ( is < Element > ( this ) ) {
2022-04-20 19:48:48 +02:00
// 1. Let copy be the result of creating an element, given document, node’ s local name, node’ s namespace, node’ s namespace prefix, and node’ s is value, with the synchronous custom elements flag unset.
2021-06-24 19:53:42 +02:00
auto & element = * verify_cast < Element > ( this ) ;
2021-07-05 05:28:29 +01:00
auto element_copy = DOM : : create_element ( * document , element . local_name ( ) , element . namespace_ ( ) /* FIXME: node’ s namespace prefix, and node’ s is value, with the synchronous custom elements flag unset */ ) ;
2022-04-20 00:57:06 +02:00
2022-04-20 19:48:48 +02:00
// 2. For each attribute in node’ s attribute list:
2021-04-14 01:25:10 +02:00
element . for_each_attribute ( [ & ] ( auto & name , auto & value ) {
2022-04-20 19:48:48 +02:00
// 1. Let copyAttribute be a clone of attribute.
// 2. Append copyAttribute to copy.
2021-04-14 01:25:10 +02:00
element_copy - > set_attribute ( name , value ) ;
} ) ;
copy = move ( element_copy ) ;
2022-04-20 00:57:06 +02:00
2022-06-03 19:12:15 +03:00
}
// 3. Otherwise, let copy be a node that implements the same interfaces as node, and fulfills these additional requirements, switching on the interface node implements:
else if ( is < Document > ( this ) ) {
2022-04-20 00:57:06 +02:00
// Document
2021-06-24 19:53:42 +02:00
auto document_ = verify_cast < Document > ( this ) ;
2021-04-14 01:25:10 +02:00
auto document_copy = Document : : create ( document_ - > url ( ) ) ;
2022-04-20 00:57:06 +02:00
// Set copy’ s encoding, content type, URL, origin, type, and mode to those of node.
2021-04-14 01:25:10 +02:00
document_copy - > set_encoding ( document_ - > encoding ( ) ) ;
document_copy - > set_content_type ( document_ - > content_type ( ) ) ;
2022-04-20 00:57:06 +02:00
document_copy - > set_url ( document_ - > url ( ) ) ;
2021-04-14 01:25:10 +02:00
document_copy - > set_origin ( document_ - > origin ( ) ) ;
2022-07-03 21:44:00 +02:00
document_copy - > set_document_type ( document_ - > document_type ( ) ) ;
2022-04-20 00:57:06 +02:00
document_copy - > set_quirks_mode ( document_ - > mode ( ) ) ;
2021-04-14 01:25:10 +02:00
copy = move ( document_copy ) ;
} else if ( is < DocumentType > ( this ) ) {
2022-04-20 00:57:06 +02:00
// DocumentType
2021-06-24 19:53:42 +02:00
auto document_type = verify_cast < DocumentType > ( this ) ;
2021-04-23 16:46:57 +02:00
auto document_type_copy = adopt_ref ( * new DocumentType ( * document ) ) ;
2022-04-20 00:57:06 +02:00
// Set copy’ s name, public ID, and system ID to those of node.
2021-04-14 01:25:10 +02:00
document_type_copy - > set_name ( document_type - > name ( ) ) ;
document_type_copy - > set_public_id ( document_type - > public_id ( ) ) ;
document_type_copy - > set_system_id ( document_type - > system_id ( ) ) ;
copy = move ( document_type_copy ) ;
2022-04-20 00:57:06 +02:00
} else if ( is < Attribute > ( this ) ) {
// FIXME:
// Attr
// Set copy’ s namespace, namespace prefix, local name, and value to those of node.
dbgln ( " clone_node() not implemented for Attribute " ) ;
2021-04-14 01:25:10 +02:00
} else if ( is < Text > ( this ) ) {
2022-04-20 00:57:06 +02:00
// Text
2021-06-24 19:53:42 +02:00
auto text = verify_cast < Text > ( this ) ;
2022-04-20 00:57:06 +02:00
// Set copy’ s data to that of node.
2021-04-23 16:46:57 +02:00
auto text_copy = adopt_ref ( * new Text ( * document , text - > data ( ) ) ) ;
2021-04-14 01:25:10 +02:00
copy = move ( text_copy ) ;
} else if ( is < Comment > ( this ) ) {
2022-04-20 00:57:06 +02:00
// Comment
2021-06-24 19:53:42 +02:00
auto comment = verify_cast < Comment > ( this ) ;
2022-04-20 00:57:06 +02:00
// Set copy’ s data to that of node.
2021-04-23 16:46:57 +02:00
auto comment_copy = adopt_ref ( * new Comment ( * document , comment - > data ( ) ) ) ;
2021-04-14 01:25:10 +02:00
copy = move ( comment_copy ) ;
} else if ( is < ProcessingInstruction > ( this ) ) {
2022-04-20 00:57:06 +02:00
// ProcessingInstruction
2021-06-24 19:53:42 +02:00
auto processing_instruction = verify_cast < ProcessingInstruction > ( this ) ;
2022-04-20 00:57:06 +02:00
// Set copy’ s target and data to those of node.
2021-04-23 16:46:57 +02:00
auto processing_instruction_copy = adopt_ref ( * new ProcessingInstruction ( * document , processing_instruction - > data ( ) , processing_instruction - > target ( ) ) ) ;
2021-04-14 01:25:10 +02:00
copy = move ( processing_instruction_copy ) ;
}
2022-04-20 00:57:06 +02:00
// Otherwise, Do nothing.
2022-06-03 19:12:15 +03:00
else if ( is < DocumentFragment > ( this ) ) {
copy = adopt_ref ( * new DocumentFragment ( * document ) ) ;
}
2022-04-20 00:57:06 +02:00
2021-04-14 01:25:10 +02:00
// FIXME: 4. Set copy’ s node document and document to copy, if copy is a document, and set copy’ s node document to document otherwise.
2021-07-05 05:30:24 +01:00
2022-04-20 00:57:06 +02:00
// 5. Run any cloning steps defined for node in other applicable specifications and pass copy, node, document and the clone children flag if set, as parameters.
2021-07-05 05:30:24 +01:00
cloned ( * copy , clone_children ) ;
2022-04-20 00:57:06 +02:00
// 6. If the clone children flag is set, clone all the children of node and append them to copy, with document as specified and the clone children flag being set.
2021-04-14 01:25:10 +02:00
if ( clone_children ) {
for_each_child ( [ & ] ( auto & child ) {
copy - > append_child ( child . clone_node ( document , true ) ) ;
} ) ;
}
2022-04-20 00:57:06 +02:00
// 7. Return copy.
2021-04-14 01:25:10 +02:00
return copy . release_nonnull ( ) ;
}
// https://dom.spec.whatwg.org/#dom-node-clonenode
2021-07-05 05:30:24 +01:00
ExceptionOr < NonnullRefPtr < Node > > Node : : clone_node_binding ( bool deep )
2021-04-14 01:25:10 +02:00
{
2022-04-20 00:57:06 +02:00
// 1. If this is a shadow root, then throw a "NotSupportedError" DOMException.
2021-04-14 01:25:10 +02:00
if ( is < ShadowRoot > ( * this ) )
return NotSupportedError : : create ( " Cannot clone shadow root " ) ;
2022-04-20 00:57:06 +02:00
// 2. Return a clone of this, with the clone children flag set if deep is true.
2021-04-14 01:25:10 +02:00
return clone_node ( nullptr , deep ) ;
}
2020-06-25 23:42:08 +02:00
void Node : : set_document ( Badge < Document > , Document & document )
{
2020-10-22 23:37:17 +02:00
if ( m_document = = & document )
return ;
2021-08-30 15:52:08 +01:00
2020-10-22 23:37:17 +02:00
document . ref_from_node ( { } ) ;
m_document - > unref_from_node ( { } ) ;
2020-06-25 23:42:08 +02:00
m_document = & document ;
2021-10-12 17:53:38 +02:00
if ( needs_style_update ( ) | | child_needs_style_update ( ) ) {
// NOTE: We unset and reset the "needs style update" flag here.
// This ensures that there's a pending style update in the new document
// that will eventually assign some style to this node if needed.
set_needs_style_update ( false ) ;
set_needs_style_update ( true ) ;
}
2020-06-25 23:42:08 +02:00
}
2020-08-02 16:05:59 +02:00
bool Node : : is_editable ( ) const
{
return parent ( ) & & parent ( ) - > is_editable ( ) ;
}
2021-01-18 12:15:02 +01:00
JS : : Object * Node : : create_wrapper ( JS : : GlobalObject & global_object )
2020-09-06 14:28:41 +02:00
{
return wrap ( global_object , * this ) ;
}
2020-10-11 21:52:59 +02:00
void Node : : removed_last_ref ( )
{
if ( is < Document > ( * this ) ) {
2021-06-24 19:53:42 +02:00
verify_cast < Document > ( * this ) . removed_last_ref ( ) ;
2020-10-11 21:52:59 +02:00
return ;
}
2020-10-22 23:38:14 +02:00
m_deletion_has_begun = true ;
2020-10-11 21:52:59 +02:00
delete this ;
}
2020-11-22 15:53:01 +01:00
void Node : : set_layout_node ( Badge < Layout : : Node > , Layout : : Node * layout_node ) const
2020-10-22 20:26:32 +02:00
{
2022-07-04 00:42:44 +02:00
m_layout_node = layout_node ;
2020-10-22 20:26:32 +02:00
}
2022-04-01 20:58:27 +03:00
EventTarget * Node : : get_parent ( Event const & )
2020-11-21 18:32:39 +00:00
{
// FIXME: returns the node’ s assigned slot, if node is assigned, and node’ s parent otherwise.
return parent ( ) ;
}
2020-12-13 15:19:42 +01:00
void Node : : set_needs_style_update ( bool value )
{
if ( m_needs_style_update = = value )
return ;
m_needs_style_update = value ;
2020-12-14 12:04:30 +01:00
if ( m_needs_style_update ) {
2022-03-14 12:52:27 +01:00
for ( auto * ancestor = parent_or_shadow_host ( ) ; ancestor ; ancestor = ancestor - > parent_or_shadow_host ( ) ) {
2020-12-14 12:04:30 +01:00
ancestor - > m_child_needs_style_update = true ;
2021-04-06 19:34:49 +01:00
}
2020-12-13 15:19:42 +01:00
document ( ) . schedule_style_update ( ) ;
2020-12-14 12:04:30 +01:00
}
}
2021-04-06 19:34:49 +01:00
void Node : : inserted ( )
2020-12-14 12:04:30 +01:00
{
set_needs_style_update ( true ) ;
2020-12-13 15:19:42 +01:00
}
2021-02-10 18:22:20 +01:00
ParentNode * Node : : parent_or_shadow_host ( )
{
if ( is < ShadowRoot > ( * this ) )
2021-06-24 19:53:42 +02:00
return verify_cast < ShadowRoot > ( * this ) . host ( ) ;
return verify_cast < ParentNode > ( parent ( ) ) ;
2021-02-10 18:22:20 +01:00
}
2021-10-02 20:37:45 +01:00
NonnullRefPtr < NodeList > Node : : child_nodes ( )
{
// FIXME: This should return the same LiveNodeList object every time,
// but that would cause a reference cycle since NodeList refs the root.
return LiveNodeList : : create ( * this , [ this ] ( auto & node ) {
return is_parent_of ( node ) ;
} ) ;
}
NonnullRefPtrVector < Node > Node : : children_as_vector ( ) const
2021-03-06 17:06:25 +00:00
{
NonnullRefPtrVector < Node > nodes ;
for_each_child ( [ & ] ( auto & child ) {
nodes . append ( child ) ;
} ) ;
return nodes ;
}
2021-04-06 19:34:49 +01:00
void Node : : remove_all_children ( bool suppress_observers )
{
while ( RefPtr < Node > child = first_child ( ) )
child - > remove ( suppress_observers ) ;
}
2021-04-10 17:21:22 -07:00
// https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
u16 Node : : compare_document_position ( RefPtr < Node > other )
{
enum Position : u16 {
DOCUMENT_POSITION_EQUAL = 0 ,
DOCUMENT_POSITION_DISCONNECTED = 1 ,
DOCUMENT_POSITION_PRECEDING = 2 ,
DOCUMENT_POSITION_FOLLOWING = 4 ,
DOCUMENT_POSITION_CONTAINS = 8 ,
DOCUMENT_POSITION_CONTAINED_BY = 16 ,
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 32 ,
} ;
2022-04-20 00:57:06 +02:00
// 1. If this is other, then return zero.
2021-04-10 17:21:22 -07:00
if ( this = = other )
return DOCUMENT_POSITION_EQUAL ;
2022-04-20 00:57:06 +02:00
// 2. Let node1 be other and node2 be this.
2021-04-10 17:21:22 -07:00
Node * node1 = other . ptr ( ) ;
Node * node2 = this ;
2022-04-20 00:57:06 +02:00
// 3. Let attr1 and attr2 be null.
Attribute * attr1 ;
Attribute * attr2 ;
// 4. If node1 is an attribute, then set attr1 to node1 and node1 to attr1’ s element.
if ( is < Attribute > ( node1 ) ) {
attr1 = verify_cast < Attribute > ( node1 ) ;
node1 = const_cast < Element * > ( attr1 - > owner_element ( ) ) ;
}
2021-04-10 17:21:22 -07:00
2022-04-20 00:57:06 +02:00
// 5. If node2 is an attribute, then:
if ( is < Attribute > ( node2 ) ) {
2022-04-20 19:48:48 +02:00
// 1. Set attr2 to node2 and node2 to attr2’ s element.
2022-04-20 00:57:06 +02:00
attr2 = verify_cast < Attribute > ( node2 ) ;
node2 = const_cast < Element * > ( attr2 - > owner_element ( ) ) ;
2022-04-20 19:48:48 +02:00
// 2. If attr1 and node1 are non-null, and node2 is node1, then:
2022-04-20 00:57:06 +02:00
if ( attr1 & & node1 & & node2 = = node1 ) {
2022-04-20 19:48:48 +02:00
// FIXME: 1. For each attr in node2’ s attribute list:
// 1. If attr equals attr1, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_PRECEDING.
// 2. If attr equals attr2, then return the result of adding DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC and DOCUMENT_POSITION_FOLLOWING.
2022-04-20 00:57:06 +02:00
}
}
// 6. If node1 or node2 is null, or node1’ s root is not node2’ s root, then return the result of adding
// DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC, and either DOCUMENT_POSITION_PRECEDING or DOCUMENT_POSITION_FOLLOWING, with the constraint that this is to be consistent, together.
2021-09-02 19:27:42 +01:00
if ( ( node1 = = nullptr | | node2 = = nullptr ) | | ( & node1 - > root ( ) ! = & node2 - > root ( ) ) )
2021-04-10 17:21:22 -07:00
return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | ( node1 > node2 ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING ) ;
2022-04-20 00:57:06 +02:00
// 7. If node1 is an ancestor of node2 and attr1 is null, or node1 is node2 and attr2 is non-null, then return the result of adding DOCUMENT_POSITION_CONTAINS to DOCUMENT_POSITION_PRECEDING.
if ( ( node1 - > is_ancestor_of ( * node2 ) & & ! attr1 ) | | ( node1 = = node2 & & attr2 ) )
2021-04-10 17:21:22 -07:00
return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING ;
2022-04-20 00:57:06 +02:00
// 8. If node1 is a descendant of node2 and attr2 is null, or node1 is node2 and attr1 is non-null, then return the result of adding DOCUMENT_POSITION_CONTAINED_BY to DOCUMENT_POSITION_FOLLOWING.
if ( ( node2 - > is_ancestor_of ( * node1 ) & & ! attr2 ) | | ( node1 = = node2 & & attr1 ) )
2021-04-10 17:21:22 -07:00
return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING ;
2022-04-20 00:57:06 +02:00
// 9. If node1 is preceding node2, then return DOCUMENT_POSITION_PRECEDING.
2021-04-10 17:21:22 -07:00
if ( node1 - > is_before ( * node2 ) )
return DOCUMENT_POSITION_PRECEDING ;
2022-04-20 00:57:06 +02:00
// 10. Return DOCUMENT_POSITION_FOLLOWING.
return DOCUMENT_POSITION_FOLLOWING ;
2021-04-10 17:21:22 -07:00
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-tree-host-including-inclusive-ancestor
2022-04-01 20:58:27 +03:00
bool Node : : is_host_including_inclusive_ancestor_of ( Node const & other ) const
2021-04-06 19:34:49 +01:00
{
2022-04-20 00:57:06 +02:00
// An object A is a host-including inclusive ancestor of an object B,
// if either A is an inclusive ancestor of B,
2022-03-14 12:46:14 +01:00
if ( is_inclusive_ancestor_of ( other ) )
return true ;
2022-04-20 00:57:06 +02:00
// or if B’ s root has a non-null host and A is a host-including inclusive ancestor of B’ s root’ s host
2022-03-14 12:46:14 +01:00
if ( is < DocumentFragment > ( other . root ( ) )
& & static_cast < DocumentFragment const & > ( other . root ( ) ) . host ( )
& & is_inclusive_ancestor_of ( * static_cast < DocumentFragment const & > ( other . root ( ) ) . host ( ) ) ) {
return true ;
}
return false ;
2021-04-06 19:34:49 +01:00
}
2021-05-02 21:03:50 +01:00
// https://dom.spec.whatwg.org/#dom-node-ownerdocument
RefPtr < Document > Node : : owner_document ( ) const
{
2022-04-20 00:57:06 +02:00
// The ownerDocument getter steps are to return null, if this is a document; otherwise this’ s node document.
2021-05-02 21:03:50 +01:00
if ( is_document ( ) )
return nullptr ;
return m_document ;
}
2021-11-02 19:20:57 +01:00
// This function tells us whether a node is interesting enough to show up
// in the DOM inspector. This hides two things:
// - Non-rendered whitespace
// - Rendered whitespace between block-level elements
bool Node : : is_uninteresting_whitespace_node ( ) const
{
if ( ! is < Text > ( * this ) )
return false ;
if ( ! static_cast < Text const & > ( * this ) . data ( ) . is_whitespace ( ) )
return false ;
if ( ! layout_node ( ) )
return true ;
if ( layout_node ( ) - > parent ( ) - > is_anonymous ( ) )
return true ;
return false ;
}
2021-06-07 16:32:24 +01:00
void Node : : serialize_tree_as_json ( JsonObjectSerializer < StringBuilder > & object ) const
{
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " name " , node_name ( ) . view ( ) ) ) ;
MUST ( object . add ( " id " , id ( ) ) ) ;
2021-07-01 12:32:31 -04:00
if ( is_document ( ) ) {
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " type " , " document " ) ) ;
2021-07-01 12:32:31 -04:00
} else if ( is_element ( ) ) {
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " type " , " element " ) ) ;
2021-06-07 16:32:24 +01:00
2021-11-24 19:15:04 +03:00
auto const * element = static_cast < DOM : : Element const * > ( this ) ;
2021-06-07 16:32:24 +01:00
if ( element - > has_attributes ( ) ) {
2022-02-24 20:08:48 +02:00
auto attributes = MUST ( object . add_object ( " attributes " ) ) ;
2021-06-07 16:32:24 +01:00
element - > for_each_attribute ( [ & attributes ] ( auto & name , auto & value ) {
2022-02-24 20:08:48 +02:00
MUST ( attributes . add ( name , value ) ) ;
2021-06-07 16:32:24 +01:00
} ) ;
2022-02-24 20:08:48 +02:00
MUST ( attributes . finish ( ) ) ;
2021-06-07 16:32:24 +01:00
}
2021-11-24 19:15:04 +03:00
if ( element - > is_browsing_context_container ( ) ) {
auto const * container = static_cast < HTML : : BrowsingContextContainer const * > ( element ) ;
if ( auto const * content_document = container - > content_document ( ) ) {
2022-02-24 20:08:48 +02:00
auto children = MUST ( object . add_array ( " children " ) ) ;
JsonObjectSerializer < StringBuilder > content_document_object = MUST ( children . add_object ( ) ) ;
2021-11-24 19:15:04 +03:00
content_document - > serialize_tree_as_json ( content_document_object ) ;
2022-02-24 20:08:48 +02:00
MUST ( content_document_object . finish ( ) ) ;
MUST ( children . finish ( ) ) ;
2021-11-24 19:15:04 +03:00
}
}
2021-06-07 16:32:24 +01:00
} else if ( is_text ( ) ) {
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " type " , " text " ) ) ;
2021-06-07 16:32:24 +01:00
2021-06-29 23:11:09 +02:00
auto text_node = static_cast < DOM : : Text const * > ( this ) ;
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " text " , text_node - > data ( ) ) ) ;
2021-11-02 19:25:15 +01:00
} else if ( is_comment ( ) ) {
2022-02-24 20:08:48 +02:00
MUST ( object . add ( " type " sv , " comment " sv ) ) ;
MUST ( object . add ( " data " sv , static_cast < DOM : : Comment const & > ( * this ) . data ( ) ) ) ;
2021-06-07 16:32:24 +01:00
}
2022-03-20 19:11:03 +01:00
MUST ( ( object . add ( " visible " sv , ! ! layout_node ( ) ) ) ) ;
2021-06-07 16:32:24 +01:00
if ( has_child_nodes ( ) ) {
2022-02-24 20:08:48 +02:00
auto children = MUST ( object . add_array ( " children " ) ) ;
2021-06-07 16:32:24 +01:00
for_each_child ( [ & children ] ( DOM : : Node & child ) {
2021-11-02 19:20:57 +01:00
if ( child . is_uninteresting_whitespace_node ( ) )
return ;
2022-02-24 20:08:48 +02:00
JsonObjectSerializer < StringBuilder > child_object = MUST ( children . add_object ( ) ) ;
2021-06-07 16:32:24 +01:00
child . serialize_tree_as_json ( child_object ) ;
2022-02-24 20:08:48 +02:00
MUST ( child_object . finish ( ) ) ;
2021-06-07 16:32:24 +01:00
} ) ;
2022-03-03 17:50:12 +00:00
// Pseudo-elements don't have DOM nodes,so we have to add them separately.
if ( is_element ( ) ) {
auto const * element = static_cast < DOM : : Element const * > ( this ) ;
element - > serialize_pseudo_elements_as_json ( children ) ;
}
2022-02-24 20:08:48 +02:00
MUST ( children . finish ( ) ) ;
2021-06-07 16:32:24 +01:00
}
}
2022-03-30 22:28:28 +01:00
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-n-script
bool Node : : is_scripting_enabled ( ) const
{
// Scripting is enabled for a node node if node's node document's browsing context is non-null, and scripting is enabled for node's relevant settings object.
2022-03-30 22:42:09 +01:00
return document ( ) . browsing_context ( ) & & const_cast < Document & > ( document ( ) ) . relevant_settings_object ( ) . is_scripting_enabled ( ) ;
2022-03-30 22:28:28 +01:00
}
2021-07-05 03:59:47 +01:00
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-n-noscript
bool Node : : is_scripting_disabled ( ) const
{
2022-03-30 22:28:28 +01:00
// Scripting is disabled for a node when scripting is not enabled, i.e., when its node document's browsing context is null or when scripting is disabled for its relevant settings object.
return ! is_scripting_enabled ( ) ;
2021-07-05 03:59:47 +01:00
}
2021-07-05 05:55:02 +01:00
// https://dom.spec.whatwg.org/#dom-node-contains
bool Node : : contains ( RefPtr < Node > other ) const
{
2022-04-20 00:57:06 +02:00
// The contains(other) method steps are to return true if other is an inclusive descendant of this; otherwise false (including when other is null).
2021-07-05 05:55:02 +01:00
return other & & other - > is_inclusive_descendant_of ( * this ) ;
}
2021-09-02 02:17:13 +01:00
// https://dom.spec.whatwg.org/#concept-shadow-including-descendant
bool Node : : is_shadow_including_descendant_of ( Node const & other ) const
{
2022-04-20 00:57:06 +02:00
// An object A is a shadow-including descendant of an object B,
// if A is a descendant of B,
2021-09-02 02:17:13 +01:00
if ( is_descendant_of ( other ) )
return true ;
2022-04-20 00:57:06 +02:00
// or A’ s root is a shadow root
2021-09-02 02:17:13 +01:00
if ( ! is < ShadowRoot > ( root ( ) ) )
return false ;
2022-04-20 00:57:06 +02:00
// and A’ s root’ s host is a shadow-including inclusive descendant of B.
2021-09-02 19:27:42 +01:00
auto & shadow_root = verify_cast < ShadowRoot > ( root ( ) ) ;
2021-09-02 02:17:13 +01:00
// NOTE: While host is nullable because of inheriting from DocumentFragment, shadow roots always have a host.
2021-09-02 19:27:42 +01:00
return shadow_root . host ( ) - > is_shadow_including_inclusive_descendant_of ( other ) ;
2021-09-02 02:17:13 +01:00
}
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-descendant
bool Node : : is_shadow_including_inclusive_descendant_of ( Node const & other ) const
{
2022-04-20 00:57:06 +02:00
// A shadow-including inclusive descendant is an object or one of its shadow-including descendants.
2021-09-02 02:17:13 +01:00
return & other = = this | | is_shadow_including_descendant_of ( other ) ;
}
// https://dom.spec.whatwg.org/#concept-shadow-including-ancestor
bool Node : : is_shadow_including_ancestor_of ( Node const & other ) const
{
2022-04-20 00:57:06 +02:00
// An object A is a shadow-including ancestor of an object B, if and only if B is a shadow-including descendant of A.
2021-09-02 02:17:13 +01:00
return other . is_shadow_including_descendant_of ( * this ) ;
}
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
bool Node : : is_shadow_including_inclusive_ancestor_of ( Node const & other ) const
{
2022-04-20 00:57:06 +02:00
// A shadow-including inclusive ancestor is an object or one of its shadow-including ancestors.
2021-09-02 02:17:13 +01:00
return other . is_shadow_including_inclusive_descendant_of ( * this ) ;
}
2021-09-06 01:25:58 +01:00
// https://dom.spec.whatwg.org/#concept-node-replace-all
void Node : : replace_all ( RefPtr < Node > node )
{
2022-07-11 16:39:14 +01:00
// 1. Let removedNodes be parent’ s children.
auto removed_nodes = children_as_vector ( ) ;
// 2. Let addedNodes be the empty set.
NonnullRefPtrVector < Node > added_nodes ;
// 3. If node is a DocumentFragment node, then set addedNodes to node’ s children.
if ( node & & is < DocumentFragment > ( * node ) ) {
added_nodes = node - > children_as_vector ( ) ;
}
// 4. Otherwise, if node is non-null, set addedNodes to « node ».
else if ( node ) {
added_nodes . append ( * node ) ;
}
2021-09-06 01:25:58 +01:00
2022-04-20 00:57:06 +02:00
// 5. Remove all parent’ s children, in tree order, with the suppress observers flag set.
2021-09-06 01:25:58 +01:00
remove_all_children ( true ) ;
2022-04-20 00:57:06 +02:00
// 6. If node is non-null, then insert node into parent before null with the suppress observers flag set.
2021-09-06 01:25:58 +01:00
if ( node )
insert_before ( * node , nullptr , true ) ;
2022-07-11 16:39:14 +01:00
// 7. If either addedNodes or removedNodes is not empty, then queue a tree mutation record for parent with addedNodes, removedNodes, null, and null.
if ( ! added_nodes . is_empty ( ) | | ! removed_nodes . is_empty ( ) )
queue_tree_mutation_record ( StaticNodeList : : create ( move ( added_nodes ) ) , StaticNodeList : : create ( move ( removed_nodes ) ) , nullptr , nullptr ) ;
2021-09-06 01:25:58 +01:00
}
// https://dom.spec.whatwg.org/#string-replace-all
void Node : : string_replace_all ( String const & string )
{
2022-04-20 00:57:06 +02:00
// 1. Let node be null.
2021-09-06 01:25:58 +01:00
RefPtr < Node > node ;
2022-04-20 00:57:06 +02:00
// 2. If string is not the empty string, then set node to a new Text node whose data is string and node document is parent’ s node document.
2021-09-06 01:25:58 +01:00
if ( ! string . is_empty ( ) )
node = make_ref_counted < Text > ( document ( ) , string ) ;
2022-04-20 00:57:06 +02:00
// 3. Replace all with node within parent.
2021-09-06 01:25:58 +01:00
replace_all ( node ) ;
}
2021-09-13 22:42:15 +01:00
// https://w3c.github.io/DOM-Parsing/#dfn-fragment-serializing-algorithm
String Node : : serialize_fragment ( /* FIXME: Requires well-formed flag */ ) const
{
2022-04-20 19:48:48 +02:00
// FIXME: 1. Let context document be the value of node's node document.
2021-09-13 22:42:15 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 2. If context document is an HTML document, return an HTML serialization of node.
2021-09-13 22:42:15 +01:00
// (We currently always do this)
2021-09-25 23:15:48 +02:00
return HTML : : HTMLParser : : serialize_html_fragment ( * this ) ;
2021-09-13 22:42:15 +01:00
2022-04-20 19:48:48 +02:00
// FIXME: 3. Otherwise, context document is an XML document; return an XML serialization of node passing the flag require well-formed.
2021-09-13 22:42:15 +01:00
}
2021-09-13 12:49:23 +02:00
// https://dom.spec.whatwg.org/#dom-node-issamenode
bool Node : : is_same_node ( Node const * other_node ) const
{
2022-04-20 00:57:06 +02:00
// The isSameNode(otherNode) method steps are to return true if otherNode is this; otherwise false.
2021-09-13 12:49:23 +02:00
return this = = other_node ;
}
2021-09-13 12:54:24 +02:00
// https://dom.spec.whatwg.org/#dom-node-isequalnode
bool Node : : is_equal_node ( Node const * other_node ) const
{
// The isEqualNode(otherNode) method steps are to return true if otherNode is non-null and this equals otherNode; otherwise false.
if ( ! other_node )
return false ;
// Fast path for testing a node against itself.
if ( this = = other_node )
return true ;
// A node A equals a node B if all of the following conditions are true:
// A and B implement the same interfaces.
if ( node_name ( ) ! = other_node - > node_name ( ) )
return false ;
// The following are equal, switching on the interface A implements:
switch ( node_type ( ) ) {
case ( u16 ) NodeType : : DOCUMENT_TYPE_NODE : {
// Its name, public ID, and system ID.
auto & this_doctype = verify_cast < DocumentType > ( * this ) ;
auto & other_doctype = verify_cast < DocumentType > ( * other_node ) ;
if ( this_doctype . name ( ) ! = other_doctype . name ( )
| | this_doctype . public_id ( ) ! = other_doctype . public_id ( )
| | this_doctype . system_id ( ) ! = other_doctype . system_id ( ) )
return false ;
break ;
}
case ( u16 ) NodeType : : ELEMENT_NODE : {
// Its namespace, namespace prefix, local name, and its attribute list’ s size.
auto & this_element = verify_cast < Element > ( * this ) ;
auto & other_element = verify_cast < Element > ( * other_node ) ;
if ( this_element . namespace_ ( ) ! = other_element . namespace_ ( )
| | this_element . prefix ( ) ! = other_element . prefix ( )
| | this_element . local_name ( ) ! = other_element . local_name ( )
| | this_element . attribute_list_size ( ) ! = other_element . attribute_list_size ( ) )
return false ;
// If A is an element, each attribute in its attribute list has an attribute that equals an attribute in B’ s attribute list.
bool has_same_attributes = true ;
this_element . for_each_attribute ( [ & ] ( auto & name , auto & value ) {
if ( other_element . get_attribute ( name ) ! = value )
has_same_attributes = false ;
} ) ;
if ( ! has_same_attributes )
return false ;
break ;
}
case ( u16 ) NodeType : : COMMENT_NODE :
case ( u16 ) NodeType : : TEXT_NODE : {
// Its data.
auto & this_cdata = verify_cast < CharacterData > ( * this ) ;
auto & other_cdata = verify_cast < CharacterData > ( * other_node ) ;
if ( this_cdata . data ( ) ! = other_cdata . data ( ) )
return false ;
break ;
}
case ( u16 ) NodeType : : PROCESSING_INSTRUCTION_NODE :
case ( u16 ) NodeType : : ATTRIBUTE_NODE :
TODO ( ) ;
default :
break ;
}
// A and B have the same number of children.
size_t this_child_count = child_count ( ) ;
size_t other_child_count = other_node - > child_count ( ) ;
if ( this_child_count ! = other_child_count )
return false ;
// Each child of A equals the child of B at the identical index.
// FIXME: This can be made nicer. child_at_index() is O(n).
for ( size_t i = 0 ; i < this_child_count ; + + i ) {
auto * this_child = child_at_index ( i ) ;
auto * other_child = other_node - > child_at_index ( i ) ;
VERIFY ( this_child ) ;
VERIFY ( other_child ) ;
if ( ! this_child - > is_equal_node ( other_child ) )
return false ;
}
return true ;
}
2021-09-29 22:23:28 +02:00
// https://dom.spec.whatwg.org/#in-a-document-tree
bool Node : : in_a_document_tree ( ) const
{
// An element is in a document tree if its root is a document.
return root ( ) . is_document ( ) ;
}
2021-10-16 03:04:55 +01:00
// https://dom.spec.whatwg.org/#dom-node-getrootnode
NonnullRefPtr < Node > Node : : get_root_node ( GetRootNodeOptions const & options )
{
2022-04-20 00:57:06 +02:00
// The getRootNode(options) method steps are to return this’ s shadow-including root if options["composed"] is true;
2021-10-16 03:04:55 +01:00
if ( options . composed )
return shadow_including_root ( ) ;
2022-04-20 00:57:06 +02:00
// otherwise this’ s root.
2021-10-16 03:04:55 +01:00
return root ( ) ;
}
2022-02-26 08:18:14 +01:00
String Node : : debug_description ( ) const
{
StringBuilder builder ;
builder . append ( node_name ( ) . to_lowercase ( ) ) ;
if ( is_element ( ) ) {
auto & element = static_cast < DOM : : Element const & > ( * this ) ;
if ( auto id = element . get_attribute ( HTML : : AttributeNames : : id ) ; ! id . is_null ( ) )
builder . appendff ( " #{} " , id ) ;
for ( auto const & class_name : element . class_names ( ) )
builder . appendff ( " .{} " , class_name ) ;
}
return builder . to_string ( ) ;
}
2022-01-31 18:05:54 +00:00
// https://dom.spec.whatwg.org/#concept-node-length
size_t Node : : length ( ) const
{
// 1. If node is a DocumentType or Attr node, then return 0.
if ( is_document_type ( ) | | is_attribute ( ) )
return 0 ;
// 2. If node is a CharacterData node, then return node’ s data’ s length.
if ( is_character_data ( ) ) {
auto * character_data_node = verify_cast < CharacterData > ( this ) ;
return character_data_node - > data ( ) . length ( ) ;
}
// 3. Return the number of node’ s children.
return child_count ( ) ;
}
2022-03-10 22:46:35 +01:00
Painting : : Paintable const * Node : : paintable ( ) const
{
if ( ! layout_node ( ) )
return nullptr ;
return layout_node ( ) - > paintable ( ) ;
}
2022-03-10 15:50:57 +01:00
Painting : : PaintableBox const * Node : : paint_box ( ) const
2022-03-09 23:53:41 +01:00
{
if ( ! layout_node ( ) )
return nullptr ;
if ( ! layout_node ( ) - > is_box ( ) )
return nullptr ;
2022-03-10 15:50:16 +01:00
return static_cast < Layout : : Box const & > ( * layout_node ( ) ) . paint_box ( ) ;
2022-03-09 23:53:41 +01:00
}
2022-07-11 16:39:14 +01:00
// https://dom.spec.whatwg.org/#queue-a-mutation-record
void Node : : queue_mutation_record ( FlyString const & type , String attribute_name , String attribute_namespace , String old_value , NonnullRefPtr < NodeList > added_nodes , NonnullRefPtr < NodeList > removed_nodes , Node * previous_sibling , Node * next_sibling )
{
// 1. Let interestedObservers be an empty map.
// mutationObserver -> mappedOldValue
OrderedHashMap < NonnullRefPtr < MutationObserver > , String > interested_observers ;
// 2. Let nodes be the inclusive ancestors of target.
NonnullRefPtrVector < Node > nodes ;
nodes . append ( * this ) ;
for ( auto * parent_node = parent ( ) ; parent_node ; parent_node = parent_node - > parent ( ) )
nodes . append ( * parent_node ) ;
// 3. For each node in nodes, and then for each registered of node’ s registered observer list:
for ( auto & node : nodes ) {
for ( auto & registered_observer : node . m_registered_observer_list ) {
// 1. Let options be registered’ s options.
auto & options = registered_observer . options ;
// 2. If none of the following are true
// - node is not target and options["subtree"] is false
// - type is "attributes" and options["attributes"] either does not exist or is false
// - type is "attributes", options["attributeFilter"] exists, and options["attributeFilter"] does not contain name or namespace is non-null
// - type is "characterData" and options["characterData"] either does not exist or is false
// - type is "childList" and options["childList"] is false
// then:
if ( ! ( & node ! = this & & ! options . subtree )
& & ! ( type = = MutationType : : attributes & & ( ! options . attributes . has_value ( ) | | ! options . attributes . value ( ) ) )
& & ! ( type = = MutationType : : attributes & & options . attribute_filter . has_value ( ) & & ( ! attribute_namespace . is_null ( ) | | ! options . attribute_filter - > contains_slow ( attribute_name ) ) )
& & ! ( type = = MutationType : : characterData & & ( ! options . character_data . has_value ( ) | | ! options . character_data . value ( ) ) )
& & ! ( type = = MutationType : : childList & & ! options . child_list ) ) {
// 1. Let mo be registered’ s observer.
auto mutation_observer = registered_observer . observer ;
// 2. If interestedObservers[mo] does not exist, then set interestedObservers[mo] to null.
if ( ! interested_observers . contains ( mutation_observer ) )
interested_observers . set ( mutation_observer , { } ) ;
// 3. If either type is "attributes" and options["attributeOldValue"] is true, or type is "characterData" and options["characterDataOldValue"] is true, then set interestedObservers[mo] to oldValue.
if ( ( type = = MutationType : : attributes & & options . attribute_old_value . has_value ( ) & & options . attribute_old_value . value ( ) ) | | ( type = = MutationType : : characterData & & options . character_data_old_value . has_value ( ) & & options . character_data_old_value . value ( ) ) )
interested_observers . set ( mutation_observer , old_value ) ;
}
}
}
// 4. For each observer → mappedOldValue of interestedObservers:
for ( auto & interested_observer : interested_observers ) {
// 1. Let record be a new MutationRecord object with its type set to type, target set to target, attributeName set to name, attributeNamespace set to namespace, oldValue set to mappedOldValue,
// addedNodes set to addedNodes, removedNodes set to removedNodes, previousSibling set to previousSibling, and nextSibling set to nextSibling.
auto record = MutationRecord : : create ( type , * this , added_nodes , removed_nodes , previous_sibling , next_sibling , attribute_name , attribute_namespace , /* mappedOldValue */ interested_observer . value ) ;
// 2. Enqueue record to observer’ s record queue.
interested_observer . key - > enqueue_record ( { } , move ( record ) ) ;
}
// 5. Queue a mutation observer microtask.
Bindings : : queue_mutation_observer_microtask ( document ( ) ) ;
}
// https://dom.spec.whatwg.org/#queue-a-tree-mutation-record
void Node : : queue_tree_mutation_record ( NonnullRefPtr < NodeList > added_nodes , NonnullRefPtr < NodeList > removed_nodes , Node * previous_sibling , Node * next_sibling )
{
// 1. Assert: either addedNodes or removedNodes is not empty.
VERIFY ( added_nodes - > length ( ) > 0 | | removed_nodes - > length ( ) > 0 ) ;
// 2. Queue a mutation record of "childList" for target with null, null, null, addedNodes, removedNodes, previousSibling, and nextSibling.
queue_mutation_record ( MutationType : : childList , { } , { } , { } , move ( added_nodes ) , move ( removed_nodes ) , previous_sibling , next_sibling ) ;
}
2020-03-07 10:27:02 +01:00
}