mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-17 05:29:56 +00:00
LibWeb: Use document's viewport when resolving lengths in media queries
Previously we would always use the window's viewport which was incorrect if we were within an iframe. This is likely applicable to all uses of `Length::ResolutionContext::for_window`.
This commit is contained in:
parent
c33be71df9
commit
05c336ea4e
Notes:
github-actions[bot]
2025-10-07 09:34:36 +00:00
Author: https://github.com/Calme1709
Commit: 05c336ea4e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6412
Reviewed-by: https://github.com/AtkinsSJ ✅
23 changed files with 198 additions and 83 deletions
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
bool BooleanExpression::evaluate_to_boolean(HTML::Window const* window) const
|
bool BooleanExpression::evaluate_to_boolean(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
return evaluate(window) == MatchResult::True;
|
return evaluate(document) == MatchResult::True;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BooleanExpression::indent(StringBuilder& builder, int levels)
|
void BooleanExpression::indent(StringBuilder& builder, int levels)
|
||||||
|
@ -25,11 +25,11 @@ void GeneralEnclosed::dump(StringBuilder& builder, int indent_levels) const
|
||||||
builder.appendff("GeneralEnclosed: {}\n", to_string());
|
builder.appendff("GeneralEnclosed: {}\n", to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult BooleanNotExpression::evaluate(HTML::Window const* window) const
|
MatchResult BooleanNotExpression::evaluate(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
||||||
// `not test` evaluates to true if its contained test is false, false if it’s true, and unknown if it’s unknown.
|
// `not test` evaluates to true if its contained test is false, false if it’s true, and unknown if it’s unknown.
|
||||||
switch (m_child->evaluate(window)) {
|
switch (m_child->evaluate(document)) {
|
||||||
case MatchResult::False:
|
case MatchResult::False:
|
||||||
return MatchResult::True;
|
return MatchResult::True;
|
||||||
case MatchResult::True:
|
case MatchResult::True:
|
||||||
|
@ -52,9 +52,9 @@ void BooleanNotExpression::dump(StringBuilder& builder, int indent_levels) const
|
||||||
m_child->dump(builder, indent_levels + 1);
|
m_child->dump(builder, indent_levels + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult BooleanExpressionInParens::evaluate(HTML::Window const* window) const
|
MatchResult BooleanExpressionInParens::evaluate(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
return m_child->evaluate(window);
|
return m_child->evaluate(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
String BooleanExpressionInParens::to_string() const
|
String BooleanExpressionInParens::to_string() const
|
||||||
|
@ -71,14 +71,14 @@ void BooleanExpressionInParens::dump(StringBuilder& builder, int indent_levels)
|
||||||
builder.append(")\n"sv);
|
builder.append(")\n"sv);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult BooleanAndExpression::evaluate(HTML::Window const* window) const
|
MatchResult BooleanAndExpression::evaluate(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
||||||
// Multiple tests connected with `and` evaluate to true if all of those tests are true, false if any of them are
|
// Multiple tests connected with `and` evaluate to true if all of those tests are true, false if any of them are
|
||||||
// false, and unknown otherwise (i.e. if at least one unknown, but no false).
|
// false, and unknown otherwise (i.e. if at least one unknown, but no false).
|
||||||
size_t true_results = 0;
|
size_t true_results = 0;
|
||||||
for (auto const& child : m_children) {
|
for (auto const& child : m_children) {
|
||||||
auto child_match = child->evaluate(window);
|
auto child_match = child->evaluate(document);
|
||||||
if (child_match == MatchResult::False)
|
if (child_match == MatchResult::False)
|
||||||
return MatchResult::False;
|
return MatchResult::False;
|
||||||
if (child_match == MatchResult::True)
|
if (child_match == MatchResult::True)
|
||||||
|
@ -102,14 +102,14 @@ void BooleanAndExpression::dump(StringBuilder& builder, int indent_levels) const
|
||||||
child->dump(builder, indent_levels + 1);
|
child->dump(builder, indent_levels + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult BooleanOrExpression::evaluate(HTML::Window const* window) const
|
MatchResult BooleanOrExpression::evaluate(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
// https://drafts.csswg.org/css-values-5/#boolean-logic
|
||||||
// Multiple tests connected with `or` evaluate to true if any of those tests are true, false if all of them are
|
// Multiple tests connected with `or` evaluate to true if any of those tests are true, false if all of them are
|
||||||
// false, and unknown otherwise (i.e. at least one unknown, but no true).
|
// false, and unknown otherwise (i.e. at least one unknown, but no true).
|
||||||
size_t false_results = 0;
|
size_t false_results = 0;
|
||||||
for (auto const& child : m_children) {
|
for (auto const& child : m_children) {
|
||||||
auto child_match = child->evaluate(window);
|
auto child_match = child->evaluate(document);
|
||||||
if (child_match == MatchResult::True)
|
if (child_match == MatchResult::True)
|
||||||
return MatchResult::True;
|
return MatchResult::True;
|
||||||
if (child_match == MatchResult::False)
|
if (child_match == MatchResult::False)
|
||||||
|
|
|
@ -81,10 +81,10 @@ class BooleanExpression {
|
||||||
public:
|
public:
|
||||||
virtual ~BooleanExpression() = default;
|
virtual ~BooleanExpression() = default;
|
||||||
|
|
||||||
bool evaluate_to_boolean(HTML::Window const*) const;
|
bool evaluate_to_boolean(DOM::Document const*) const;
|
||||||
static void indent(StringBuilder& builder, int levels);
|
static void indent(StringBuilder& builder, int levels);
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const = 0;
|
virtual MatchResult evaluate(DOM::Document const*) const = 0;
|
||||||
virtual String to_string() const = 0;
|
virtual String to_string() const = 0;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const = 0;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const = 0;
|
||||||
};
|
};
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~GeneralEnclosed() override = default;
|
virtual ~GeneralEnclosed() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override { return m_matches; }
|
virtual MatchResult evaluate(DOM::Document const*) const override { return m_matches; }
|
||||||
virtual String to_string() const override { return m_serialized_contents; }
|
virtual String to_string() const override { return m_serialized_contents; }
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~BooleanNotExpression() override = default;
|
virtual ~BooleanNotExpression() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~BooleanExpressionInParens() override = default;
|
virtual ~BooleanExpressionInParens() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~BooleanAndExpression() override = default;
|
virtual ~BooleanAndExpression() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~BooleanOrExpression() override = default;
|
virtual ~BooleanOrExpression() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
|
|
||||||
MediaList* media() const { return m_media; }
|
MediaList* media() const { return m_media; }
|
||||||
|
|
||||||
bool evaluate(HTML::Window const& window) { return m_media->evaluate(window); }
|
bool evaluate(DOM::Document const& document) { return m_media->evaluate(document); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CSSMediaRule(JS::Realm&, MediaList&, CSSRuleList&);
|
CSSMediaRule(JS::Realm&, MediaList&, CSSRuleList&);
|
||||||
|
|
|
@ -224,7 +224,7 @@ void CSSRuleList::for_each_effective_rule(TraversalOrder order, Function<void(We
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSSRuleList::evaluate_media_queries(HTML::Window const& window)
|
bool CSSRuleList::evaluate_media_queries(DOM::Document const& document)
|
||||||
{
|
{
|
||||||
bool any_media_queries_changed_match_state = false;
|
bool any_media_queries_changed_match_state = false;
|
||||||
|
|
||||||
|
@ -232,35 +232,35 @@ bool CSSRuleList::evaluate_media_queries(HTML::Window const& window)
|
||||||
switch (rule->type()) {
|
switch (rule->type()) {
|
||||||
case CSSRule::Type::Import: {
|
case CSSRule::Type::Import: {
|
||||||
auto& import_rule = as<CSSImportRule>(*rule);
|
auto& import_rule = as<CSSImportRule>(*rule);
|
||||||
if (import_rule.loaded_style_sheet() && import_rule.loaded_style_sheet()->evaluate_media_queries(window))
|
if (import_rule.loaded_style_sheet() && import_rule.loaded_style_sheet()->evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSSRule::Type::LayerBlock: {
|
case CSSRule::Type::LayerBlock: {
|
||||||
auto& layer_rule = as<CSSLayerBlockRule>(*rule);
|
auto& layer_rule = as<CSSLayerBlockRule>(*rule);
|
||||||
if (layer_rule.css_rules().evaluate_media_queries(window))
|
if (layer_rule.css_rules().evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSSRule::Type::Media: {
|
case CSSRule::Type::Media: {
|
||||||
auto& media_rule = as<CSSMediaRule>(*rule);
|
auto& media_rule = as<CSSMediaRule>(*rule);
|
||||||
bool did_match = media_rule.condition_matches();
|
bool did_match = media_rule.condition_matches();
|
||||||
bool now_matches = media_rule.evaluate(window);
|
bool now_matches = media_rule.evaluate(document);
|
||||||
if (did_match != now_matches)
|
if (did_match != now_matches)
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
if (now_matches && media_rule.css_rules().evaluate_media_queries(window))
|
if (now_matches && media_rule.css_rules().evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSSRule::Type::Supports: {
|
case CSSRule::Type::Supports: {
|
||||||
auto& supports_rule = as<CSSSupportsRule>(*rule);
|
auto& supports_rule = as<CSSSupportsRule>(*rule);
|
||||||
if (supports_rule.condition_matches() && supports_rule.css_rules().evaluate_media_queries(window))
|
if (supports_rule.condition_matches() && supports_rule.css_rules().evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CSSRule::Type::Style: {
|
case CSSRule::Type::Style: {
|
||||||
auto& style_rule = as<CSSStyleRule>(*rule);
|
auto& style_rule = as<CSSStyleRule>(*rule);
|
||||||
if (style_rule.css_rules().evaluate_media_queries(window))
|
if (style_rule.css_rules().evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ public:
|
||||||
|
|
||||||
void for_each_effective_rule(TraversalOrder, Function<void(CSSRule const&)> const& callback) const;
|
void for_each_effective_rule(TraversalOrder, Function<void(CSSRule const&)> const& callback) const;
|
||||||
// Returns whether the match state of any media queries changed after evaluation.
|
// Returns whether the match state of any media queries changed after evaluation.
|
||||||
bool evaluate_media_queries(HTML::Window const&);
|
bool evaluate_media_queries(DOM::Document const&);
|
||||||
|
|
||||||
void set_owner_rule(GC::Ref<CSSRule> owner_rule) { m_owner_rule = owner_rule; }
|
void set_owner_rule(GC::Ref<CSSRule> owner_rule) { m_owner_rule = owner_rule; }
|
||||||
void set_rules(Badge<CSSStyleSheet>, Vector<GC::Ref<CSSRule>> rules) { m_rules = move(rules); }
|
void set_rules(Badge<CSSStyleSheet>, Vector<GC::Ref<CSSRule>> rules) { m_rules = move(rules); }
|
||||||
|
|
|
@ -353,14 +353,14 @@ GC::Ptr<DOM::Document> CSSStyleSheet::owning_document() const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSSStyleSheet::evaluate_media_queries(HTML::Window const& window)
|
bool CSSStyleSheet::evaluate_media_queries(DOM::Document const& document)
|
||||||
{
|
{
|
||||||
bool any_media_queries_changed_match_state = false;
|
bool any_media_queries_changed_match_state = false;
|
||||||
|
|
||||||
bool now_matches = m_media->evaluate(window);
|
bool now_matches = m_media->evaluate(document);
|
||||||
if (!m_did_match.has_value() || m_did_match.value() != now_matches)
|
if (!m_did_match.has_value() || m_did_match.value() != now_matches)
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
if (now_matches && m_rules->evaluate_media_queries(window))
|
if (now_matches && m_rules->evaluate_media_queries(document))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
|
|
||||||
m_did_match = now_matches;
|
m_did_match = now_matches;
|
||||||
|
|
|
@ -63,7 +63,7 @@ public:
|
||||||
void for_each_effective_rule(TraversalOrder, Function<void(CSSRule const&)> const& callback) const;
|
void for_each_effective_rule(TraversalOrder, Function<void(CSSRule const&)> const& callback) const;
|
||||||
void for_each_effective_style_producing_rule(Function<void(CSSRule const&)> const& callback) const;
|
void for_each_effective_style_producing_rule(Function<void(CSSRule const&)> const& callback) const;
|
||||||
// Returns whether the match state of any media queries changed after evaluation.
|
// Returns whether the match state of any media queries changed after evaluation.
|
||||||
bool evaluate_media_queries(HTML::Window const&);
|
bool evaluate_media_queries(DOM::Document const&);
|
||||||
void for_each_effective_keyframes_at_rule(Function<void(CSSKeyframesRule const&)> const& callback) const;
|
void for_each_effective_keyframes_at_rule(Function<void(CSSKeyframesRule const&)> const& callback) const;
|
||||||
|
|
||||||
void add_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
void add_owning_document_or_shadow_root(DOM::Node& document_or_shadow_root);
|
||||||
|
|
|
@ -137,6 +137,18 @@ Length::ResolutionContext Length::ResolutionContext::for_window(HTML::Window con
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Length::ResolutionContext Length::ResolutionContext::for_document(DOM::Document const& document)
|
||||||
|
{
|
||||||
|
auto const& initial_font = document.style_computer().initial_font();
|
||||||
|
Gfx::FontPixelMetrics const& initial_font_metrics = initial_font.pixel_metrics();
|
||||||
|
Length::FontMetrics font_metrics { CSSPixels { initial_font.pixel_size() }, initial_font_metrics, InitialValues::line_height() };
|
||||||
|
return Length::ResolutionContext {
|
||||||
|
.viewport_rect = document.navigable()->viewport_rect(),
|
||||||
|
.font_metrics = font_metrics,
|
||||||
|
.root_font_metrics = font_metrics,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Node const& node)
|
Length::ResolutionContext Length::ResolutionContext::for_layout_node(Layout::Node const& node)
|
||||||
{
|
{
|
||||||
Layout::Node const* root_layout_node;
|
Layout::Node const* root_layout_node;
|
||||||
|
|
|
@ -55,7 +55,10 @@ public:
|
||||||
FlyString unit_name() const { return CSS::to_string(m_unit); }
|
FlyString unit_name() const { return CSS::to_string(m_unit); }
|
||||||
|
|
||||||
struct ResolutionContext {
|
struct ResolutionContext {
|
||||||
|
[[nodiscard]] static ResolutionContext for_document(DOM::Document const&);
|
||||||
[[nodiscard]] static ResolutionContext for_element(DOM::AbstractElement const&);
|
[[nodiscard]] static ResolutionContext for_element(DOM::AbstractElement const&);
|
||||||
|
// FIXME: Anywhere we use this we probably want to use `for_document` instead since this uses the window's
|
||||||
|
// viewport rather than the documents which can differ e.g. with iframes.
|
||||||
[[nodiscard]] static ResolutionContext for_window(HTML::Window const&);
|
[[nodiscard]] static ResolutionContext for_window(HTML::Window const&);
|
||||||
[[nodiscard]] static ResolutionContext for_layout_node(Layout::Node const&);
|
[[nodiscard]] static ResolutionContext for_layout_node(Layout::Node const&);
|
||||||
|
|
||||||
|
|
|
@ -109,10 +109,10 @@ void MediaList::delete_medium(StringView medium)
|
||||||
// FIXME: If nothing was removed, then throw a NotFoundError exception.
|
// FIXME: If nothing was removed, then throw a NotFoundError exception.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaList::evaluate(HTML::Window const& window)
|
bool MediaList::evaluate(DOM::Document const& document)
|
||||||
{
|
{
|
||||||
for (auto& media : m_media)
|
for (auto& media : m_media)
|
||||||
media->evaluate(window);
|
media->evaluate(document);
|
||||||
|
|
||||||
return matches();
|
return matches();
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
|
|
||||||
virtual Optional<JS::Value> item_value(size_t index) const override;
|
virtual Optional<JS::Value> item_value(size_t index) const override;
|
||||||
|
|
||||||
bool evaluate(HTML::Window const&);
|
bool evaluate(DOM::Document const&);
|
||||||
bool matches() const;
|
bool matches() const;
|
||||||
|
|
||||||
void set_associated_style_sheet(GC::Ref<StyleSheet> style_sheet) { m_associated_style_sheet = style_sheet; }
|
void set_associated_style_sheet(GC::Ref<StyleSheet> style_sheet) { m_associated_style_sheet = style_sheet; }
|
||||||
|
|
|
@ -96,16 +96,17 @@ String MediaFeature::to_string() const
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult MediaFeature::evaluate(HTML::Window const* window) const
|
MatchResult MediaFeature::evaluate(DOM::Document const* document) const
|
||||||
{
|
{
|
||||||
VERIFY(window);
|
VERIFY(document);
|
||||||
auto maybe_queried_value = window->query_media_feature(m_id);
|
VERIFY(document->window());
|
||||||
|
auto maybe_queried_value = document->window()->query_media_feature(m_id);
|
||||||
if (!maybe_queried_value.has_value())
|
if (!maybe_queried_value.has_value())
|
||||||
return MatchResult::False;
|
return MatchResult::False;
|
||||||
auto queried_value = maybe_queried_value.release_value();
|
auto queried_value = maybe_queried_value.release_value();
|
||||||
|
|
||||||
CalculationResolutionContext calculation_context {
|
CalculationResolutionContext calculation_context {
|
||||||
.length_resolution_context = Length::ResolutionContext::for_window(*window),
|
.length_resolution_context = Length::ResolutionContext::for_document(*document),
|
||||||
};
|
};
|
||||||
switch (m_type) {
|
switch (m_type) {
|
||||||
case Type::IsTrue:
|
case Type::IsTrue:
|
||||||
|
@ -128,23 +129,23 @@ MatchResult MediaFeature::evaluate(HTML::Window const* window) const
|
||||||
return MatchResult::False;
|
return MatchResult::False;
|
||||||
|
|
||||||
case Type::ExactValue:
|
case Type::ExactValue:
|
||||||
return compare(*window, value(), Comparison::Equal, queried_value);
|
return compare(*document, value(), Comparison::Equal, queried_value);
|
||||||
|
|
||||||
case Type::MinValue:
|
case Type::MinValue:
|
||||||
return compare(*window, queried_value, Comparison::GreaterThanOrEqual, value());
|
return compare(*document, queried_value, Comparison::GreaterThanOrEqual, value());
|
||||||
|
|
||||||
case Type::MaxValue:
|
case Type::MaxValue:
|
||||||
return compare(*window, queried_value, Comparison::LessThanOrEqual, value());
|
return compare(*document, queried_value, Comparison::LessThanOrEqual, value());
|
||||||
|
|
||||||
case Type::Range: {
|
case Type::Range: {
|
||||||
auto const& range = this->range();
|
auto const& range = this->range();
|
||||||
if (range.left_comparison.has_value()) {
|
if (range.left_comparison.has_value()) {
|
||||||
if (auto const left_result = compare(*window, *range.left_value, *range.left_comparison, queried_value); left_result != MatchResult::True)
|
if (auto const left_result = compare(*document, *range.left_value, *range.left_comparison, queried_value); left_result != MatchResult::True)
|
||||||
return left_result;
|
return left_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (range.right_comparison.has_value()) {
|
if (range.right_comparison.has_value()) {
|
||||||
if (auto const right_result = compare(*window, queried_value, *range.right_comparison, *range.right_value); right_result != MatchResult::True)
|
if (auto const right_result = compare(*document, queried_value, *range.right_comparison, *range.right_value); right_result != MatchResult::True)
|
||||||
return right_result;
|
return right_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +156,7 @@ MatchResult MediaFeature::evaluate(HTML::Window const* window) const
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult MediaFeature::compare(HTML::Window const& window, MediaFeatureValue const& left, Comparison comparison, MediaFeatureValue const& right)
|
MatchResult MediaFeature::compare(DOM::Document const& document, MediaFeatureValue const& left, Comparison comparison, MediaFeatureValue const& right)
|
||||||
{
|
{
|
||||||
if (left.is_unknown() || right.is_unknown())
|
if (left.is_unknown() || right.is_unknown())
|
||||||
return MatchResult::Unknown;
|
return MatchResult::Unknown;
|
||||||
|
@ -169,7 +170,7 @@ MatchResult MediaFeature::compare(HTML::Window const& window, MediaFeatureValue
|
||||||
return MatchResult::False;
|
return MatchResult::False;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto length_resolution_context = Length::ResolutionContext::for_window(window);
|
auto length_resolution_context = Length::ResolutionContext::for_document(document);
|
||||||
|
|
||||||
CalculationResolutionContext calculation_context {
|
CalculationResolutionContext calculation_context {
|
||||||
.length_resolution_context = length_resolution_context,
|
.length_resolution_context = length_resolution_context,
|
||||||
|
@ -282,7 +283,7 @@ String MediaQuery::to_string() const
|
||||||
return MUST(builder.to_string());
|
return MUST(builder.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaQuery::evaluate(HTML::Window const& window)
|
bool MediaQuery::evaluate(DOM::Document const& document)
|
||||||
{
|
{
|
||||||
auto matches_media = [](MediaType const& media) -> MatchResult {
|
auto matches_media = [](MediaType const& media) -> MatchResult {
|
||||||
if (!media.known_type.has_value())
|
if (!media.known_type.has_value())
|
||||||
|
@ -303,7 +304,7 @@ bool MediaQuery::evaluate(HTML::Window const& window)
|
||||||
MatchResult result = matches_media(m_media_type);
|
MatchResult result = matches_media(m_media_type);
|
||||||
|
|
||||||
if ((result != MatchResult::False) && m_media_condition)
|
if ((result != MatchResult::False) && m_media_condition)
|
||||||
result = result && m_media_condition->evaluate(&window);
|
result = result && m_media_condition->evaluate(&document);
|
||||||
|
|
||||||
if (m_negated)
|
if (m_negated)
|
||||||
result = negate(result);
|
result = negate(result);
|
||||||
|
|
|
@ -161,7 +161,7 @@ public:
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ private:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static MatchResult compare(HTML::Window const& window, MediaFeatureValue const& left, Comparison comparison, MediaFeatureValue const& right);
|
static MatchResult compare(DOM::Document const& document, MediaFeatureValue const& left, Comparison comparison, MediaFeatureValue const& right);
|
||||||
MediaFeatureValue const& value() const { return m_value.get<MediaFeatureValue>(); }
|
MediaFeatureValue const& value() const { return m_value.get<MediaFeatureValue>(); }
|
||||||
Range const& range() const { return m_value.get<Range>(); }
|
Range const& range() const { return m_value.get<Range>(); }
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ public:
|
||||||
static NonnullRefPtr<MediaQuery> create() { return adopt_ref(*new MediaQuery); }
|
static NonnullRefPtr<MediaQuery> create() { return adopt_ref(*new MediaQuery); }
|
||||||
|
|
||||||
bool matches() const { return m_matches; }
|
bool matches() const { return m_matches; }
|
||||||
bool evaluate(HTML::Window const&);
|
bool evaluate(DOM::Document const&);
|
||||||
String to_string() const;
|
String to_string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -85,16 +85,12 @@ bool MediaQueryList::matches() const
|
||||||
|
|
||||||
bool MediaQueryList::evaluate()
|
bool MediaQueryList::evaluate()
|
||||||
{
|
{
|
||||||
auto window = m_document->window();
|
|
||||||
if (!window)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (m_media.is_empty())
|
if (m_media.is_empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool now_matches = false;
|
bool now_matches = false;
|
||||||
for (auto& media : m_media) {
|
for (auto& media : m_media) {
|
||||||
now_matches = now_matches || media->evaluate(*window);
|
now_matches = now_matches || media->evaluate(m_document);
|
||||||
}
|
}
|
||||||
|
|
||||||
return now_matches;
|
return now_matches;
|
||||||
|
|
|
@ -1814,8 +1814,7 @@ LengthOrCalculated Parser::parse_as_sizes_attribute(DOM::Element const& element,
|
||||||
// If it does not parse correctly, or it does parse correctly but the <media-condition> evaluates to false, continue.
|
// If it does not parse correctly, or it does parse correctly but the <media-condition> evaluates to false, continue.
|
||||||
TokenStream token_stream { unparsed_size };
|
TokenStream token_stream { unparsed_size };
|
||||||
auto media_condition = parse_media_condition(token_stream);
|
auto media_condition = parse_media_condition(token_stream);
|
||||||
auto const* context_window = window();
|
if (!media_condition || (m_document && media_condition->evaluate(m_document) == MatchResult::False)) {
|
||||||
if (!media_condition || (context_window && media_condition->evaluate(context_window) == MatchResult::False)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ void StyleSheetList::add_sheet(CSSStyleSheet& sheet)
|
||||||
// NOTE: We evaluate media queries immediately when adding a new sheet.
|
// NOTE: We evaluate media queries immediately when adding a new sheet.
|
||||||
// This coalesces the full document style invalidations.
|
// This coalesces the full document style invalidations.
|
||||||
// If we don't do this, we invalidate now, and then again when Document updates media rules.
|
// If we don't do this, we invalidate now, and then again when Document updates media rules.
|
||||||
sheet.evaluate_media_queries(as<HTML::Window>(HTML::relevant_global_object(*this)));
|
sheet.evaluate_media_queries(document());
|
||||||
|
|
||||||
if (sheet.rules().length() == 0) {
|
if (sheet.rules().length() == 0) {
|
||||||
// NOTE: If the added sheet has no rules, we don't have to invalidate anything.
|
// NOTE: If the added sheet has no rules, we don't have to invalidate anything.
|
||||||
|
|
|
@ -15,7 +15,7 @@ Supports::Supports(NonnullOwnPtr<BooleanExpression>&& condition)
|
||||||
m_matches = m_condition->evaluate_to_boolean(nullptr);
|
m_matches = m_condition->evaluate_to_boolean(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult Supports::Declaration::evaluate(HTML::Window const*) const
|
MatchResult Supports::Declaration::evaluate(DOM::Document const*) const
|
||||||
{
|
{
|
||||||
return as_match_result(m_matches);
|
return as_match_result(m_matches);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ void Supports::Declaration::dump(StringBuilder& builder, int indent_levels) cons
|
||||||
builder.appendff("Declaration: `{}`, matches={}\n", m_declaration, m_matches);
|
builder.appendff("Declaration: `{}`, matches={}\n", m_declaration, m_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult Supports::Selector::evaluate(HTML::Window const*) const
|
MatchResult Supports::Selector::evaluate(DOM::Document const*) const
|
||||||
{
|
{
|
||||||
return as_match_result(m_matches);
|
return as_match_result(m_matches);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ void Supports::Selector::dump(StringBuilder& builder, int indent_levels) const
|
||||||
builder.appendff("Selector: `{}` matches={}\n", m_selector, m_matches);
|
builder.appendff("Selector: `{}` matches={}\n", m_selector, m_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult Supports::FontTech::evaluate(HTML::Window const*) const
|
MatchResult Supports::FontTech::evaluate(DOM::Document const*) const
|
||||||
{
|
{
|
||||||
return as_match_result(m_matches);
|
return as_match_result(m_matches);
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ void Supports::FontTech::dump(StringBuilder& builder, int indent_levels) const
|
||||||
builder.appendff("FontTech: `{}` matches={}\n", m_tech, m_matches);
|
builder.appendff("FontTech: `{}` matches={}\n", m_tech, m_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatchResult Supports::FontFormat::evaluate(HTML::Window const*) const
|
MatchResult Supports::FontFormat::evaluate(DOM::Document const*) const
|
||||||
{
|
{
|
||||||
return as_match_result(m_matches);
|
return as_match_result(m_matches);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~Declaration() override = default;
|
virtual ~Declaration() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~Selector() override = default;
|
virtual ~Selector() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~FontTech() override = default;
|
virtual ~FontTech() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~FontFormat() override = default;
|
virtual ~FontFormat() override = default;
|
||||||
|
|
||||||
virtual MatchResult evaluate(HTML::Window const*) const override;
|
virtual MatchResult evaluate(DOM::Document const*) const override;
|
||||||
virtual String to_string() const override;
|
virtual String to_string() const override;
|
||||||
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
virtual void dump(StringBuilder&, int indent_levels = 0) const override;
|
||||||
|
|
||||||
|
|
|
@ -1675,7 +1675,7 @@ void Document::obtain_theme_color()
|
||||||
auto media = element.attribute(HTML::AttributeNames::media);
|
auto media = element.attribute(HTML::AttributeNames::media);
|
||||||
if (media.has_value()) {
|
if (media.has_value()) {
|
||||||
auto query = parse_media_query(context, media.value());
|
auto query = parse_media_query(context, media.value());
|
||||||
if (query.is_null() || !window() || !query->evaluate(*window()))
|
if (query.is_null() || !query->evaluate(*this))
|
||||||
return TraversalDecision::Continue;
|
return TraversalDecision::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3467,13 +3467,9 @@ void Document::evaluate_media_queries_and_report_changes()
|
||||||
|
|
||||||
void Document::evaluate_media_rules()
|
void Document::evaluate_media_rules()
|
||||||
{
|
{
|
||||||
auto window = this->window();
|
|
||||||
if (!window)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool any_media_queries_changed_match_state = false;
|
bool any_media_queries_changed_match_state = false;
|
||||||
for_each_active_css_style_sheet([&](CSS::CSSStyleSheet& style_sheet, auto) {
|
for_each_active_css_style_sheet([&](CSS::CSSStyleSheet& style_sheet, auto) {
|
||||||
if (style_sheet.evaluate_media_queries(*window))
|
if (style_sheet.evaluate_media_queries(*this))
|
||||||
any_media_queries_changed_match_state = true;
|
any_media_queries_changed_match_state = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1188,7 +1188,7 @@ static void update_the_source_set(DOM::Element& element)
|
||||||
if (child->has_attribute(HTML::AttributeNames::media)) {
|
if (child->has_attribute(HTML::AttributeNames::media)) {
|
||||||
auto media_query = parse_media_query(CSS::Parser::ParsingParams { element.document() },
|
auto media_query = parse_media_query(CSS::Parser::ParsingParams { element.document() },
|
||||||
child->get_attribute_value(HTML::AttributeNames::media));
|
child->get_attribute_value(HTML::AttributeNames::media));
|
||||||
if (!media_query || !element.document().window() || !media_query->evaluate(*element.document().window())) {
|
if (!media_query || !media_query->evaluate(element.document())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,17 +2,17 @@ Harness status: OK
|
||||||
|
|
||||||
Found 12 tests
|
Found 12 tests
|
||||||
|
|
||||||
2 Pass
|
11 Pass
|
||||||
10 Fail
|
1 Fail
|
||||||
Pass box should be orange if the calc between px-em in @media was correct
|
Pass box should be orange if the calc between px-em in @media was correct
|
||||||
Fail box should be orange if the calc between vh+em in @media was correct
|
Pass box should be orange if the calc between vh+em in @media was correct
|
||||||
Fail box should be orange if the calc between vw-em in @media was correct
|
Pass box should be orange if the calc between vw-em in @media was correct
|
||||||
Fail box should be orange if the calc between vw+vh in @media was correct
|
Pass box should be orange if the calc between vw+vh in @media was correct
|
||||||
Fail box should be orange if the calc between vh+px in @media was correct
|
Pass box should be orange if the calc between vh+px in @media was correct
|
||||||
Fail box should be orange if the calc between vw+px in @media was correct
|
Pass box should be orange if the calc between vw+px in @media was correct
|
||||||
Pass box should be orange if the calc between px/em*em in @media was correct
|
Pass box should be orange if the calc between px/em*em in @media was correct
|
||||||
Fail box should be orange if the calc between vh*em in @media was correct
|
Pass box should be orange if the calc between vh*em in @media was correct
|
||||||
Fail box should be orange if the calc between vh*vw/em*px/vh in @media was correct
|
Fail box should be orange if the calc between vh*vw/em*px/vh in @media was correct
|
||||||
Fail box should be orange if the calc between vw/px*vh in @media was correct
|
Pass box should be orange if the calc between vw/px*vh in @media was correct
|
||||||
Fail box should be orange if the calc between vh*vw/em*px in @media was correct
|
Pass box should be orange if the calc between vh*vw/em*px in @media was correct
|
||||||
Fail box should be orange if the calc between vw*vh*px*em/px/px/px in @media was correct
|
Pass box should be orange if the calc between vw*vh*px*em/px/px/px in @media was correct
|
|
@ -0,0 +1,31 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 26 tests
|
||||||
|
|
||||||
|
26 Pass
|
||||||
|
Pass @media(width:100vw) applies
|
||||||
|
Pass @media(width:100vi) applies
|
||||||
|
Pass @media(width:100vmax) applies
|
||||||
|
Pass @media(width:100svw) applies
|
||||||
|
Pass @media(width:100svi) applies
|
||||||
|
Pass @media(width:100svmax) applies
|
||||||
|
Pass @media(width:100lvw) applies
|
||||||
|
Pass @media(width:100lvi) applies
|
||||||
|
Pass @media(width:100lvmax) applies
|
||||||
|
Pass @media(width:100dvw) applies
|
||||||
|
Pass @media(width:100dvi) applies
|
||||||
|
Pass @media(width:100dvmax) applies
|
||||||
|
Pass @media(height:100vh) applies
|
||||||
|
Pass @media(height:100vb) applies
|
||||||
|
Pass @media(height:100vmin) applies
|
||||||
|
Pass @media(height:100svh) applies
|
||||||
|
Pass @media(height:100svb) applies
|
||||||
|
Pass @media(height:100svmin) applies
|
||||||
|
Pass @media(height:100lvh) applies
|
||||||
|
Pass @media(height:100lvb) applies
|
||||||
|
Pass @media(height:100lvmin) applies
|
||||||
|
Pass @media(height:100dvh) applies
|
||||||
|
Pass @media(height:100dvb) applies
|
||||||
|
Pass @media(height:100dvmin) applies
|
||||||
|
Pass @media(width:90vw) does not apply
|
||||||
|
Pass @media(height:90vh) does not apply
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Viewport units in @media</title>
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-values-4/#viewport-relative-lengths">
|
||||||
|
<script src="../../resources/testharness.js"></script>
|
||||||
|
<script src="../../resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
iframe {
|
||||||
|
width: 200px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<iframe id=iframe></iframe>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const doc = iframe.contentDocument;
|
||||||
|
const win = iframe.contentWindow;
|
||||||
|
|
||||||
|
function test_media_query(feature, result, description) {
|
||||||
|
test((t) => {
|
||||||
|
t.add_cleanup(() => { doc.body.innerHTML = ''; })
|
||||||
|
doc.body.innerHTML = `
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
@media (${feature}) {
|
||||||
|
body {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
assert_equals(win.getComputedStyle(doc.body).color, result);
|
||||||
|
}, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_media_query_applies(feature) {
|
||||||
|
test_media_query(feature, 'rgb(0, 128, 0)', `@media(${feature}) applies`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_media_query_does_not_apply(feature) {
|
||||||
|
test_media_query(feature, 'rgb(255, 0, 0)', `@media(${feature}) does not apply`);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_media_query_applies('width:100vw');
|
||||||
|
test_media_query_applies('width:100vi');
|
||||||
|
test_media_query_applies('width:100vmax');
|
||||||
|
test_media_query_applies('width:100svw');
|
||||||
|
test_media_query_applies('width:100svi');
|
||||||
|
test_media_query_applies('width:100svmax');
|
||||||
|
test_media_query_applies('width:100lvw');
|
||||||
|
test_media_query_applies('width:100lvi');
|
||||||
|
test_media_query_applies('width:100lvmax');
|
||||||
|
test_media_query_applies('width:100dvw');
|
||||||
|
test_media_query_applies('width:100dvi');
|
||||||
|
test_media_query_applies('width:100dvmax');
|
||||||
|
|
||||||
|
test_media_query_applies('height:100vh');
|
||||||
|
test_media_query_applies('height:100vb');
|
||||||
|
test_media_query_applies('height:100vmin');
|
||||||
|
test_media_query_applies('height:100svh');
|
||||||
|
test_media_query_applies('height:100svb');
|
||||||
|
test_media_query_applies('height:100svmin');
|
||||||
|
test_media_query_applies('height:100lvh');
|
||||||
|
test_media_query_applies('height:100lvb');
|
||||||
|
test_media_query_applies('height:100lvmin');
|
||||||
|
test_media_query_applies('height:100dvh');
|
||||||
|
test_media_query_applies('height:100dvb');
|
||||||
|
test_media_query_applies('height:100dvmin');
|
||||||
|
|
||||||
|
test_media_query_does_not_apply('width:90vw');
|
||||||
|
test_media_query_does_not_apply('height:90vh');
|
||||||
|
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue