2021-09-12 16:55:10 +01:00
/*
2022-08-22 18:31:08 +01:00
* Copyright ( c ) 2021 - 2022 , Linus Groh < linusg @ serenityos . org >
2021-10-03 19:39:48 +01:00
* Copyright ( c ) 2021 , Sam Atkins < atkinssj @ serenityos . org >
2021-09-12 16:55:10 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-09-24 16:34:04 -06:00
# include <LibWeb/Bindings/Intrinsics.h>
# include <LibWeb/Bindings/MediaQueryListPrototype.h>
2021-09-12 16:55:10 +01:00
# include <LibWeb/CSS/MediaQueryList.h>
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/EventDispatcher.h>
2022-02-16 20:43:24 +01:00
# include <LibWeb/DOM/IDLEventListener.h>
2021-10-01 01:33:10 +01:00
# include <LibWeb/HTML/EventHandler.h>
2021-09-12 16:55:10 +01:00
namespace Web : : CSS {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( MediaQueryList ) ;
2023-11-19 19:47:52 +01:00
2024-11-15 04:01:23 +13:00
GC : : Ref < MediaQueryList > MediaQueryList : : create ( DOM : : Document & document , Vector < NonnullRefPtr < MediaQuery > > & & media )
2022-08-28 13:42:07 +02:00
{
2024-11-14 05:50:17 +13:00
return document . realm ( ) . create < MediaQueryList > ( document , move ( media ) ) ;
2022-08-28 13:42:07 +02:00
}
2023-03-06 14:17:01 +01:00
MediaQueryList : : MediaQueryList ( DOM : : Document & document , Vector < NonnullRefPtr < MediaQuery > > & & media )
2022-08-28 13:42:07 +02:00
: DOM : : EventTarget ( document . realm ( ) )
2021-09-12 16:55:10 +01:00
, m_document ( document )
, m_media ( move ( media ) )
{
2021-10-03 19:39:48 +01:00
evaluate ( ) ;
2021-09-12 16:55:10 +01:00
}
2023-08-07 08:41:28 +02:00
void MediaQueryList : : initialize ( JS : : Realm & realm )
2023-01-10 06:28:20 -05:00
{
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( MediaQueryList ) ;
2025-04-20 16:22:57 +02:00
Base : : initialize ( realm ) ;
2023-01-10 06:28:20 -05:00
}
2022-08-28 13:42:07 +02:00
void MediaQueryList : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_document ) ;
2022-08-28 13:42:07 +02:00
}
2021-09-12 16:55:10 +01:00
// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-media
2023-12-01 16:54:48 +00:00
String MediaQueryList : : media ( ) const
2021-09-12 16:55:10 +01:00
{
2023-12-01 16:54:48 +00:00
return serialize_a_media_query_list ( m_media ) ;
2021-09-12 16:55:10 +01:00
}
// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-matches
bool MediaQueryList : : matches ( ) const
{
2024-10-03 16:00:48 +10:00
if ( m_media . is_empty ( ) )
return true ;
2025-02-13 14:09:39 +01:00
bool did_match = false ;
for ( auto const & media : m_media ) {
if ( media - > matches ( ) ) {
did_match = true ;
break ;
}
}
2025-02-13 13:02:14 +01:00
// NOTE: If our document is inside a frame, we need to update layout
// since that may cause our frame (and thus viewport) to resize.
if ( auto container_document = m_document - > container_document ( ) ) {
2025-03-05 20:50:05 +01:00
container_document - > update_layout ( DOM : : UpdateLayoutReason : : MediaQueryListMatches ) ;
2025-02-13 13:02:14 +01:00
const_cast < MediaQueryList * > ( this ) - > evaluate ( ) ;
}
2025-02-13 14:09:39 +01:00
bool now_matches = false ;
2021-10-03 19:39:48 +01:00
for ( auto & media : m_media ) {
2025-02-13 14:09:39 +01:00
if ( media - > matches ( ) ) {
now_matches = true ;
break ;
}
2021-10-03 19:39:48 +01:00
}
2024-10-03 16:00:48 +10:00
2025-02-13 14:09:39 +01:00
if ( did_match ! = now_matches )
m_has_changed_state = true ;
return now_matches ;
2021-09-12 16:55:10 +01:00
}
2021-10-03 19:39:48 +01:00
bool MediaQueryList : : evaluate ( )
{
2024-10-03 16:00:48 +10:00
if ( m_media . is_empty ( ) )
return true ;
2021-10-03 19:39:48 +01:00
bool now_matches = false ;
for ( auto & media : m_media ) {
2025-10-07 00:54:19 +13:00
now_matches = now_matches | | media - > evaluate ( m_document ) ;
2021-10-03 19:39:48 +01:00
}
return now_matches ;
}
2021-09-13 00:18:28 +01:00
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener
2024-11-15 04:01:23 +13:00
void MediaQueryList : : add_listener ( GC : : Ptr < DOM : : IDLEventListener > listener )
2021-09-13 00:18:28 +01:00
{
// 1. If listener is null, terminate these steps.
if ( ! listener )
return ;
// 2. Append an event listener to the associated list of event listeners with type set to change,
// callback set to listener, and capture set to false, unless there already is an event listener
// in that list with the same type, callback, and capture.
// (NOTE: capture is set to false by default)
2023-04-09 09:52:27 +02:00
add_event_listener_without_options ( HTML : : EventNames : : change , * listener ) ;
2021-09-13 00:18:28 +01:00
}
// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener
2024-11-15 04:01:23 +13:00
void MediaQueryList : : remove_listener ( GC : : Ptr < DOM : : IDLEventListener > listener )
2021-09-13 00:18:28 +01:00
{
// 1. Remove an event listener from the associated list of event listeners, whose type is change, callback is listener, and capture is false.
// NOTE: While the spec doesn't technically use remove_event_listener and instead manipulates the list directly, every major engine uses remove_event_listener.
// This means if an event listener removes another event listener that comes after it, the removed event listener will not be invoked.
2024-11-13 08:19:53 -05:00
if ( listener )
remove_event_listener_without_options ( HTML : : EventNames : : change , * listener ) ;
2021-09-13 00:18:28 +01:00
}
2022-09-24 16:02:41 +01:00
void MediaQueryList : : set_onchange ( WebIDL : : CallbackType * event_handler )
2021-10-01 01:33:10 +01:00
{
set_event_handler_attribute ( HTML : : EventNames : : change , event_handler ) ;
}
2022-09-24 16:02:41 +01:00
WebIDL : : CallbackType * MediaQueryList : : onchange ( )
2021-10-01 01:33:10 +01:00
{
return event_handler_attribute ( HTML : : EventNames : : change ) ;
}
2021-09-12 16:55:10 +01:00
}