2022-07-11 16:37:51 +01:00
/*
* Copyright ( c ) 2022 , Luke Wilde < lukew @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-09-25 16:15:49 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
2022-07-11 16:37:51 +01:00
# include <LibWeb/Bindings/MainThreadVM.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/MutationObserverPrototype.h>
2022-07-11 16:37:51 +01:00
# include <LibWeb/DOM/MutationObserver.h>
# include <LibWeb/DOM/Node.h>
namespace Web : : DOM {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( MutationObserver ) ;
GC_DEFINE_ALLOCATOR ( TransientRegisteredObserver ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < MutationObserver > > MutationObserver : : construct_impl ( JS : : Realm & realm , GC : : Ptr < WebIDL : : CallbackType > callback )
2022-09-01 17:59:48 +02:00
{
2024-11-14 05:50:17 +13:00
return realm . create < MutationObserver > ( realm , callback ) ;
2022-09-01 17:59:48 +02:00
}
2022-07-11 16:37:51 +01:00
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
2024-11-15 04:01:23 +13:00
MutationObserver : : MutationObserver ( JS : : Realm & realm , GC : : Ptr < WebIDL : : CallbackType > callback )
2022-09-25 16:15:49 -06:00
: PlatformObject ( realm )
2022-09-01 17:59:48 +02:00
, m_callback ( move ( callback ) )
2022-07-11 16:37:51 +01:00
{
2022-09-01 17:59:48 +02:00
2022-07-11 16:37:51 +01:00
// 1. Set this’ s callback to callback.
// 2. Append this to this’ s relevant agent’ s mutation observers.
2022-09-25 16:15:49 -06:00
auto * agent_custom_data = verify_cast < Bindings : : WebEngineCustomData > ( realm . vm ( ) . custom_data ( ) ) ;
2022-07-11 16:37:51 +01:00
agent_custom_data - > mutation_observers . append ( * this ) ;
}
2023-09-27 14:58:14 +02:00
MutationObserver : : ~ MutationObserver ( )
{
auto * agent_custom_data = verify_cast < Bindings : : WebEngineCustomData > ( vm ( ) . custom_data ( ) ) ;
agent_custom_data - > mutation_observers . remove_all_matching ( [ this ] ( auto & observer ) {
return observer . ptr ( ) = = this ;
} ) ;
}
2022-09-01 17:59:48 +02:00
2023-08-07 08:41:28 +02:00
void MutationObserver : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( MutationObserver ) ;
2023-01-10 06:28:20 -05:00
}
2022-09-01 17:59:48 +02:00
void MutationObserver : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_callback ) ;
2024-04-15 13:58:21 +02:00
visitor . visit ( m_record_queue ) ;
2022-09-01 17:59:48 +02:00
}
2022-07-11 16:37:51 +01:00
// https://dom.spec.whatwg.org/#dom-mutationobserver-observe
2022-09-25 17:03:42 +01:00
WebIDL : : ExceptionOr < void > MutationObserver : : observe ( Node & target , MutationObserverInit options )
2022-07-11 16:37:51 +01:00
{
// 1. If either options["attributeOldValue"] or options["attributeFilter"] exists, and options["attributes"] does not exist, then set options["attributes"] to true.
if ( ( options . attribute_old_value . has_value ( ) | | options . attribute_filter . has_value ( ) ) & & ! options . attributes . has_value ( ) )
options . attributes = true ;
// 2. If options["characterDataOldValue"] exists and options["characterData"] does not exist, then set options["characterData"] to true.
if ( options . character_data_old_value . has_value ( ) & & ! options . character_data . has_value ( ) )
options . character_data = true ;
// 3. If none of options["childList"], options["attributes"], and options["characterData"] is true, then throw a TypeError.
if ( ! options . child_list & & ( ! options . attributes . has_value ( ) | | ! options . attributes . value ( ) ) & & ( ! options . character_data . has_value ( ) | | ! options . character_data . value ( ) ) )
2022-10-27 23:18:16 +01:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Options must have one of childList, attributes or characterData set to true. " sv } ;
2022-07-11 16:37:51 +01:00
// 4. If options["attributeOldValue"] is true and options["attributes"] is false, then throw a TypeError.
// NOTE: If attributeOldValue is present, attributes will be present because of step 1.
if ( options . attribute_old_value . has_value ( ) & & options . attribute_old_value . value ( ) & & ! options . attributes . value ( ) )
2022-10-27 23:18:16 +01:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " attributes must be true if attributeOldValue is true. " sv } ;
2022-07-11 16:37:51 +01:00
// 5. If options["attributeFilter"] is present and options["attributes"] is false, then throw a TypeError.
// NOTE: If attributeFilter is present, attributes will be present because of step 1.
if ( options . attribute_filter . has_value ( ) & & ! options . attributes . value ( ) )
2022-10-27 23:18:16 +01:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " attributes must be true if attributeFilter is present. " sv } ;
2022-07-11 16:37:51 +01:00
// 6. If options["characterDataOldValue"] is true and options["characterData"] is false, then throw a TypeError.
// NOTE: If characterDataOldValue is present, characterData will be present because of step 2.
if ( options . character_data_old_value . has_value ( ) & & options . character_data_old_value . value ( ) & & ! options . character_data . value ( ) )
2022-10-27 23:18:16 +01:00
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " characterData must be true if characterDataOldValue is true. " sv } ;
2022-07-11 16:37:51 +01:00
// 7. For each registered of target’ s registered observer list, if registered’ s observer is this:
bool updated_existing_observer = false ;
2023-11-18 11:22:51 +01:00
if ( target . registered_observer_list ( ) ) {
for ( auto & registered_observer : * target . registered_observer_list ( ) ) {
if ( registered_observer - > observer ( ) . ptr ( ) ! = this )
continue ;
2022-07-11 16:37:51 +01:00
2023-11-18 11:22:51 +01:00
updated_existing_observer = true ;
2022-07-11 16:37:51 +01:00
2023-11-18 11:22:51 +01:00
// 1. For each node of this’ s node list, remove all transient registered observers whose source is registered from node’ s registered observer list.
for ( auto & node : m_node_list ) {
// FIXME: Is this correct?
if ( node . is_null ( ) )
continue ;
2022-07-11 16:37:51 +01:00
2023-11-18 11:22:51 +01:00
if ( node - > registered_observer_list ( ) ) {
node - > registered_observer_list ( ) - > remove_all_matching ( [ & registered_observer ] ( RegisteredObserver & observer ) {
return is < TransientRegisteredObserver > ( observer ) & & verify_cast < TransientRegisteredObserver > ( observer ) . source ( ) . ptr ( ) = = registered_observer ;
} ) ;
}
}
2022-07-11 16:37:51 +01:00
2023-11-18 11:22:51 +01:00
// 2. Set registered’ s options to options.
registered_observer - > set_options ( options ) ;
break ;
}
2022-07-11 16:37:51 +01:00
}
// 8. Otherwise:
if ( ! updated_existing_observer ) {
// 1. Append a new registered observer whose observer is this and options is options to target’ s registered observer list.
auto new_registered_observer = RegisteredObserver : : create ( * this , options ) ;
target . add_registered_observer ( new_registered_observer ) ;
// 2. Append target to this’ s node list.
2022-08-28 13:42:07 +02:00
m_node_list . append ( target . make_weak_ptr < Node > ( ) ) ;
2022-07-11 16:37:51 +01:00
}
return { } ;
}
// https://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
void MutationObserver : : disconnect ( )
{
// 1. For each node of this’ s node list, remove any registered observer from node’ s registered observer list for which this is the observer.
for ( auto & node : m_node_list ) {
// FIXME: Is this correct?
if ( node . is_null ( ) )
continue ;
2023-11-18 11:22:51 +01:00
if ( node - > registered_observer_list ( ) ) {
node - > registered_observer_list ( ) - > remove_all_matching ( [ this ] ( RegisteredObserver & registered_observer ) {
return registered_observer . observer ( ) . ptr ( ) = = this ;
} ) ;
}
2022-07-11 16:37:51 +01:00
}
// 2. Empty this’ s record queue.
m_record_queue . clear ( ) ;
}
// https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < MutationRecord > > MutationObserver : : take_records ( )
2022-07-11 16:37:51 +01:00
{
// 1. Let records be a clone of this’ s record queue.
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < MutationRecord > > records ;
2022-09-01 17:59:48 +02:00
for ( auto & record : m_record_queue )
records . append ( * record ) ;
2022-07-11 16:37:51 +01:00
// 2. Empty this’ s record queue.
m_record_queue . clear ( ) ;
// 3. Return records.
return records ;
}
2024-11-15 04:01:23 +13:00
GC : : Ref < RegisteredObserver > RegisteredObserver : : create ( MutationObserver & observer , MutationObserverInit const & options )
2022-09-01 17:59:48 +02:00
{
2024-11-14 06:13:46 +13:00
return observer . heap ( ) . allocate < RegisteredObserver > ( observer , options ) ;
2022-09-01 17:59:48 +02:00
}
RegisteredObserver : : RegisteredObserver ( MutationObserver & observer , MutationObserverInit const & options )
: m_observer ( observer )
, m_options ( options )
{
}
RegisteredObserver : : ~ RegisteredObserver ( ) = default ;
void RegisteredObserver : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_observer ) ;
2022-09-01 17:59:48 +02:00
}
2024-11-15 04:01:23 +13:00
GC : : Ref < TransientRegisteredObserver > TransientRegisteredObserver : : create ( MutationObserver & observer , MutationObserverInit const & options , RegisteredObserver & source )
2022-09-01 17:59:48 +02:00
{
2024-11-14 06:13:46 +13:00
return observer . heap ( ) . allocate < TransientRegisteredObserver > ( observer , options , source ) ;
2022-09-01 17:59:48 +02:00
}
TransientRegisteredObserver : : TransientRegisteredObserver ( MutationObserver & observer , MutationObserverInit const & options , RegisteredObserver & source )
: RegisteredObserver ( observer , options )
, m_source ( source )
{
}
TransientRegisteredObserver : : ~ TransientRegisteredObserver ( ) = default ;
void TransientRegisteredObserver : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_source ) ;
2022-09-01 17:59:48 +02:00
}
2022-07-11 16:37:51 +01:00
}