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>
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>
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>
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
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
2021-03-30 12:06:06 -04:00
const HTML : : HTMLElement * Node : : enclosing_html_element_with_attribute ( const FlyString & attribute ) const
{
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
{
2021-09-06 00:21:59 +01:00
if ( is < DocumentFragment > ( this ) | | is < Element > ( this ) )
return descendant_text_content ( ) ;
else if ( is < CharacterData > ( this ) )
return verify_cast < CharacterData > ( this ) - > data ( ) ;
// FIXME: Else if this is an Attr node, return this's value.
return { } ;
}
// https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent%E2%91%A0
void Node : : set_text_content ( String const & content )
{
if ( is < DocumentFragment > ( this ) | | is < Element > ( this ) ) {
string_replace_all ( content ) ;
} else if ( is < CharacterData > ( this ) ) {
// 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.
auto * character_data_node = verify_cast < CharacterData > ( this ) ;
character_data_node - > set_data ( content ) ;
2020-08-17 12:36:00 -04:00
} else {
2021-09-06 00:21:59 +01:00
// FIXME: Else if this is an Attr node, set an existing attribute value with this and the given value.
return ;
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
{
if ( is < Attribute > ( this ) ) {
return verify_cast < Attribute > ( this ) - > value ( ) ;
}
if ( is < CharacterData > ( this ) ) {
return verify_cast < CharacterData > ( this ) - > data ( ) ;
}
return { } ;
}
// https://dom.spec.whatwg.org/#ref-for-dom-node-nodevalue%E2%91%A0
void Node : : set_node_value ( const String & value )
{
if ( is < Attribute > ( this ) ) {
verify_cast < Attribute > ( this ) - > set_value ( value ) ;
} else if ( is < CharacterData > ( this ) ) {
verify_cast < CharacterData > ( this ) - > set_data ( value ) ;
}
// Otherwise: Do nothing.
}
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
{
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
{
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
{
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
}
const Element * Node : : parent_element ( ) const
{
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
{
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 " ) ;
if ( node - > is_host_including_inclusive_ancestor_of ( * this ) )
return DOM : : HierarchyRequestError : : create ( " New node is an ancestor of this node " ) ;
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.
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 " ) ;
if ( ( is < Text > ( * node ) & & is < Document > ( this ) ) | | ( is < DocumentType > ( * node ) & & ! is < Document > ( this ) ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
if ( is < Document > ( this ) ) {
if ( is < DocumentFragment > ( * node ) ) {
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 ) ) {
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 ) ) {
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
{
2021-04-06 19:34:49 +01:00
NonnullRefPtrVector < Node > nodes ;
if ( is < DocumentFragment > ( * node ) )
2021-10-02 20:37:45 +01:00
nodes = verify_cast < DocumentFragment > ( * node ) . children_as_vector ( ) ;
2021-04-06 19:34:49 +01:00
else
nodes . append ( node ) ;
auto count = nodes . size ( ) ;
if ( count = = 0 )
return ;
if ( is < DocumentFragment > ( * node ) ) {
node - > remove_all_children ( true ) ;
// FIXME: Queue a tree mutation record for node with « », nodes, null, and null.
}
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-03-21 20:26:35 +01: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.
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 ) ;
}
// 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.
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
}
// FIXME: Let previousSibling be child’ s previous sibling or parent’ s last child if child is null. (Currently unused so not included)
for ( auto & node_to_insert : nodes ) { // FIXME: In tree order
document ( ) . adopt_node ( node_to_insert ) ;
if ( ! child )
2021-05-03 02:06:11 +01:00
TreeNode < Node > : : append_child ( node_to_insert ) ;
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
// FIXME: If parent is a shadow host and node is a slottable, then assign a slot for node.
// FIXME: 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: Run assign slottables for a tree with node’ s root.
// FIXME: This should be shadow-including.
node_to_insert . for_each_in_inclusive_subtree ( [ & ] ( Node & inclusive_descendant ) {
inclusive_descendant . inserted ( ) ;
if ( inclusive_descendant . is_connected ( ) ) {
// FIXME: If inclusiveDescendant is custom, then enqueue a custom element callback reaction with inclusiveDescendant,
// callback name "connectedCallback", and an empty argument list.
// FIXME: Otherwise, try to upgrade inclusiveDescendant.
}
return IterationDecision : : Continue ;
} ) ;
}
if ( ! suppress_observers ) {
// FIXME: queue a tree mutation record for parent with nodes, « », previousSibling, and child.
}
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-03-22 12:39:16 +00:00
TRY ( ensure_pre_insertion_validity ( node , child ) ) ;
2021-04-06 19:34:49 +01:00
auto reference_child = child ;
if ( reference_child = = node )
reference_child = node - > next_sibling ( ) ;
insert_before ( node , reference_child ) ;
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 )
{
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
{
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
child - > remove ( ) ;
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
{
return pre_insert ( node , nullptr ) ;
}
// https://dom.spec.whatwg.org/#concept-node-remove
void Node : : remove ( bool suppress_observers )
{
auto * parent = TreeNode < Node > : : parent ( ) ;
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-03-09 16:38:44 +01:00
// 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.
document ( ) . for_each_node_iterator ( [ & ] ( NodeIterator & node_iterator ) {
node_iterator . run_pre_removing_steps ( * this ) ;
} ) ;
2021-04-06 19:34:49 +01:00
// FIXME: Let oldPreviousSibling be node’ s previous sibling. (Currently unused so not included)
// FIXME: Let oldNextSibling be node’ s next sibling. (Currently unused so not included)
2022-02-21 22:21:59 +01:00
parent - > TreeNode : : remove_child ( * this ) ;
2021-04-06 19:34:49 +01:00
// FIXME: If node is assigned, then run assign slottables for node’ s assigned slot.
// FIXME: 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: If node has an inclusive descendant that is a slot, then:
// Run assign slottables for a tree with parent’ s root.
// Run assign slottables for a tree with node.
removed_from ( parent ) ;
// FIXME: Let isParentConnected be parent’ s connected. (Currently unused so not included)
// FIXME: If node is custom and isParentConnected is true, then enqueue a custom element callback reaction with node,
// callback name "disconnectedCallback", and an empty argument list.
// FIXME: This should be shadow-including.
for_each_in_subtree ( [ & ] ( Node & descendant ) {
descendant . removed_from ( nullptr ) ;
// FIXME: If descendant is custom and isParentConnected is true, then enqueue a custom element callback reaction with descendant,
// callback name "disconnectedCallback", and an empty argument list.
return IterationDecision : : Continue ;
} ) ;
if ( ! suppress_observers ) {
// FIXME: queue a tree mutation record for parent with « », « node », oldPreviousSibling, and oldNextSibling.
}
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 )
{
// NOTE: This differs slightly from ensure_pre_insertion_validity.
if ( ! is < Document > ( this ) & & ! is < DocumentFragment > ( this ) & & ! is < Element > ( this ) )
return DOM : : HierarchyRequestError : : create ( " Can only insert into a document, document fragment or element " ) ;
if ( node - > is_host_including_inclusive_ancestor_of ( * this ) )
return DOM : : HierarchyRequestError : : create ( " New node is an ancestor of this node " ) ;
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.
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 " ) ;
if ( ( is < Text > ( * node ) & & is < Document > ( this ) ) | | ( is < DocumentType > ( * node ) & & ! is < Document > ( this ) ) )
return DOM : : HierarchyRequestError : : create ( " Invalid node type for insertion " ) ;
if ( is < Document > ( this ) ) {
if ( is < DocumentFragment > ( * node ) ) {
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 ) ) {
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 ) ) {
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 " ) ;
}
}
auto reference_child = child - > next_sibling ( ) ;
if ( reference_child = = node )
reference_child = node - > next_sibling ( ) ;
// FIXME: Let previousSibling be child’ s previous sibling. (Currently unused so not included)
// FIXME: Let removedNodes be the empty set. (Currently unused so not included)
if ( child - > parent ( ) ) {
// FIXME: Set removedNodes to « child ».
child - > remove ( true ) ;
}
// FIXME: Let nodes be node’ s children if node is a DocumentFragment node; otherwise « node ». (Currently unused so not included)
insert_before ( node , reference_child , true ) ;
// FIXME: Queue a tree mutation record for parent with nodes, removedNodes, previousSibling, and referenceChild.
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
{
if ( ! document )
document = m_document ;
RefPtr < Node > copy ;
if ( is < Element > ( this ) ) {
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 */ ) ;
2021-04-14 01:25:10 +02:00
element . for_each_attribute ( [ & ] ( auto & name , auto & value ) {
element_copy - > set_attribute ( name , value ) ;
} ) ;
copy = move ( element_copy ) ;
} else if ( is < Document > ( this ) ) {
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 ( ) ) ;
document_copy - > set_encoding ( document_ - > encoding ( ) ) ;
document_copy - > set_content_type ( document_ - > content_type ( ) ) ;
document_copy - > set_origin ( document_ - > origin ( ) ) ;
document_copy - > set_quirks_mode ( document_ - > mode ( ) ) ;
// FIXME: Set type ("xml" or "html")
copy = move ( document_copy ) ;
} else if ( is < DocumentType > ( this ) ) {
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 ) ) ;
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 ) ;
} else if ( is < Text > ( this ) ) {
2021-06-24 19:53:42 +02:00
auto text = verify_cast < Text > ( this ) ;
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 ) ) {
2021-06-24 19:53:42 +02:00
auto comment = verify_cast < Comment > ( this ) ;
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 ) ) {
2021-06-24 19:53:42 +02:00
auto processing_instruction = verify_cast < ProcessingInstruction > ( this ) ;
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 ) ;
2021-07-05 05:21:52 +01:00
} else if ( is < DocumentFragment > ( this ) ) {
auto document_fragment_copy = adopt_ref ( * new DocumentFragment ( * document ) ) ;
copy = move ( document_fragment_copy ) ;
2021-04-14 01:25:10 +02:00
} else {
dbgln ( " clone_node() not implemented for NodeType {} " , ( u16 ) m_type ) ;
TODO ( ) ;
}
// 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
cloned ( * copy , clone_children ) ;
2021-04-14 01:25:10 +02:00
if ( clone_children ) {
for_each_child ( [ & ] ( auto & child ) {
copy - > append_child ( child . clone_node ( document , true ) ) ;
} ) ;
}
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
{
if ( is < ShadowRoot > ( * this ) )
return NotSupportedError : : create ( " Cannot clone shadow root " ) ;
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
{
if ( layout_node )
m_layout_node = layout_node - > make_weak_ptr ( ) ;
else
m_layout_node = nullptr ;
}
2020-11-21 18:32:39 +00:00
EventTarget * Node : : get_parent ( const Event & )
{
// 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 ,
} ;
if ( this = = other )
return DOCUMENT_POSITION_EQUAL ;
Node * node1 = other . ptr ( ) ;
Node * node2 = this ;
// FIXME: Once LibWeb supports attribute nodes fix to follow the specification.
VERIFY ( node1 - > type ( ) ! = NodeType : : ATTRIBUTE_NODE & & node2 - > type ( ) ! = NodeType : : ATTRIBUTE_NODE ) ;
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 ) ;
if ( node1 - > is_ancestor_of ( * node2 ) )
return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING ;
if ( node2 - > is_ancestor_of ( * node1 ) )
return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING ;
if ( node1 - > is_before ( * node2 ) )
return DOCUMENT_POSITION_PRECEDING ;
else
return DOCUMENT_POSITION_FOLLOWING ;
}
2021-04-06 19:34:49 +01:00
// https://dom.spec.whatwg.org/#concept-tree-host-including-inclusive-ancestor
bool Node : : is_host_including_inclusive_ancestor_of ( const Node & other ) const
{
2022-03-14 12:46:14 +01:00
if ( is_inclusive_ancestor_of ( other ) )
return true ;
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
{
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
}
}
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
{
// FIXME: or when scripting is disabled for its relevant settings object.
return ! document ( ) . browsing_context ( ) ;
}
2021-07-05 05:55:02 +01:00
// https://dom.spec.whatwg.org/#dom-node-contains
bool Node : : contains ( RefPtr < Node > other ) const
{
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
{
if ( is_descendant_of ( other ) )
return true ;
if ( ! is < ShadowRoot > ( root ( ) ) )
return false ;
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
{
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
{
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
{
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 )
{
// FIXME: Let removedNodes be parent’ s children. (Current unused so not included)
// FIXME: Let addedNodes be the empty set. (Currently unused so not included)
// FIXME: If node is a DocumentFragment node, then set addedNodes to node’ s children.
// FIXME: Otherwise, if node is non-null, set addedNodes to « node ».
remove_all_children ( true ) ;
if ( node )
insert_before ( * node , nullptr , true ) ;
// FIXME: If either addedNodes or removedNodes is not empty, then queue a tree mutation record for parent with addedNodes, removedNodes, null, and null.
}
// https://dom.spec.whatwg.org/#string-replace-all
void Node : : string_replace_all ( String const & string )
{
RefPtr < Node > node ;
if ( ! string . is_empty ( ) )
node = make_ref_counted < Text > ( document ( ) , string ) ;
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
{
// FIXME: Let context document be the value of node's node document.
// FIXME: If context document is an HTML document, return an HTML serialization of node.
// (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
// FIXME: Otherwise, context document is an XML document; return an XML serialization of node passing the flag require well-formed.
}
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
{
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 )
{
// The getRootNode(options) method steps are to return this’ s shadow-including root if options["composed"] is true; otherwise this’ s root.
if ( options . composed )
return shadow_including_root ( ) ;
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
}
2020-03-07 10:27:02 +01:00
}