Commit f9954f1c authored by Rune Lillesveen's avatar Rune Lillesveen Committed by Commit Bot

Introduce SelectorFilterAncestorScope for style recalc roots.

We currently push a SelectorFilterParentScope on the stack for each
element in the ancestor chain while matching style rules. In order to
support starting style recalc at a root element which is not the
document root, we need to be able to push a scope for an ancestor chain
instead of a single element. This CL introduces a subclass for doing
that. Also added some simple unit tests.

Bug: 868810
Change-Id: I23dd49ab1b9d02efe5f281ca2dbec773d699bc0e
Reviewed-on: https://chromium-review.googlesource.com/1219106Reviewed-by: default avatarAnders Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590280}
parent e1c73235
...@@ -1766,6 +1766,7 @@ jumbo_source_set("unit_tests") { ...@@ -1766,6 +1766,7 @@ jumbo_source_set("unit_tests") {
"css/resolver/font_builder_test.cc", "css/resolver/font_builder_test.cc",
"css/resolver/font_style_resolver_test.cc", "css/resolver/font_style_resolver_test.cc",
"css/resolver/match_result_test.cc", "css/resolver/match_result_test.cc",
"css/resolver/selector_filter_parent_scope_test.cc",
"css/resolver/style_adjuster_test.cc", "css/resolver/style_adjuster_test.cc",
"css/rule_feature_set_test.cc", "css/rule_feature_set_test.cc",
"css/rule_set_test.cc", "css/rule_set_test.cc",
......
...@@ -8,4 +8,18 @@ namespace blink { ...@@ -8,4 +8,18 @@ namespace blink {
SelectorFilterParentScope* SelectorFilterParentScope::current_scope_ = nullptr; SelectorFilterParentScope* SelectorFilterParentScope::current_scope_ = nullptr;
void SelectorFilterParentScope::PushAncestors(Element& element) {
if (Element* ancestor = element.ParentOrShadowHostElement()) {
PushAncestors(*ancestor);
resolver_->GetSelectorFilter().PushParent(*ancestor);
}
}
void SelectorFilterParentScope::PopAncestors(Element& element) {
if (Element* ancestor = element.ParentOrShadowHostElement()) {
resolver_->GetSelectorFilter().PopParent(*ancestor);
PopAncestors(*ancestor);
}
}
} // namespace blink } // namespace blink
...@@ -12,30 +12,55 @@ ...@@ -12,30 +12,55 @@
namespace blink { namespace blink {
// Maintains the parent element stack (and bloom filter) inside recalcStyle. // Maintains the parent element stack (and bloom filter) inside RecalcStyle.
class SelectorFilterParentScope final { // SelectorFilterParentScope for the parent element is added to the stack before
// recalculating style for its children. The bloom filter is populated lazily by
// PushParentIfNeeded().
class CORE_EXPORT SelectorFilterParentScope {
STACK_ALLOCATED(); STACK_ALLOCATED();
public: public:
explicit SelectorFilterParentScope(Element& parent); explicit SelectorFilterParentScope(Element& parent)
: SelectorFilterParentScope(parent, ScopeType::kParent) {}
~SelectorFilterParentScope(); ~SelectorFilterParentScope();
static void EnsureParentStackIsPushed(); static void EnsureParentStackIsPushed();
protected:
enum class ScopeType { kParent, kAncestors };
SelectorFilterParentScope(Element& parent, ScopeType scope);
private: private:
void PushParentIfNeeded(); void PushParentIfNeeded();
void PushAncestors(Element&);
void PopAncestors(Element&);
Member<Element> parent_; Member<Element> parent_;
bool pushed_; bool pushed_ = false;
ScopeType scope_type_;
SelectorFilterParentScope* previous_; SelectorFilterParentScope* previous_;
Member<StyleResolver> resolver_; Member<StyleResolver> resolver_;
static SelectorFilterParentScope* current_scope_; static SelectorFilterParentScope* current_scope_;
}; };
inline SelectorFilterParentScope::SelectorFilterParentScope(Element& parent) // When starting the style recalc from an element which is not the root, we push
// an object of this class onto the stack to push all ancestors instead of
// having to push a SelectorFilterParentScope for each of the ancestors.
class CORE_EXPORT SelectorFilterAncestorScope final
: private SelectorFilterParentScope {
STACK_ALLOCATED();
public:
explicit SelectorFilterAncestorScope(Element& parent)
: SelectorFilterParentScope(parent, ScopeType::kAncestors) {}
};
inline SelectorFilterParentScope::SelectorFilterParentScope(
Element& parent,
ScopeType scope_type)
: parent_(parent), : parent_(parent),
pushed_(false), scope_type_(scope_type),
previous_(current_scope_), previous_(current_scope_),
resolver_(parent.GetDocument().GetStyleResolver()) { resolver_(parent.GetDocument().GetStyleResolver()) {
DCHECK(parent.GetDocument().InStyleRecalc()); DCHECK(parent.GetDocument().InStyleRecalc());
...@@ -47,6 +72,8 @@ inline SelectorFilterParentScope::~SelectorFilterParentScope() { ...@@ -47,6 +72,8 @@ inline SelectorFilterParentScope::~SelectorFilterParentScope() {
if (!pushed_) if (!pushed_)
return; return;
resolver_->GetSelectorFilter().PopParent(*parent_); resolver_->GetSelectorFilter().PopParent(*parent_);
if (scope_type_ == ScopeType::kAncestors)
PopAncestors(*parent_);
} }
inline void SelectorFilterParentScope::EnsureParentStackIsPushed() { inline void SelectorFilterParentScope::EnsureParentStackIsPushed() {
...@@ -57,8 +84,11 @@ inline void SelectorFilterParentScope::EnsureParentStackIsPushed() { ...@@ -57,8 +84,11 @@ inline void SelectorFilterParentScope::EnsureParentStackIsPushed() {
inline void SelectorFilterParentScope::PushParentIfNeeded() { inline void SelectorFilterParentScope::PushParentIfNeeded() {
if (pushed_) if (pushed_)
return; return;
DCHECK(!previous_ || scope_type_ == ScopeType::kParent);
if (previous_) if (previous_)
previous_->PushParentIfNeeded(); previous_->PushParentIfNeeded();
if (scope_type_ == ScopeType::kAncestors)
PushAncestors(*parent_);
resolver_->GetSelectorFilter().PushParent(*parent_); resolver_->GetSelectorFilter().PushParent(*parent_);
pushed_ = true; pushed_ = true;
} }
......
// Copyright 2018 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/resolver/selector_filter_parent_scope.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
namespace blink {
class SelectorFilterParentScopeTest : public testing::Test {
protected:
void SetUp() override {
dummy_page_holder_ = DummyPageHolder::Create(IntSize(800, 600));
}
Document& GetDocument() { return dummy_page_holder_->GetDocument(); }
static constexpr size_t max_identifier_hashes = 4;
private:
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
};
TEST_F(SelectorFilterParentScopeTest, ParentScope) {
GetDocument().body()->setAttribute(HTMLNames::classAttr, "match");
GetDocument().documentElement()->SetIdAttribute("myId");
SelectorFilter& filter =
GetDocument().EnsureStyleResolver().GetSelectorFilter();
GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
SelectorFilterParentScope html_scope(*GetDocument().documentElement());
{
SelectorFilterParentScope body_scope(*GetDocument().body());
SelectorFilterParentScope::EnsureParentStackIsPushed();
CSSSelectorList selectors = CSSParser::ParseSelector(
CSSParserContext::Create(kHTMLStandardMode,
SecureContextMode::kInsecureContext),
nullptr, "html, body, .match, #myId");
for (const CSSSelector* selector = selectors.First(); selector;
selector = CSSSelectorList::Next(*selector)) {
unsigned selector_hashes[max_identifier_hashes];
filter.CollectIdentifierHashes(*selector, selector_hashes,
max_identifier_hashes);
EXPECT_FALSE(
filter.FastRejectSelector<max_identifier_hashes>(selector_hashes));
}
}
}
TEST_F(SelectorFilterParentScopeTest, AncestorScope) {
GetDocument().body()->SetInnerHTMLFromString(R"HTML(
<div class=x>
<span id=y></span>
</div>
)HTML");
SelectorFilter& filter =
GetDocument().EnsureStyleResolver().GetSelectorFilter();
GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInStyleRecalc);
SelectorFilterAncestorScope span_scope(*GetDocument().getElementById("y"));
SelectorFilterParentScope::EnsureParentStackIsPushed();
CSSSelectorList selectors = CSSParser::ParseSelector(
CSSParserContext::Create(kHTMLStandardMode,
SecureContextMode::kInsecureContext),
nullptr, "html, body, div, span, .x, #y");
for (const CSSSelector* selector = selectors.First(); selector;
selector = CSSSelectorList::Next(*selector)) {
unsigned selector_hashes[max_identifier_hashes];
filter.CollectIdentifierHashes(*selector, selector_hashes,
max_identifier_hashes);
EXPECT_FALSE(
filter.FastRejectSelector<max_identifier_hashes>(selector_hashes));
}
}
} // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment