mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-06-19 00:01:59 +00:00
`<media-feature>` and the upcoming `<size-feature>` from `@container`, share the same syntax and almost all of their behaviour. To avoid a lot of duplication, pull as much as possible into a FeatureQuery template class that they will both inherit from. MediaFeatureValue is renamed FeatureValue as it's also shared by both. No behaviour change.
172 lines
5.2 KiB
C++
172 lines
5.2 KiB
C++
/*
|
|
* Copyright (c) 2021-2026, Sam Atkins <sam@ladybird.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibWeb/CSS/MediaQuery.h>
|
|
#include <LibWeb/CSS/Serialize.h>
|
|
#include <LibWeb/CSS/StyleComputer.h>
|
|
#include <LibWeb/DOM/Document.h>
|
|
#include <LibWeb/Dump.h>
|
|
#include <LibWeb/HTML/Window.h>
|
|
|
|
namespace Web::CSS {
|
|
|
|
NonnullRefPtr<MediaQuery> MediaQuery::create_not_all()
|
|
{
|
|
auto media_query = new MediaQuery;
|
|
media_query->m_negated = true;
|
|
media_query->m_media_type = {
|
|
.name = "all"_fly_string,
|
|
.known_type = KnownMediaType::All,
|
|
};
|
|
|
|
return adopt_ref(*media_query);
|
|
}
|
|
|
|
StringView MediaFeature::serialize_feature_id(MediaFeatureID id)
|
|
{
|
|
return string_from_media_feature_id(id);
|
|
}
|
|
|
|
bool MediaFeature::keyword_is_falsey(MediaFeatureID id, Keyword keyword)
|
|
{
|
|
return media_feature_keyword_is_falsey(id, keyword);
|
|
}
|
|
|
|
MatchResult MediaFeature::evaluate(BooleanExpressionEvaluationContext const& context) const
|
|
{
|
|
auto const& document = context.document;
|
|
VERIFY(document);
|
|
|
|
// FIXME: In some cases (e.g. when parsing HTML using DOMParser::parse_from_string()) a document may not be associated with a window -
|
|
// for now we just return false but perhaps there are some media queries we should still attempt to resolve.
|
|
if (!document->window())
|
|
return MatchResult::False;
|
|
|
|
auto queried_value = document->window()->query_media_feature(id());
|
|
if (!queried_value.has_value())
|
|
return MatchResult::False;
|
|
|
|
ComputationContext computation_context {
|
|
.length_resolution_context = Length::ResolutionContext::for_document(*document),
|
|
};
|
|
return evaluate_internal(queried_value.value(), computation_context);
|
|
}
|
|
|
|
void MediaFeature::dump(StringBuilder& builder, int indent_levels) const
|
|
{
|
|
indent(builder, indent_levels);
|
|
builder.appendff("MediaFeature: {}\n", to_string());
|
|
}
|
|
|
|
String MediaQuery::to_string() const
|
|
{
|
|
StringBuilder builder;
|
|
|
|
if (m_negated)
|
|
builder.append("not "sv);
|
|
|
|
if (m_negated || m_media_type.known_type != KnownMediaType::All || !m_media_condition) {
|
|
if (m_media_type.known_type.has_value()) {
|
|
builder.append(CSS::to_string(m_media_type.known_type.value()));
|
|
} else {
|
|
builder.append(serialize_an_identifier(m_media_type.name.to_ascii_lowercase()));
|
|
}
|
|
if (m_media_condition)
|
|
builder.append(" and "sv);
|
|
}
|
|
|
|
if (m_media_condition) {
|
|
builder.append(m_media_condition->to_string());
|
|
}
|
|
|
|
return MUST(builder.to_string());
|
|
}
|
|
|
|
bool MediaQuery::evaluate(DOM::Document const& document)
|
|
{
|
|
auto matches_media = [](MediaType const& media) -> MatchResult {
|
|
if (!media.known_type.has_value())
|
|
return MatchResult::False;
|
|
switch (media.known_type.value()) {
|
|
case KnownMediaType::All:
|
|
return MatchResult::True;
|
|
case KnownMediaType::Print:
|
|
// FIXME: Enable for printing, when we have printing!
|
|
return MatchResult::False;
|
|
case KnownMediaType::Screen:
|
|
// FIXME: Disable for printing, when we have printing!
|
|
return MatchResult::True;
|
|
}
|
|
VERIFY_NOT_REACHED();
|
|
};
|
|
|
|
MatchResult result = matches_media(m_media_type);
|
|
|
|
if ((result != MatchResult::False) && m_media_condition)
|
|
result = result && m_media_condition->evaluate({ .document = document });
|
|
|
|
if (m_negated)
|
|
result = negate(result);
|
|
|
|
m_matches = result == MatchResult::True;
|
|
return m_matches;
|
|
}
|
|
|
|
void MediaQuery::dump(StringBuilder& builder, int indent_levels) const
|
|
{
|
|
dump_indent(builder, indent_levels);
|
|
builder.appendff("Media condition: (matches = {})\n", m_matches);
|
|
|
|
dump_indent(builder, indent_levels + 1);
|
|
builder.appendff("Negated: {}\n", m_negated);
|
|
|
|
dump_indent(builder, indent_levels + 1);
|
|
builder.appendff("Type: {}\n", m_media_type.name);
|
|
|
|
if (m_media_condition) {
|
|
dump_indent(builder, indent_levels + 1);
|
|
builder.append("Condition:\n"sv);
|
|
m_media_condition->dump(builder, indent_levels + 2);
|
|
}
|
|
}
|
|
|
|
// https://www.w3.org/TR/cssom-1/#serialize-a-media-query-list
|
|
String serialize_a_media_query_list(Vector<NonnullRefPtr<MediaQuery>> const& media_queries)
|
|
{
|
|
// 1. If the media query list is empty, then return the empty string.
|
|
if (media_queries.is_empty())
|
|
return String {};
|
|
|
|
// 2. Serialize each media query in the list of media queries, in the same order as they
|
|
// appear in the media query list, and then serialize the list.
|
|
return MUST(String::join(", "sv, media_queries));
|
|
}
|
|
|
|
Optional<MediaQuery::KnownMediaType> media_type_from_string(StringView name)
|
|
{
|
|
if (name.equals_ignoring_ascii_case("all"sv))
|
|
return MediaQuery::KnownMediaType::All;
|
|
if (name.equals_ignoring_ascii_case("print"sv))
|
|
return MediaQuery::KnownMediaType::Print;
|
|
if (name.equals_ignoring_ascii_case("screen"sv))
|
|
return MediaQuery::KnownMediaType::Screen;
|
|
return {};
|
|
}
|
|
|
|
StringView to_string(MediaQuery::KnownMediaType media_type)
|
|
{
|
|
switch (media_type) {
|
|
case MediaQuery::KnownMediaType::All:
|
|
return "all"sv;
|
|
case MediaQuery::KnownMediaType::Print:
|
|
return "print"sv;
|
|
case MediaQuery::KnownMediaType::Screen:
|
|
return "screen"sv;
|
|
}
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
}
|