summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@serenityos.org>2021-10-03 19:39:48 +0100
committerAndreas Kling <kling@serenityos.org>2021-10-05 18:51:39 +0200
commit1c829e0417ede6ae70cde68269e25afa363df48f (patch)
tree694f943f69bb77126192b06784ebe41fe57f01bc /Userland
parentf354fd72f1518b250f7e1de6f1ab8f61da557396 (diff)
downloadserenity-1c829e0417ede6ae70cde68269e25afa363df48f.zip
LibWeb: Implement MediaQuery matching :^)
Currently, `evaluate()` recalculates whether the MediaQuery matches or not, and stores it in `m_matches`, which users can query using `matches()`. This allows us to know when the match-state changes, which is required to fire MediaQueryList's change event.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaList.cpp17
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaList.h3
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQuery.cpp117
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQuery.h7
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp17
-rw-r--r--Userland/Libraries/LibWeb/CSS/MediaQueryList.h3
6 files changed, 163 insertions, 1 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/MediaList.cpp b/Userland/Libraries/LibWeb/CSS/MediaList.cpp
index aa8097f256..b723800519 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaList.cpp
+++ b/Userland/Libraries/LibWeb/CSS/MediaList.cpp
@@ -67,4 +67,21 @@ void MediaList::delete_medium(String medium)
// FIXME: If nothing was removed, then throw a NotFoundError exception.
}
+bool MediaList::evaluate(DOM::Window const& window)
+{
+ for (auto& media : m_media)
+ media.evaluate(window);
+
+ return matches();
+}
+
+bool MediaList::matches() const
+{
+ for (auto& media : m_media) {
+ if (media.matches())
+ return true;
+ }
+ return false;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/CSS/MediaList.h b/Userland/Libraries/LibWeb/CSS/MediaList.h
index 4586f731bb..d264654316 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaList.h
+++ b/Userland/Libraries/LibWeb/CSS/MediaList.h
@@ -29,6 +29,9 @@ public:
void append_medium(String);
void delete_medium(String);
+ bool evaluate(DOM::Window const&);
+ bool matches() const;
+
private:
explicit MediaList(NonnullRefPtrVector<MediaQuery>&&);
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp b/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp
index 507d28a48a..83effe782f 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp
+++ b/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp
@@ -5,6 +5,8 @@
*/
#include <LibWeb/CSS/MediaQuery.h>
+#include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/Window.h>
namespace Web::CSS {
@@ -33,6 +35,59 @@ String MediaQuery::MediaFeature::to_string() const
VERIFY_NOT_REACHED();
}
+bool MediaQuery::MediaFeature::evaluate(DOM::Window const& window) const
+{
+ auto queried_value = window.query_media_feature(name);
+ if (!queried_value)
+ return false;
+
+ switch (type) {
+ case Type::IsTrue:
+ if (queried_value->has_number())
+ return queried_value->to_number() != 0;
+ if (queried_value->has_length())
+ return queried_value->to_length().raw_value() != 0;
+ if (queried_value->has_identifier())
+ return queried_value->to_identifier() != ValueID::None;
+ return false;
+
+ case Type::ExactValue:
+ return queried_value->equals(*value);
+
+ case Type::MinValue:
+ if (queried_value->has_number() && value->has_number())
+ return queried_value->to_number() >= value->to_number();
+ if (queried_value->has_length() && value->has_length()) {
+ auto queried_length = queried_value->to_length();
+ auto value_length = value->to_length();
+ // FIXME: We should be checking that lengths are valid during parsing
+ if (!value_length.is_absolute()) {
+ dbgln("Media feature was given a non-absolute length, which is invalid! {}", value_length.to_string());
+ return false;
+ }
+ return queried_length.absolute_length_to_px() >= value_length.absolute_length_to_px();
+ }
+ return false;
+
+ case Type::MaxValue:
+ if (queried_value->has_number() && value->has_number())
+ return queried_value->to_number() <= value->to_number();
+ if (queried_value->has_length() && value->has_length()) {
+ auto queried_length = queried_value->to_length();
+ auto value_length = value->to_length();
+ // FIXME: We should be checking that lengths are valid during parsing
+ if (!value_length.is_absolute()) {
+ dbgln("Media feature was given a non-absolute length, which is invalid! {}", value_length.to_string());
+ return false;
+ }
+ return queried_length.absolute_length_to_px() <= value_length.absolute_length_to_px();
+ }
+ return false;
+ }
+
+ VERIFY_NOT_REACHED();
+}
+
String MediaQuery::MediaCondition::to_string() const
{
StringBuilder builder;
@@ -56,6 +111,33 @@ String MediaQuery::MediaCondition::to_string() const
return builder.to_string();
}
+bool MediaQuery::MediaCondition::evaluate(DOM::Window const& window) const
+{
+ switch (type) {
+ case Type::Single:
+ return feature.evaluate(window);
+
+ case Type::Not:
+ return !conditions.first().evaluate(window);
+
+ case Type::And:
+ for (auto& child : conditions) {
+ if (!child.evaluate(window))
+ return false;
+ }
+ return true;
+
+ case Type::Or:
+ for (auto& child : conditions) {
+ if (child.evaluate(window))
+ return true;
+ }
+ return false;
+ }
+
+ VERIFY_NOT_REACHED();
+}
+
String MediaQuery::to_string() const
{
StringBuilder builder;
@@ -110,4 +192,39 @@ String MediaQuery::to_string() const
return builder.to_string();
}
+bool MediaQuery::evaluate(DOM::Window const& window)
+{
+ auto matches_media = [](MediaType media) -> bool {
+ switch (media) {
+ case MediaType::All:
+ return true;
+ case MediaType::Print:
+ // FIXME: Enable for printing, when we have printing!
+ return false;
+ case MediaType::Screen:
+ // FIXME: Disable for printing, when we have printing!
+ return true;
+ // Deprecated, must never match:
+ case MediaType::TTY:
+ case MediaType::TV:
+ case MediaType::Projection:
+ case MediaType::Handheld:
+ case MediaType::Braille:
+ case MediaType::Embossed:
+ case MediaType::Aural:
+ case MediaType::Speech:
+ return false;
+ }
+ VERIFY_NOT_REACHED();
+ };
+
+ bool result = matches_media(m_media_type);
+
+ if (result && m_media_condition)
+ result = m_media_condition->evaluate(window);
+
+ m_matches = m_negated ? !result : result;
+ return m_matches;
+}
+
}
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQuery.h b/Userland/Libraries/LibWeb/CSS/MediaQuery.h
index 97775333c3..9321525227 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQuery.h
+++ b/Userland/Libraries/LibWeb/CSS/MediaQuery.h
@@ -53,6 +53,7 @@ public:
FlyString name;
RefPtr<StyleValue> value { nullptr };
+ bool evaluate(DOM::Window const&) const;
String to_string() const;
};
@@ -69,12 +70,15 @@ public:
MediaFeature feature;
NonnullOwnPtrVector<MediaCondition> conditions;
+ bool evaluate(DOM::Window const&) const;
String to_string() const;
};
static NonnullRefPtr<MediaQuery> create_not_all();
static NonnullRefPtr<MediaQuery> create() { return adopt_ref(*new MediaQuery); }
+ bool matches() const { return m_matches; }
+ bool evaluate(DOM::Window const&);
String to_string() const;
private:
@@ -84,6 +88,9 @@ private:
bool m_negated { false };
MediaType m_media_type { MediaType::All };
OwnPtr<MediaCondition> m_media_condition { nullptr };
+
+ // Cached value, updated by evaluate()
+ bool m_matches { false };
};
}
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp b/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
index d3295c3075..f581a96e96 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
+++ b/Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
+ * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@@ -18,6 +19,7 @@ MediaQueryList::MediaQueryList(DOM::Document& document, NonnullRefPtrVector<Medi
, m_document(document)
, m_media(move(media))
{
+ evaluate();
}
MediaQueryList::~MediaQueryList()
@@ -35,10 +37,23 @@ String MediaQueryList::media() const
// https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-matches
bool MediaQueryList::matches() const
{
- // TODO: Implement me :^)
+ for (auto& media : m_media) {
+ if (media.matches())
+ return true;
+ }
return false;
}
+bool MediaQueryList::evaluate()
+{
+ bool now_matches = false;
+ for (auto& media : m_media) {
+ now_matches = now_matches || media.evaluate(m_document.window());
+ }
+
+ return now_matches;
+}
+
JS::Object* MediaQueryList::create_wrapper(JS::GlobalObject& global_object)
{
return wrap(global_object, *this);
diff --git a/Userland/Libraries/LibWeb/CSS/MediaQueryList.h b/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
index 45c2caf099..ced43fe3d2 100644
--- a/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
+++ b/Userland/Libraries/LibWeb/CSS/MediaQueryList.h
@@ -8,6 +8,7 @@
#include <AK/Forward.h>
#include <AK/RefCounted.h>
+#include <AK/Weakable.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/CSS/MediaQuery.h>
#include <LibWeb/DOM/EventTarget.h>
@@ -18,6 +19,7 @@ namespace Web::CSS {
// 4.2. The MediaQueryList Interface, https://drafts.csswg.org/cssom-view/#the-mediaquerylist-interface
class MediaQueryList final
: public RefCounted<MediaQueryList>
+ , public Weakable<MediaQueryList>
, public DOM::EventTarget
, public Bindings::Wrappable {
@@ -36,6 +38,7 @@ public:
String media() const;
bool matches() const;
+ bool evaluate();
// ^EventTarget
virtual void ref_event_target() override { ref(); }