Serenity Operating System
1/*
2 * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
3 * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <LibWeb/Bindings/Intrinsics.h>
9#include <LibWeb/Bindings/MediaQueryListPrototype.h>
10#include <LibWeb/CSS/MediaQueryList.h>
11#include <LibWeb/DOM/Document.h>
12#include <LibWeb/DOM/EventDispatcher.h>
13#include <LibWeb/DOM/IDLEventListener.h>
14#include <LibWeb/HTML/EventHandler.h>
15
16namespace Web::CSS {
17
18WebIDL::ExceptionOr<JS::NonnullGCPtr<MediaQueryList>> MediaQueryList::create(DOM::Document& document, Vector<NonnullRefPtr<MediaQuery>>&& media)
19{
20 return MUST_OR_THROW_OOM(document.heap().allocate<MediaQueryList>(document.realm(), document, move(media)));
21}
22
23MediaQueryList::MediaQueryList(DOM::Document& document, Vector<NonnullRefPtr<MediaQuery>>&& media)
24 : DOM::EventTarget(document.realm())
25 , m_document(document)
26 , m_media(move(media))
27{
28 evaluate();
29}
30
31JS::ThrowCompletionOr<void> MediaQueryList::initialize(JS::Realm& realm)
32{
33 MUST_OR_THROW_OOM(Base::initialize(realm));
34 set_prototype(&Bindings::ensure_web_prototype<Bindings::MediaQueryListPrototype>(realm, "MediaQueryList"));
35
36 return {};
37}
38
39void MediaQueryList::visit_edges(Cell::Visitor& visitor)
40{
41 Base::visit_edges(visitor);
42 visitor.visit(m_document.ptr());
43}
44
45// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-media
46DeprecatedString MediaQueryList::media() const
47{
48 return serialize_a_media_query_list(m_media).release_value_but_fixme_should_propagate_errors().to_deprecated_string();
49}
50
51// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-matches
52bool MediaQueryList::matches() const
53{
54 for (auto& media : m_media) {
55 if (media->matches())
56 return true;
57 }
58 return false;
59}
60
61bool MediaQueryList::evaluate()
62{
63 bool now_matches = false;
64 for (auto& media : m_media) {
65 now_matches = now_matches || media->evaluate(m_document->window());
66 }
67
68 return now_matches;
69}
70
71// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-addlistener
72void MediaQueryList::add_listener(DOM::IDLEventListener* listener)
73{
74 // 1. If listener is null, terminate these steps.
75 if (!listener)
76 return;
77
78 // 2. Append an event listener to the associated list of event listeners with type set to change,
79 // callback set to listener, and capture set to false, unless there already is an event listener
80 // in that list with the same type, callback, and capture.
81 // (NOTE: capture is set to false by default)
82 add_event_listener_without_options(HTML::EventNames::change, *listener);
83}
84
85// https://www.w3.org/TR/cssom-view/#dom-mediaquerylist-removelistener
86void MediaQueryList::remove_listener(DOM::IDLEventListener* listener)
87{
88 // 1. Remove an event listener from the associated list of event listeners, whose type is change, callback is listener, and capture is false.
89 // NOTE: While the spec doesn't technically use remove_event_listener and instead manipulates the list directly, every major engine uses remove_event_listener.
90 // This means if an event listener removes another event listener that comes after it, the removed event listener will not be invoked.
91 remove_event_listener_without_options(HTML::EventNames::change, *listener);
92}
93
94void MediaQueryList::set_onchange(WebIDL::CallbackType* event_handler)
95{
96 set_event_handler_attribute(HTML::EventNames::change, event_handler);
97}
98
99WebIDL::CallbackType* MediaQueryList::onchange()
100{
101 return event_handler_attribute(HTML::EventNames::change);
102}
103
104}