1/*
2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "MediaQueryMatcher.h"
22
23#include "Document.h"
24#include "Frame.h"
25#include "FrameView.h"
26#include "Logging.h"
27#include "MediaList.h"
28#include "MediaQueryEvaluator.h"
29#include "MediaQueryList.h"
30#include "MediaQueryListListener.h"
31#include "MediaQueryParserContext.h"
32#include "NodeRenderStyle.h"
33#include "RenderElement.h"
34#include "StyleResolver.h"
35#include "StyleScope.h"
36#include <wtf/text/TextStream.h>
37
38namespace WebCore {
39
40MediaQueryMatcher::MediaQueryMatcher(Document& document)
41 : m_document(makeWeakPtr(document))
42{
43}
44
45MediaQueryMatcher::~MediaQueryMatcher() = default;
46
47void MediaQueryMatcher::documentDestroyed()
48{
49 m_listeners.clear();
50 m_document = nullptr;
51}
52
53String MediaQueryMatcher::mediaType() const
54{
55 if (!m_document || !m_document->frame() || !m_document->frame()->view())
56 return String();
57
58 return m_document->frame()->view()->mediaType();
59}
60
61std::unique_ptr<RenderStyle> MediaQueryMatcher::documentElementUserAgentStyle() const
62{
63 if (!m_document || !m_document->frame())
64 return nullptr;
65
66 auto* documentElement = m_document->documentElement();
67 if (!documentElement)
68 return nullptr;
69
70 return m_document->styleScope().resolver().styleForElement(*documentElement, m_document->renderStyle(), nullptr, RuleMatchingBehavior::MatchOnlyUserAgentRules).renderStyle;
71}
72
73bool MediaQueryMatcher::evaluate(const MediaQuerySet& media)
74{
75 auto style = documentElementUserAgentStyle();
76 if (!style)
77 return false;
78 return MediaQueryEvaluator { mediaType(), *m_document, style.get() }.evaluate(media);
79}
80
81RefPtr<MediaQueryList> MediaQueryMatcher::matchMedia(const String& query)
82{
83 if (!m_document)
84 return nullptr;
85
86 auto media = MediaQuerySet::create(query, MediaQueryParserContext(*m_document));
87 reportMediaQueryWarningIfNeeded(m_document.get(), media.ptr());
88 bool result = evaluate(media.get());
89 return MediaQueryList::create(*this, WTFMove(media), result);
90}
91
92void MediaQueryMatcher::addListener(Ref<MediaQueryListListener>&& listener, MediaQueryList& query)
93{
94 if (!m_document)
95 return;
96
97 for (auto& existingListener : m_listeners) {
98 if (existingListener.listener.get() == listener.get() && existingListener.query.ptr() == &query)
99 return;
100 }
101
102 m_listeners.append(Listener { WTFMove(listener), query });
103}
104
105void MediaQueryMatcher::removeListener(MediaQueryListListener& listener, MediaQueryList& query)
106{
107 m_listeners.removeFirstMatching([&listener, &query](auto& existingListener) {
108 return existingListener.listener.get() == listener && existingListener.query.ptr() == &query;
109 });
110}
111
112void MediaQueryMatcher::styleResolverChanged()
113{
114 ASSERT(m_document);
115
116 ++m_evaluationRound;
117
118 auto style = documentElementUserAgentStyle();
119 if (!style)
120 return;
121
122 LOG_WITH_STREAM(MediaQueries, stream << "MediaQueryMatcher::styleResolverChanged " << m_document->url());
123
124 MediaQueryEvaluator evaluator { mediaType(), *m_document, style.get() };
125 Vector<Listener> listeners;
126 listeners.reserveInitialCapacity(m_listeners.size());
127 for (auto& listener : m_listeners)
128 listeners.uncheckedAppend({ listener.listener.copyRef(), listener.query.copyRef() });
129 for (auto& listener : listeners) {
130 bool notify;
131 listener.query->evaluate(evaluator, notify);
132 if (notify)
133 listener.listener->handleEvent(listener.query);
134 }
135}
136
137} // namespace WebCore
138