2020-05-27 23:22:42 +02:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2020 - 2022 , Andreas Kling < andreas @ ladybird . org >
2020-05-27 23:22:42 +02:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-05-27 23:22:42 +02:00
*/
# include <LibWeb/DOM/Element.h>
2020-07-28 19:18:23 +02:00
# include <LibWeb/HTML/Parser/ListOfActiveFormattingElements.h>
2020-05-27 23:22:42 +02:00
2020-07-28 18:20:36 +02:00
namespace Web : : HTML {
2020-05-27 23:22:42 +02:00
2025-09-02 03:37:18 +02:00
static constexpr size_t NoahsArkCapacity = 3 ;
2022-03-14 13:21:51 -06:00
ListOfActiveFormattingElements : : ~ ListOfActiveFormattingElements ( ) = default ;
2020-05-27 23:22:42 +02:00
2022-10-17 10:46:11 +02:00
void ListOfActiveFormattingElements : : visit_edges ( JS : : Cell : : Visitor & visitor )
{
for ( auto & entry : m_entries )
visitor . visit ( entry . element ) ;
}
2025-09-02 03:37:18 +02:00
void ListOfActiveFormattingElements : : ensure_noahs_ark_clause ( DOM : : Element & element )
{
Vector < Entry > possible_matches ;
for ( size_t i = m_entries . size ( ) ; i > 0 ; ) {
i - - ;
auto & entry = m_entries [ i ] ;
if ( entry . is_marker ( ) )
break ;
if ( entry . element - > local_name ( ) = = element . local_name ( )
& & entry . element - > namespace_uri ( ) = = element . namespace_uri ( )
& & entry . element - > attribute_list_size ( ) = = element . attribute_list_size ( ) )
possible_matches . append ( entry ) ;
}
if ( possible_matches . size ( ) < NoahsArkCapacity )
return ;
// FIXME: the attributes should be compared as they where created by the parser
element . for_each_attribute ( [ & ] ( auto & name , auto & value ) {
possible_matches . remove_all_matching ( [ & ] ( auto & entry ) {
auto attr = entry . element - > get_attribute ( name ) ;
return ! attr . has_value ( ) | | attr ! = value ;
} ) ;
} ) ;
if ( possible_matches . size ( ) < NoahsArkCapacity )
return ;
remove ( * possible_matches . last ( ) . element ) ;
}
// https://html.spec.whatwg.org/multipage/parsing.html#push-onto-the-list-of-active-formatting-elements
2020-07-26 19:37:56 +02:00
void ListOfActiveFormattingElements : : add ( DOM : : Element & element )
2020-05-27 23:22:42 +02:00
{
2025-09-02 03:37:18 +02:00
// 1. If there are already three elements in the list of active formatting elements after the last marker, if any, or anywhere in the list if there are no markers,
// that have the same tag name, namespace, and attributes as element, then remove the earliest such element from the list of active formatting elements.
// For these purposes, the attributes must be compared as they were when the elements were created by the parser; two elements have the same attributes if all their parsed attributes
// can be paired such that the two attributes in each pair have identical names, namespaces, and values (the order of the attributes does not matter).
ensure_noahs_ark_clause ( element ) ;
// 2. Add element to the list of active formatting elements.
2022-10-17 10:46:11 +02:00
m_entries . append ( { element } ) ;
2020-05-27 23:22:42 +02:00
}
void ListOfActiveFormattingElements : : add_marker ( )
{
2022-10-17 10:46:11 +02:00
m_entries . append ( { nullptr } ) ;
2020-05-27 23:22:42 +02:00
}
2025-08-29 13:02:52 +01:00
bool ListOfActiveFormattingElements : : contains ( DOM : : Element const & element ) const
2020-05-27 23:22:42 +02:00
{
for ( auto & entry : m_entries ) {
2022-08-28 13:42:07 +02:00
if ( entry . element . ptr ( ) = = & element )
2020-05-27 23:22:42 +02:00
return true ;
}
return false ;
}
2023-10-01 20:07:44 +13:00
DOM : : Element * ListOfActiveFormattingElements : : last_element_with_tag_name_before_marker ( FlyString const & tag_name )
2020-05-27 23:22:42 +02:00
{
for ( ssize_t i = m_entries . size ( ) - 1 ; i > = 0 ; - - i ) {
auto & entry = m_entries [ i ] ;
if ( entry . is_marker ( ) )
return nullptr ;
2020-07-23 18:18:13 +02:00
if ( entry . element - > local_name ( ) = = tag_name )
2022-08-28 13:42:07 +02:00
return entry . element . ptr ( ) ;
2020-05-27 23:22:42 +02:00
}
return nullptr ;
}
2020-07-26 19:37:56 +02:00
void ListOfActiveFormattingElements : : remove ( DOM : : Element & element )
2020-05-27 23:22:42 +02:00
{
m_entries . remove_first_matching ( [ & ] ( auto & entry ) {
2022-08-28 13:42:07 +02:00
return entry . element . ptr ( ) = = & element ;
2020-05-27 23:22:42 +02:00
} ) ;
}
2020-05-28 00:27:46 +02:00
void ListOfActiveFormattingElements : : clear_up_to_the_last_marker ( )
{
while ( ! m_entries . is_empty ( ) ) {
auto entry = m_entries . take_last ( ) ;
if ( entry . is_marker ( ) )
break ;
}
}
2022-03-20 02:11:42 +01:00
Optional < size_t > ListOfActiveFormattingElements : : find_index ( DOM : : Element const & element ) const
{
for ( size_t i = 0 ; i < m_entries . size ( ) ; i + + ) {
2022-08-28 13:42:07 +02:00
if ( m_entries [ i ] . element . ptr ( ) = = & element )
2022-03-20 02:11:42 +01:00
return i ;
}
return { } ;
}
void ListOfActiveFormattingElements : : replace ( DOM : : Element & to_remove , DOM : : Element & to_add )
{
for ( size_t i = 0 ; i < m_entries . size ( ) ; i + + ) {
2022-08-28 13:42:07 +02:00
if ( m_entries [ i ] . element . ptr ( ) = = & to_remove )
2024-11-15 04:01:23 +13:00
m_entries [ i ] . element = GC : : make_root ( to_add ) ;
2022-03-20 02:11:42 +01:00
}
}
void ListOfActiveFormattingElements : : insert_at ( size_t index , DOM : : Element & element )
{
2022-10-17 10:46:11 +02:00
m_entries . insert ( index , { element } ) ;
2022-03-20 02:11:42 +01:00
}
2020-05-27 23:22:42 +02:00
}