blob: a1df746ee1a5fe9d0987b3f0bc579903c37f296d [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// 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/style_scope_frame.h"
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/css/style_scope.h"
#include "third_party/blink/renderer/core/css/style_scope_data.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/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
namespace blink {
class StyleScopeFrameTest : public PageTestBase {
public:
HeapVector<Member<const StyleScope>, 1> TriggeredScopes(Element& e) {
if (StyleScopeData* style_scope_data = e.GetStyleScopeData()) {
return style_scope_data->GetTriggeredScopes();
}
return HeapVector<Member<const StyleScope>, 1>();
}
};
TEST_F(StyleScopeFrameTest, HasSeenImplicitScope) {
SetBodyInnerHTML(R"HTML(
<div id=a>
<div id=b>
<div id=c>
</div>
</div>
</div>
<div id=d>
<div id=e>
<style>
@scope {
div { }
}
</style>
<div id=f>
</div>
</div>
</div>
)HTML");
Element* a = GetElementById("a");
Element* b = GetElementById("b");
Element* c = GetElementById("c");
Element* d = GetElementById("d");
Element* e = GetElementById("e");
Element* f = GetElementById("f");
ASSERT_TRUE(a && b && c && d && e && f);
HeapVector<Member<const StyleScope>, 1> style_scopes = TriggeredScopes(*e);
ASSERT_EQ(1u, style_scopes.size());
const StyleScope* scope = style_scopes[0];
ASSERT_TRUE(scope && scope->IsImplicit());
// Check HasSeenImplicitScope with a single frame,
// simulating a recalc rooted at that element.
{
StyleScopeFrame a_frame(*a);
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*scope));
}
{
StyleScopeFrame b_frame(*b);
EXPECT_FALSE(b_frame.HasSeenImplicitScope(*scope));
}
{
StyleScopeFrame c_frame(*c);
EXPECT_FALSE(c_frame.HasSeenImplicitScope(*scope));
}
{
StyleScopeFrame d_frame(*d);
EXPECT_FALSE(d_frame.HasSeenImplicitScope(*scope));
}
{
StyleScopeFrame e_frame(*e);
EXPECT_TRUE(e_frame.HasSeenImplicitScope(*scope));
}
{
StyleScopeFrame f_frame(*f);
EXPECT_TRUE(f_frame.HasSeenImplicitScope(*scope));
}
// Check HasSeenImplicitScope when we have StyleScopeFrames for more
// of the ancestor chain.
// #c, #a and #b already on the stack.
{
StyleScopeFrame a_frame(*a);
StyleScopeFrame b_frame(*b, &a_frame);
StyleScopeFrame c_frame(*c, &b_frame);
EXPECT_FALSE(c_frame.HasSeenImplicitScope(*scope));
}
// #e, with #d already on the stack.
{
StyleScopeFrame d_frame(*d);
StyleScopeFrame e_frame(*e, &d_frame);
EXPECT_TRUE(e_frame.HasSeenImplicitScope(*scope));
}
// #f, with #c and #d already on the stack.
{
StyleScopeFrame d_frame(*d);
StyleScopeFrame e_frame(*e, &d_frame);
StyleScopeFrame f_frame(*f, &e_frame);
EXPECT_TRUE(f_frame.HasSeenImplicitScope(*scope));
}
}
TEST_F(StyleScopeFrameTest, HasSeenImplicitScope_Nested) {
SetBodyInnerHTML(R"HTML(
<div id=a>
<div id=b>
<style>
@scope {
div { }
@scope {
div { }
}
}
</style>
<div id=c>
</div>
</div>
</div>
)HTML");
Element* a = GetElementById("a");
Element* b = GetElementById("b");
Element* c = GetElementById("c");
ASSERT_TRUE(a && b && c);
HeapVector<Member<const StyleScope>> style_scopes = TriggeredScopes(*b);
ASSERT_EQ(2u, style_scopes.size());
const StyleScope* outer_scope = style_scopes[0];
ASSERT_TRUE(outer_scope && outer_scope->IsImplicit());
const StyleScope* inner_scope = style_scopes[1];
ASSERT_TRUE(inner_scope && inner_scope->IsImplicit());
{
StyleScopeFrame a_frame(*a);
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*inner_scope));
}
{
StyleScopeFrame b_frame(*b);
EXPECT_TRUE(b_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_TRUE(b_frame.HasSeenImplicitScope(*inner_scope));
}
{
StyleScopeFrame c_frame(*c);
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*inner_scope));
}
{
StyleScopeFrame a_frame(*a);
StyleScopeFrame b_frame(*b, &a_frame);
StyleScopeFrame c_frame(*c, &b_frame);
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_TRUE(b_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*outer_scope));
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*inner_scope));
EXPECT_TRUE(b_frame.HasSeenImplicitScope(*inner_scope));
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*inner_scope));
}
}
TEST_F(StyleScopeFrameTest, HasSeenImplicitScope_Multi) {
SetBodyInnerHTML(R"HTML(
<div id=a>
<div id=b>
<style>
@scope {
div { }
}
</style>
<div id=c>
</div>
</div>
</div>
<div id=d>
<div id=e>
<style>
@scope {
span { }
}
</style>
<div id=f>
</div>
</div>
</div>
)HTML");
Element* a = GetElementById("a");
Element* b = GetElementById("b");
Element* c = GetElementById("c");
Element* d = GetElementById("d");
Element* e = GetElementById("e");
Element* f = GetElementById("f");
ASSERT_TRUE(a && b && c && d && e && f);
HeapVector<Member<const StyleScope>, 1> b_scopes = TriggeredScopes(*b);
ASSERT_EQ(1u, b_scopes.size());
const StyleScope* b_scope = b_scopes[0];
ASSERT_TRUE(b_scope && b_scope->IsImplicit());
HeapVector<Member<const StyleScope>, 1> e_scopes = TriggeredScopes(*e);
ASSERT_EQ(1u, e_scopes.size());
const StyleScope* e_scope = e_scopes[0];
ASSERT_TRUE(e_scope && e_scope->IsImplicit());
{
StyleScopeFrame c_frame(*c);
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*b_scope));
EXPECT_FALSE(c_frame.HasSeenImplicitScope(*e_scope));
}
{
StyleScopeFrame f_frame(*f);
EXPECT_FALSE(f_frame.HasSeenImplicitScope(*b_scope));
EXPECT_TRUE(f_frame.HasSeenImplicitScope(*e_scope));
}
{
StyleScopeFrame a_frame(*a);
StyleScopeFrame b_frame(*b, &a_frame);
StyleScopeFrame c_frame(*c, &b_frame);
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*b_scope));
EXPECT_FALSE(a_frame.HasSeenImplicitScope(*e_scope));
EXPECT_TRUE(b_frame.HasSeenImplicitScope(*b_scope));
EXPECT_FALSE(b_frame.HasSeenImplicitScope(*e_scope));
EXPECT_TRUE(c_frame.HasSeenImplicitScope(*b_scope));
EXPECT_FALSE(c_frame.HasSeenImplicitScope(*e_scope));
}
{
StyleScopeFrame d_frame(*d);
StyleScopeFrame e_frame(*e, &d_frame);
StyleScopeFrame f_frame(*f, &e_frame);
EXPECT_FALSE(d_frame.HasSeenImplicitScope(*b_scope));
EXPECT_FALSE(d_frame.HasSeenImplicitScope(*e_scope));
EXPECT_FALSE(e_frame.HasSeenImplicitScope(*b_scope));
EXPECT_TRUE(e_frame.HasSeenImplicitScope(*e_scope));
EXPECT_FALSE(f_frame.HasSeenImplicitScope(*b_scope));
EXPECT_TRUE(f_frame.HasSeenImplicitScope(*e_scope));
}
}
} // namespace blink