blob: 4e1dd37bbb30d06c29a9fa60c178d8a88c17ba4b [file] [log] [blame]
// Copyright 2016 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 "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/css/css_style_sheet.h"
#include "third_party/blink/renderer/core/css/parser/css_lazy_parsing_state.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css/style_sheet_contents.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class CSSLazyParsingTest : public testing::Test {
public:
bool HasParsedProperties(StyleRule* rule) {
return rule->HasParsedProperties();
}
StyleRule* RuleAt(StyleSheetContents* sheet, wtf_size_t index) {
return To<StyleRule>(sheet->ChildRules()[index].Get());
}
protected:
Persistent<StyleSheetContents> cached_contents_;
};
TEST_F(CSSLazyParsingTest, Simple) {
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
String sheet_text = "body { background-color: red; }";
CSSParser::ParseSheet(context, style_sheet, sheet_text,
CSSDeferPropertyParsing::kYes);
StyleRule* rule = RuleAt(style_sheet, 0);
EXPECT_FALSE(HasParsedProperties(rule));
rule->Properties();
EXPECT_TRUE(HasParsedProperties(rule));
}
TEST_F(CSSLazyParsingTest, LazyParseBeforeAfter) {
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
String sheet_text =
"p::before { content: 'foo' } p .class::after { content: 'bar' } ";
CSSParser::ParseSheet(context, style_sheet, sheet_text,
CSSDeferPropertyParsing::kYes);
EXPECT_FALSE(HasParsedProperties(RuleAt(style_sheet, 0)));
EXPECT_FALSE(HasParsedProperties(RuleAt(style_sheet, 1)));
}
// Test for crbug.com/664115 where |shouldConsiderForMatchingRules| would flip
// from returning false to true if the lazy property was parsed. This is a
// dangerous API because callers will expect the set of matching rules to be
// identical if the stylesheet is not mutated.
TEST_F(CSSLazyParsingTest, ShouldConsiderForMatchingRulesDoesntChange1) {
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
String sheet_text = "p::first-letter { ,badness, } ";
CSSParser::ParseSheet(context, style_sheet, sheet_text,
CSSDeferPropertyParsing::kYes);
StyleRule* rule = RuleAt(style_sheet, 0);
EXPECT_FALSE(HasParsedProperties(rule));
EXPECT_TRUE(
rule->ShouldConsiderForMatchingRules(false /* includeEmptyRules */));
// Parse the rule.
rule->Properties();
// Now, we should still consider this for matching rules even if it is empty.
EXPECT_TRUE(HasParsedProperties(rule));
EXPECT_TRUE(
rule->ShouldConsiderForMatchingRules(false /* includeEmptyRules */));
}
// Test the same thing as above with lazy parsing off to ensure that we perform
// the optimization where possible.
TEST_F(CSSLazyParsingTest, ShouldConsiderForMatchingRulesSimple) {
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext);
auto* style_sheet = MakeGarbageCollected<StyleSheetContents>(context);
String sheet_text = "p::before { ,badness, } ";
CSSParser::ParseSheet(context, style_sheet, sheet_text,
CSSDeferPropertyParsing::kNo);
StyleRule* rule = RuleAt(style_sheet, 0);
EXPECT_TRUE(HasParsedProperties(rule));
EXPECT_FALSE(
rule->ShouldConsiderForMatchingRules(false /* includeEmptyRules */));
}
// Regression test for crbug.com/660290 where we change the underlying owning
// document from the StyleSheetContents without changing the UseCounter. This
// test ensures that the new UseCounter is used when doing new parsing work.
TEST_F(CSSLazyParsingTest, ChangeDocuments) {
auto dummy_holder = std::make_unique<DummyPageHolder>(IntSize(500, 500));
Page::InsertOrdinaryPageForTesting(&dummy_holder->GetPage());
auto* context = MakeGarbageCollected<CSSParserContext>(
kHTMLStandardMode, SecureContextMode::kInsecureContext,
CSSParserContext::kLiveProfile, &dummy_holder->GetDocument());
cached_contents_ = MakeGarbageCollected<StyleSheetContents>(context);
{
auto* sheet = MakeGarbageCollected<CSSStyleSheet>(
cached_contents_, dummy_holder->GetDocument());
DCHECK(sheet);
String sheet_text = "body { background-color: red; } p { color: orange; }";
CSSParser::ParseSheet(context, cached_contents_, sheet_text,
CSSDeferPropertyParsing::kYes);
// Parse the first property set with the first document as owner.
StyleRule* rule = RuleAt(cached_contents_, 0);
EXPECT_FALSE(HasParsedProperties(rule));
rule->Properties();
EXPECT_TRUE(HasParsedProperties(rule));
EXPECT_EQ(&dummy_holder->GetDocument(),
cached_contents_->SingleOwnerDocument());
UseCounterHelper& use_counter1 =
dummy_holder->GetDocument().Loader()->GetUseCounterHelper();
EXPECT_TRUE(
use_counter1.IsCounted(CSSPropertyID::kBackgroundColor,
UseCounterHelper::CSSPropertyType::kDefault));
EXPECT_FALSE(use_counter1.IsCounted(
CSSPropertyID::kColor, UseCounterHelper::CSSPropertyType::kDefault));
// Change owner document.
cached_contents_->UnregisterClient(sheet);
dummy_holder.reset();
}
// Ensure no stack references to oilpan objects.
ThreadState::Current()->CollectAllGarbageForTesting();
auto dummy_holder2 = std::make_unique<DummyPageHolder>(IntSize(500, 500));
Page::InsertOrdinaryPageForTesting(&dummy_holder2->GetPage());
auto* sheet2 = MakeGarbageCollected<CSSStyleSheet>(
cached_contents_, dummy_holder2->GetDocument());
EXPECT_EQ(&dummy_holder2->GetDocument(),
cached_contents_->SingleOwnerDocument());
// Parse the second property set with the second document as owner.
StyleRule* rule2 = RuleAt(cached_contents_, 1);
EXPECT_FALSE(HasParsedProperties(rule2));
rule2->Properties();
EXPECT_TRUE(HasParsedProperties(rule2));
UseCounterHelper& use_counter2 =
dummy_holder2->GetDocument().Loader()->GetUseCounterHelper();
EXPECT_TRUE(sheet2);
EXPECT_TRUE(use_counter2.IsCounted(
CSSPropertyID::kColor, UseCounterHelper::CSSPropertyType::kDefault));
EXPECT_FALSE(
use_counter2.IsCounted(CSSPropertyID::kBackgroundColor,
UseCounterHelper::CSSPropertyType::kDefault));
}
} // namespace blink