blob: 1979b42a6848d549d5a550376e7f4d17861de286 [file] [log] [blame]
// 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/style_environment_variables.h"
#include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/html_element.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/loader/fetch/substitute_data.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
namespace blink {
namespace {
static const char kVariableName[] = "test";
// red
static const Color kTestColorRed = Color(255, 0, 0);
static const char kVariableTestColor[] = "red";
// blue
static const Color kAltTestColor = Color(0, 0, 255);
static const char kVariableAltTestColor[] = "blue";
// no set
static const Color kNoColor = Color(0, 0, 0, 0);
static const char kSafeAreaInsetExpectedDefault[] = "0px";
} // namespace
class StyleEnvironmentVariablesTest : public PageTestBase {
public:
void SetUp() override {
PageTestBase::SetUp();
// Needed for RecordUseCounter_IgnoreMediaControls.
RuntimeEnabledFeatures::SetModernMediaControlsEnabled(true);
}
void TearDown() override {
StyleEnvironmentVariables::GetRootInstance().ClearForTesting();
}
DocumentStyleEnvironmentVariables& GetDocumentVariables() {
return GetStyleEngine().EnsureEnvironmentVariables();
}
void InitializeWithHTML(LocalFrame& frame, const String& html_content) {
// Sets the inner html and runs the document lifecycle.
frame.GetDocument()->body()->SetInnerHTMLFromString(html_content);
frame.GetDocument()->View()->UpdateAllLifecyclePhases(
DocumentLifecycle::LifecycleUpdateReason::kTest);
}
void InitializeTestPageWithVariableNamed(LocalFrame& frame,
const String& name) {
InitializeWithHTML(frame,
"<style>"
" #target { background-color: env(" +
name +
"); }"
"</style>"
"<div>"
" <div id=target></div>"
"</div>");
}
void InitializeTestPageWithVariableNamed(LocalFrame& frame,
const UADefinedVariable name) {
InitializeTestPageWithVariableNamed(
frame, StyleEnvironmentVariables::GetVariableName(name));
}
void SimulateNavigation() {
const KURL& url = KURL(NullURL(), "https://www.example.com");
GetDocument().GetFrame()->Loader().CommitNavigation(
ResourceRequest(url), SubstituteData(SharedBuffer::Create()),
ClientRedirectPolicy::kNotClientRedirect,
base::UnguessableToken::Create());
blink::test::RunPendingTasks();
ASSERT_EQ(url.GetString(), GetDocument().Url().GetString());
}
const String& GetRootVariableValue(UADefinedVariable name) {
CSSVariableData* data =
StyleEnvironmentVariables::GetRootInstance().ResolveVariable(
StyleEnvironmentVariables::GetVariableName(name));
EXPECT_NE(nullptr, data);
return data->BackingStrings()[0];
}
};
TEST_F(StyleEnvironmentVariablesTest, DocumentVariable_AfterLoad) {
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, DocumentVariable_Change) {
GetDocumentVariables().SetVariable(kVariableName, kVariableAltTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Change the variable value after we have loaded the page.
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest,
DocumentVariable_Override_RemoveDocument) {
// Set the variable globally.
StyleEnvironmentVariables::GetRootInstance().SetVariable(
kVariableName, kVariableAltTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the global
// variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kAltTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Change the variable value on the document after we have loaded the page.
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the document
// variable.
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Remove the document variable.
GetDocumentVariables().RemoveVariable(kVariableName);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the global
// variable.
EXPECT_EQ(kAltTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, DocumentVariable_Override_RemoveGlobal) {
// Set the variable globally.
StyleEnvironmentVariables::GetRootInstance().SetVariable(
kVariableName, kVariableAltTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the global
// variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kAltTestColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Change the variable value on the document after we have loaded the page.
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the document
// variable.
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Remove the global variable.
StyleEnvironmentVariables::GetRootInstance().RemoveVariable(kVariableName);
// Ensure that the document has not been invalidated.
EXPECT_FALSE(GetDocument().NeedsLayoutTreeUpdate());
}
TEST_F(StyleEnvironmentVariablesTest, DocumentVariable_Preset) {
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, DocumentVariable_Remove) {
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Change the variable value after we have loaded the page.
GetDocumentVariables().RemoveVariable(kVariableName);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element does not have the background color any more.
EXPECT_NE(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, MultiDocumentInvalidation_FromRoot) {
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Create a second page that uses the variable.
std::unique_ptr<DummyPageHolder> new_page =
DummyPageHolder::Create(IntSize(800, 600));
InitializeTestPageWithVariableNamed(new_page->GetFrame(), kVariableName);
// Create an empty page that does not use the variable.
std::unique_ptr<DummyPageHolder> empty_page =
DummyPageHolder::Create(IntSize(800, 600));
empty_page->GetDocument().View()->UpdateAllLifecyclePhases(
DocumentLifecycle::LifecycleUpdateReason::kTest);
StyleEnvironmentVariables::GetRootInstance().SetVariable(kVariableName,
kVariableTestColor);
// The first two pages should be invalidated and the empty one should not.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
EXPECT_TRUE(new_page->GetDocument().NeedsLayoutTreeUpdate());
EXPECT_FALSE(empty_page->GetDocument().NeedsLayoutTreeUpdate());
}
TEST_F(StyleEnvironmentVariablesTest, MultiDocumentInvalidation_FromDocument) {
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Create a second page that uses the variable.
std::unique_ptr<DummyPageHolder> new_page =
DummyPageHolder::Create(IntSize(800, 600));
InitializeTestPageWithVariableNamed(new_page->GetFrame(), kVariableName);
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Only the first document should be invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
EXPECT_FALSE(new_page->GetDocument().NeedsLayoutTreeUpdate());
}
TEST_F(StyleEnvironmentVariablesTest, NavigateToClear) {
GetDocumentVariables().SetVariable(kVariableName, kVariableTestColor);
// Simulate a navigation to clear the variables.
SimulateNavigation();
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has no background color.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kNoColor, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, GlobalVariable_AfterLoad) {
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
StyleEnvironmentVariables::GetRootInstance().SetVariable(kVariableName,
kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, GlobalVariable_Change) {
StyleEnvironmentVariables::GetRootInstance().SetVariable(
kVariableName, kVariableAltTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Change the variable value after we have loaded the page.
StyleEnvironmentVariables::GetRootInstance().SetVariable(kVariableName,
kVariableTestColor);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, GlobalVariable_DefaultsPresent) {
EXPECT_EQ(kSafeAreaInsetExpectedDefault,
GetRootVariableValue(UADefinedVariable::kSafeAreaInsetTop));
EXPECT_EQ(kSafeAreaInsetExpectedDefault,
GetRootVariableValue(UADefinedVariable::kSafeAreaInsetLeft));
EXPECT_EQ(kSafeAreaInsetExpectedDefault,
GetRootVariableValue(UADefinedVariable::kSafeAreaInsetBottom));
EXPECT_EQ(kSafeAreaInsetExpectedDefault,
GetRootVariableValue(UADefinedVariable::kSafeAreaInsetRight));
EXPECT_EQ(
nullptr,
StyleEnvironmentVariables::GetRootInstance().ResolveVariable("test"));
}
TEST_F(StyleEnvironmentVariablesTest, GlobalVariable_Preset) {
StyleEnvironmentVariables::GetRootInstance().SetVariable(kVariableName,
kVariableTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest, GlobalVariable_Remove) {
StyleEnvironmentVariables::GetRootInstance().SetVariable(kVariableName,
kVariableTestColor);
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
// Check that the element has the background color provided by the variable.
Element* target = GetDocument().getElementById("target");
EXPECT_EQ(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
// Change the variable value after we have loaded the page.
StyleEnvironmentVariables::GetRootInstance().RemoveVariable(kVariableName);
// Ensure that the document has been invalidated.
EXPECT_TRUE(GetDocument().NeedsLayoutTreeUpdate());
UpdateAllLifecyclePhasesForTest();
// Check that the element does not have the background color any more.
EXPECT_NE(kTestColorRed, target->ComputedStyleRef().VisitedDependentColor(
GetCSSPropertyBackgroundColor()));
}
TEST_F(StyleEnvironmentVariablesTest,
DISABLED_PrintExpectedVariableNameHashes) {
const UADefinedVariable variables[] = {
UADefinedVariable::kSafeAreaInsetTop,
UADefinedVariable::kSafeAreaInsetLeft,
UADefinedVariable::kSafeAreaInsetRight,
UADefinedVariable::kSafeAreaInsetBottom};
for (const auto& variable : variables) {
const AtomicString name =
StyleEnvironmentVariables::GetVariableName(variable);
printf("0x%x\n",
DocumentStyleEnvironmentVariables::GenerateHashFromName(name));
}
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_IgnoreMediaControls) {
InitializeWithHTML(GetFrame(), "<video controls />");
EXPECT_FALSE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
EXPECT_FALSE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetTop));
EXPECT_FALSE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetLeft));
EXPECT_FALSE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetBottom));
EXPECT_FALSE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetRight));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_InvalidProperty) {
InitializeTestPageWithVariableNamed(GetFrame(), kVariableName);
EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_NoVariable) {
InitializeWithHTML(GetFrame(), "");
EXPECT_FALSE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_SafeAreaInsetBottom) {
InitializeTestPageWithVariableNamed(GetFrame(),
UADefinedVariable::kSafeAreaInsetBottom);
EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
EXPECT_TRUE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetBottom));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_SafeAreaInsetLeft) {
InitializeTestPageWithVariableNamed(GetFrame(),
UADefinedVariable::kSafeAreaInsetLeft);
EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
EXPECT_TRUE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetLeft));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_SafeAreaInsetRight) {
InitializeTestPageWithVariableNamed(GetFrame(),
UADefinedVariable::kSafeAreaInsetRight);
EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
EXPECT_TRUE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetRight));
}
TEST_F(StyleEnvironmentVariablesTest, RecordUseCounter_SafeAreaInsetTop) {
InitializeTestPageWithVariableNamed(GetFrame(),
UADefinedVariable::kSafeAreaInsetTop);
EXPECT_TRUE(UseCounter::IsCounted(GetDocument(),
WebFeature::kCSSEnvironmentVariable));
EXPECT_TRUE(UseCounter::IsCounted(
GetDocument(), WebFeature::kCSSEnvironmentVariable_SafeAreaInsetTop));
}
} // namespace blink