blob: 57f1d190e948f663b7e75859dafe4409f8b67bf5 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/container_query.h"
#include "third_party/blink/renderer/core/css/css_container_rule.h"
#include "third_party/blink/renderer/core/css/css_test_helpers.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
class ContainerQueryTest : public PageTestBase,
private ScopedCSSContainerQueriesForTest {
public:
ContainerQueryTest() : ScopedCSSContainerQueriesForTest(true) {}
StyleRuleContainer* ParseAtContainer(String rule_string) {
return DynamicTo<StyleRuleContainer>(
css_test_helpers::ParseRule(GetDocument(), rule_string));
}
ContainerQuery* ParseContainerQuery(String query) {
String rule = "@container " + query + " {}";
StyleRuleContainer* container = ParseAtContainer(rule);
if (!container)
return nullptr;
return &container->GetContainerQuery();
}
PhysicalAxes QueriedAxes(String query) {
ContainerQuery* container_query = ParseContainerQuery(query);
DCHECK(container_query);
return container_query->QueriedAxes();
}
String SerializeCondition(StyleRuleContainer* container) {
if (!container)
return "";
return container->GetContainerQuery().ToString();
}
// TODO(crbug.com/1145970): Remove this when ContainerQuery no longer
// relies on MediaQuerySet.
MediaQuerySet& GetMediaQuerySet(ContainerQuery& container_query) {
return *container_query.media_queries_;
}
};
TEST_F(ContainerQueryTest, PreludeParsing) {
// Valid:
EXPECT_EQ(
"(min-width: 300px)",
SerializeCondition(ParseAtContainer("@container (min-width: 300px) {}")));
EXPECT_EQ(
"(max-width: 500px)",
SerializeCondition(ParseAtContainer("@container (max-width: 500px) {}")));
// TODO(crbug.com/1145970): The MediaQuery parser emits a "not all"
// MediaQuery for parse failures. When ContainerQuery has its own parser,
// it should probably return nullptr instead.
// Invalid:
EXPECT_EQ("not all",
SerializeCondition(ParseAtContainer("@container 100px {}")));
EXPECT_EQ("not all",
SerializeCondition(ParseAtContainer("@container calc(1) {}")));
}
TEST_F(ContainerQueryTest, RuleParsing) {
StyleRuleContainer* container = ParseAtContainer(R"CSS(
@container (min-width: 100px) {
div { width: 100px; }
span { height: 100px; }
}
)CSS");
ASSERT_TRUE(container);
CSSStyleSheet* sheet = css_test_helpers::CreateStyleSheet(GetDocument());
auto* rule =
DynamicTo<CSSContainerRule>(container->CreateCSSOMWrapper(sheet));
ASSERT_TRUE(rule);
ASSERT_EQ(2u, rule->length());
auto* div_rule = rule->Item(0);
ASSERT_TRUE(div_rule);
EXPECT_EQ("div { width: 100px; }", div_rule->cssText());
auto* span_rule = rule->Item(1);
ASSERT_TRUE(span_rule);
EXPECT_EQ("span { height: 100px; }", span_rule->cssText());
}
TEST_F(ContainerQueryTest, RuleCopy) {
StyleRuleContainer* container = ParseAtContainer(R"CSS(
@container (min-width: 100px) {
div { width: 100px; }
}
)CSS");
ASSERT_TRUE(container);
// Copy via StyleRuleBase to test switch dispatch.
auto* copy_base = static_cast<StyleRuleBase*>(container)->Copy();
auto* copy = DynamicTo<StyleRuleContainer>(copy_base);
ASSERT_TRUE(copy);
// The StyleRuleContainer object should be copied.
EXPECT_NE(container, copy);
// The rules should be copied.
auto rules = container->ChildRules();
auto rules_copy = copy->ChildRules();
ASSERT_EQ(1u, rules.size());
ASSERT_EQ(1u, rules_copy.size());
EXPECT_NE(rules[0], rules_copy[0]);
// The ContainerQuery should be copied.
EXPECT_NE(&container->GetContainerQuery(), &copy->GetContainerQuery());
// The inner MediaQuerySet should be copied.
EXPECT_NE(&GetMediaQuerySet(container->GetContainerQuery()),
&GetMediaQuerySet(copy->GetContainerQuery()));
}
TEST_F(ContainerQueryTest, ContainerQueryEvaluation) {
SetBodyInnerHTML(R"HTML(
<style>
#container {
contain: size layout;
width: 500px;
height: 500px;
}
#container.adjust {
width: 600px;
}
div { z-index:1; }
/* Should apply: */
@container (min-width: 500px) {
div { z-index:2; }
}
/* Should initially not apply: */
@container (min-width: 600px) {
div { z-index:3; }
}
</style>
<div id=container>
<div id=div></div>
</div>
)HTML");
Element* div = GetDocument().getElementById("div");
ASSERT_TRUE(div);
EXPECT_EQ(2, div->ComputedStyleRef().ZIndex());
// Check that dependent elements are responsive to changes:
Element* container = GetDocument().getElementById("container");
ASSERT_TRUE(container);
container->setAttribute(html_names::kClassAttr, "adjust");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(3, div->ComputedStyleRef().ZIndex());
container->setAttribute(html_names::kClassAttr, "");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(2, div->ComputedStyleRef().ZIndex());
}
TEST_F(ContainerQueryTest, QueriedAxes) {
auto horizontal = PhysicalAxes(kPhysicalAxisHorizontal);
auto vertical = PhysicalAxes(kPhysicalAxisVertical);
auto both = PhysicalAxes(kPhysicalAxisBoth);
auto none = PhysicalAxes(kPhysicalAxisNone);
EXPECT_EQ(horizontal, QueriedAxes("(min-width: 1px)"));
EXPECT_EQ(horizontal, QueriedAxes("(max-width: 1px)"));
EXPECT_EQ(horizontal, QueriedAxes("(width: 1px)"));
EXPECT_EQ(vertical, QueriedAxes("(min-height: 1px)"));
EXPECT_EQ(vertical, QueriedAxes("(max-height: 1px)"));
EXPECT_EQ(vertical, QueriedAxes("(height: 1px)"));
EXPECT_EQ(both, QueriedAxes("(width: 1px) and (height: 1px)"));
EXPECT_EQ(both, QueriedAxes("(min-width: 1px) and (max-height: 1px)"));
// TODO(crbug.com/1145970): We want to test the case where no axes are
// queried (kPhysicalAxisNone). This can (for now) be achieved by using
// some media query feature (e.g. "resolution"). Ultimately, using
// "resolution" will not be allowed in @container: we will then need to find
// another way to author a container query that queries no axes (or make it
// illegal altogether).
EXPECT_EQ(none, QueriedAxes("(resolution: 150dpi)"));
}
} // namespace blink