blob: b6e015281eb92c8da36cc9b21cfc390799867972 [file] [log] [blame]
// Copyright 2016 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/css_selector_watch.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
namespace blink {
class CSSSelectorWatchTest : public PageTestBase {
protected:
StyleEngine& GetStyleEngine() { return GetDocument().GetStyleEngine(); }
static const HashSet<String> AddedSelectors(const CSSSelectorWatch& watch) {
return watch.added_selectors_;
}
static const HashSet<String> RemovedSelectors(const CSSSelectorWatch& watch) {
return watch.removed_selectors_;
}
static void ClearAddedRemoved(CSSSelectorWatch&);
};
void CSSSelectorWatchTest::ClearAddedRemoved(CSSSelectorWatch& watch) {
watch.added_selectors_.clear();
watch.removed_selectors_.clear();
}
TEST_F(CSSSelectorWatchTest, RecalcOnDocumentChange) {
GetDocument().body()->setInnerHTML(R"HTML(
<div>
<span id='x' class='a'></span>
<span id='y' class='b'><span></span></span>
<span id='z'><span></span></span>
</div>
)HTML");
CSSSelectorWatch& watch = CSSSelectorWatch::From(GetDocument());
Vector<String> selectors;
selectors.push_back(".a");
watch.WatchCSSSelectors(selectors);
UpdateAllLifecyclePhasesForTest();
selectors.clear();
selectors.push_back(".b");
selectors.push_back(".c");
selectors.push_back("#nomatch");
watch.WatchCSSSelectors(selectors);
UpdateAllLifecyclePhasesForTest();
Element* x = GetDocument().getElementById(AtomicString("x"));
Element* y = GetDocument().getElementById(AtomicString("y"));
Element* z = GetDocument().getElementById(AtomicString("z"));
ASSERT_TRUE(x);
ASSERT_TRUE(y);
ASSERT_TRUE(z);
x->removeAttribute(html_names::kClassAttr);
y->removeAttribute(html_names::kClassAttr);
z->setAttribute(html_names::kClassAttr, AtomicString("c"));
ClearAddedRemoved(watch);
unsigned before_count = GetStyleEngine().StyleForElementCount();
UpdateAllLifecyclePhasesForTest();
unsigned after_count = GetStyleEngine().StyleForElementCount();
EXPECT_EQ(2u, after_count - before_count);
EXPECT_EQ(1u, AddedSelectors(watch).size());
EXPECT_TRUE(AddedSelectors(watch).Contains(".c"));
EXPECT_EQ(1u, RemovedSelectors(watch).size());
EXPECT_TRUE(RemovedSelectors(watch).Contains(".b"));
}
class CSSSelectorWatchCQTest : public CSSSelectorWatchTest {
protected:
CSSSelectorWatchCQTest() = default;
};
TEST_F(CSSSelectorWatchCQTest, ContainerQueryDisplayNone) {
CSSSelectorWatch& watch = CSSSelectorWatch::From(GetDocument());
GetDocument().body()->setInnerHTML(R"HTML(
<style>
.c #container {
container-name: c1;
container-type: inline-size;
}
.c #inner { display: none; }
@container c1 (min-width: 200px) {
.c #inner { display: inline }
}
</style>
<div id="container">
<span id="inner"></span>
</div>
)HTML");
Vector<String> selectors;
selectors.push_back("#inner");
watch.WatchCSSSelectors(selectors);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(1u, AddedSelectors(watch).size());
EXPECT_TRUE(AddedSelectors(watch).Contains("#inner"));
EXPECT_EQ(0u, RemovedSelectors(watch).size());
// Setting the class 'c' on body will make #inner display:none, but also make
// #container a container 'c1' which is flipping the span back to
// display:inline.
ClearAddedRemoved(watch);
GetDocument().body()->setAttribute(html_names::kClassAttr, AtomicString("c"));
UpdateAllLifecyclePhasesForTest();
// Element::UpdateCallbackSelectors() will both remove and add #inner in the
// two passes. First without the CQ matching, and then in an interleaved style
// and layout pass. The accounting in CSSSelectorWatch::UpdateSelectorMatches
// will make sure we up with a zero balance.
EXPECT_EQ(0u, AddedSelectors(watch).size());
EXPECT_EQ(0u, RemovedSelectors(watch).size());
}
} // namespace blink