diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2021-10-03 19:39:48 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-10-05 18:51:39 +0200 |
commit | 1c829e0417ede6ae70cde68269e25afa363df48f (patch) | |
tree | 694f943f69bb77126192b06784ebe41fe57f01bc /Userland | |
parent | f354fd72f1518b250f7e1de6f1ab8f61da557396 (diff) | |
download | serenity-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.cpp | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/MediaList.h | 3 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/MediaQuery.cpp | 117 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/MediaQuery.h | 7 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/MediaQueryList.cpp | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/MediaQueryList.h | 3 |
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(); } |