#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "chrome/browser/enterprise/data_protection/data_protection_features.h"
#include "chrome/browser/enterprise/data_protection/data_protection_navigation_controller.h"
#include "chrome/browser/enterprise/watermark/settings.h"
#include "chrome/browser/enterprise/watermark/watermark_features.h"
#include "chrome/browser/enterprise/watermark/watermark_view.h"
#include "chrome/browser/policy/dm_token_utils.h"
#include "chrome/browser/preloading/scoped_prewarm_feature_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_factory.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/tabs/split_tab_metrics.h"
#include "chrome/browser/ui/test/test_browser_ui.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/webui/watermark/watermark_page_handler.h"
#include "chrome/browser/ui/webui/watermark/watermark_ui.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/enterprise/connectors/core/common.h"
#include "components/enterprise/connectors/core/connectors_prefs.h"
#include "components/enterprise/data_controls/core/browser/test_utils.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/policy/core/common/policy_types.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/browser/realtime/fake_url_lookup_service.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkColor.h"
namespace enterprise_watermark {
namespace {
constexpr char kMultilingualWatermarkMessage[] = R"(
THIS IS CONFIDENTIAL!
😀😀😀 草草草 www
مضحك جداً
)";
constexpr char kLongLinesWatermarkMessage[] = R"(
This is a very long line that should be split up into multiple lines
This is a shorter line
It was not split
This is another very long line that should be split up into multiple lines
)";
struct WatermarkTextParams {
const char* test_suffix;
const char* watermark_text;
};
struct WatermarkStyleParams {
const char* test_suffix;
int fill_opacity;
int outline_opacity;
int font_size;
};
constexpr SkColor kTestFillColor = SkColorSetARGB(0x2A, 0, 0, 0);
constexpr SkColor kTestOutlineColor = SkColorSetARGB(0x3D, 0, 0, 0);
constexpr int kTestFontSize =
enterprise_connectors::kWatermarkStyleFontSizeDefault;
class WatermarkBrowserTest
: public UiBrowserTest,
public testing::WithParamInterface<WatermarkTextParams> {
public:
WatermarkBrowserTest() {
scoped_feature_list_.InitAndDisableFeature(
enterprise_data_protection::kEnableSinglePageAppDataProtection);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
void NavigateToTestPage() {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/enterprise/watermark/watermark_test_page.html")));
}
bool SetWatermark(const std::string& watermark_message) {
if (auto* watermark_view = BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()) {
watermark_view->SetString(watermark_message, kTestFillColor,
kTestOutlineColor, kTestFontSize);
return true;
}
return false;
}
void ShowUi(const std::string& name) override {
base::RunLoop().RunUntilIdle();
}
bool VerifyUi() override {
const auto* const test_info =
testing::UnitTest::GetInstance()->current_test_info();
const std::string name =
std::string(test_info->name()) + "_" + GetParam().test_suffix;
return VerifyPixelUi(BrowserView::GetBrowserViewForBrowser(browser())
->contents_container(),
test_info->test_suite_name(),
name) != ui::test::ActionResult::kFailed;
}
void WaitForUserDismissal() override {}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
}
IN_PROC_BROWSER_TEST_P(WatermarkBrowserTest, WatermarkShownAfterNavigation) {
NavigateToTestPage();
ASSERT_TRUE(SetWatermark(GetParam().watermark_text));
ShowAndVerifyUi();
}
IN_PROC_BROWSER_TEST_P(WatermarkBrowserTest, WatermarkClearedAfterNavigation) {
ASSERT_TRUE(SetWatermark(GetParam().watermark_text));
NavigateToTestPage();
ShowAndVerifyUi();
}
INSTANTIATE_TEST_SUITE_P(
All,
WatermarkBrowserTest,
testing::Values(
WatermarkTextParams{"Multilingual", kMultilingualWatermarkMessage},
WatermarkTextParams{"LongLines", kLongLinesWatermarkMessage}));
class WatermarkTestPageBrowserTest : public UiBrowserTest {
public:
WatermarkTestPageBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(kEnableWatermarkTestPage);
}
void ShowUi(const std::string& name) override {
base::RunLoop().RunUntilIdle();
}
bool VerifyUi() override {
const auto* const test_info =
testing::UnitTest::GetInstance()->current_test_info();
return VerifyPixelUi(BrowserView::GetBrowserViewForBrowser(browser())
->contents_container(),
test_info->test_suite_name(),
test_info->name()) != ui::test::ActionResult::kFailed;
}
void WaitForUserDismissal() override {}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
namespace {
class FakeRealTimeUrlLookupService
: public safe_browsing::testing::FakeRealTimeUrlLookupService {
public:
FakeRealTimeUrlLookupService() = default;
void StartMaybeCachedLookup(
const GURL& url,
safe_browsing::RTLookupResponseCallback response_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
SessionID session_id,
std::optional<safe_browsing::internal::ReferringAppInfo>
referring_app_info,
bool use_cache) override {
auto response = std::make_unique<safe_browsing::RTLookupResponse>();
safe_browsing::RTLookupResponse::ThreatInfo* new_threat_info =
response->add_threat_info();
safe_browsing::MatchedUrlNavigationRule* matched_url_navigation_rule =
new_threat_info->mutable_matched_url_navigation_rule();
if (url.GetHost() == "watermark.com") {
safe_browsing::MatchedUrlNavigationRule::WatermarkMessage wm;
wm.set_watermark_message("custom_messge");
wm.mutable_timestamp()->set_seconds(base::Time::Now().ToTimeT());
*matched_url_navigation_rule->mutable_watermark_message() = wm;
}
callback_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(response_callback),
true,
true, std::move(response)));
}
};
}
class WatermarkBrowserNavigationTest : public InProcessBrowserTest {
public:
WatermarkBrowserNavigationTest() {
scoped_feature_list_.InitAndEnableFeature(features::kSideBySide);
}
WatermarkBrowserNavigationTest(const WatermarkBrowserNavigationTest&) =
delete;
WatermarkBrowserNavigationTest& operator=(
const WatermarkBrowserNavigationTest&) = delete;
void SetUpCommandLine(base::CommandLine* command_line) override {
policy::SetDMTokenForTesting(policy::DMToken::CreateValidToken("dm_token"));
auto create_service_callback =
base::BindRepeating([](content::BrowserContext* context) {
Profile* profile = Profile::FromBrowserContext(context);
profile->GetPrefs()->SetInteger(
enterprise_connectors::kEnterpriseRealTimeUrlCheckMode,
enterprise_connectors::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
profile->GetPrefs()->SetInteger(
enterprise_connectors::kEnterpriseRealTimeUrlCheckScope,
policy::POLICY_SCOPE_MACHINE);
auto testing_factory =
base::BindRepeating([](content::BrowserContext* context)
-> std::unique_ptr<KeyedService> {
return std::make_unique<FakeRealTimeUrlLookupService>();
});
safe_browsing::ChromeEnterpriseRealTimeUrlLookupServiceFactory::
GetInstance()
->SetTestingFactory(context, testing_factory);
});
create_services_subscription_ =
BrowserContextDependencyManager::GetInstance()
->RegisterCreateServicesCallbackForTesting(create_service_callback);
}
content::WebContents* NavigateAsync(const GURL& url) {
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
Navigate(¶ms);
return params.navigated_or_inserted_contents;
}
void NavigateToAndWait(const GURL& url) {
content::WaitForLoadStop(NavigateAsync(url));
}
private:
test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{
test::ScopedPrewarmFeatureList::PrewarmState::kDisabled};
base::CallbackListSubscription create_services_subscription_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest, Apply_NoWatermark) {
NavigateToAndWait(GURL("https://nowatermark.com"));
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest,
Apply_Nav_NoWatermark_Watermark) {
auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
EXPECT_FALSE(browser_view->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
base::test::TestFuture<void> future;
browser()
->GetActiveTabInterface()
->GetTabFeatures()
->data_protection_controller()
->SetCallbackForTesting(future.GetCallback());
auto* web_contents = NavigateAsync(GURL("https://watermark.com"));
EXPECT_TRUE(future.Wait());
EXPECT_TRUE(browser_view->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
content::WaitForLoadStop(web_contents);
EXPECT_TRUE(browser_view->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest,
Apply_Nav_Watermark_NoWatermark) {
NavigateToAndWait(GURL("https://watermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
auto* web_contents = NavigateAsync(GURL("https://nowatermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
content::WaitForLoadStop(web_contents);
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest,
Apply_SwitchTab_ToWatermark) {
NavigateToAndWait(GURL("https://watermark.com"));
ASSERT_TRUE(
AddTabAtIndex(1, GURL("chrome://version"), ui::PAGE_TRANSITION_LINK));
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kMouse));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest,
Apply_SwitchTab_ToWatermark_NoWait) {
NavigateToAndWait(GURL("https://watermark.com"));
NavigateParams params(browser(), GURL("chrome://version"),
ui::PAGE_TRANSITION_LINK);
params.tabstrip_index = 1;
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(¶ms);
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kMouse));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
content::WaitForLoadStop(params.navigated_or_inserted_contents);
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest,
Apply_SwitchTab_ToWatermark_PartialWait) {
NavigateToAndWait(GURL("https://watermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
NavigateParams params(browser(), GURL("https://nowatermark.com"),
ui::PAGE_TRANSITION_LINK);
params.tabstrip_index = 1;
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(¶ms);
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
EXPECT_FALSE(browser_view->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
base::test::TestFuture<void> future;
browser()
->GetActiveTabInterface()
->GetTabFeatures()
->data_protection_controller()
->SetCallbackForTesting(future.GetCallback());
EXPECT_TRUE(future.Wait());
EXPECT_FALSE(browser_view->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kMouse));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
content::WaitForLoadStop(params.navigated_or_inserted_contents);
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
IN_PROC_BROWSER_TEST_F(WatermarkBrowserNavigationTest, SplitTabWatermark) {
ASSERT_TRUE(
AddTabAtIndex(1, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
ASSERT_TRUE(
AddTabAtIndex(2, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_TYPED));
browser()->tab_strip_model()->ActivateTabAt(0);
split_tabs::SplitTabId split_id = browser()->tab_strip_model()->AddToNewSplit(
{1}, split_tabs::SplitTabVisualData(),
split_tabs::SplitTabCreatedSource::kToolbarButton);
NavigateToAndWait(GURL("https://watermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[1]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ReverseTabsInSplit(split_id);
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[1]
->watermark_view()
->has_text_for_testing());
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ActivateTabAt(2);
NavigateToAndWait(GURL("https://watermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[1]
->GetVisible());
browser()->tab_strip_model()->ActivateTabAt(1);
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[1]
->watermark_view()
->has_text_for_testing());
EXPECT_FALSE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
browser()->tab_strip_model()->ActivateTabAt(0);
NavigateToAndWait(GURL("https://watermark.com"));
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[1]
->watermark_view()
->has_text_for_testing());
EXPECT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())
->GetContentsContainerViews()[0]
->watermark_view()
->has_text_for_testing());
}
class WatermarkTestPageDynamicBrowserTest
: public UiBrowserTest,
public testing::WithParamInterface<WatermarkStyleParams> {
public:
WatermarkTestPageDynamicBrowserTest() {
scoped_feature_list_.InitAndEnableFeature(kEnableWatermarkTestPage);
}
void ShowUi(const std::string& name) override {
base::RunLoop().RunUntilIdle();
}
bool VerifyUi() override {
const auto* const test_info =
testing::UnitTest::GetInstance()->current_test_info();
const std::string name =
std::string(test_info->name()) + "_" + GetParam().test_suffix;
return VerifyPixelUi(BrowserView::GetBrowserViewForBrowser(browser())
->contents_container(),
test_info->test_suite_name(),
name) != ui::test::ActionResult::kFailed;
}
void WaitForUserDismissal() override {}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(WatermarkTestPageBrowserTest, InvokeUi_default) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(chrome::kChromeUIWatermarkURL)));
ShowAndVerifyUi();
}
IN_PROC_BROWSER_TEST_P(WatermarkTestPageDynamicBrowserTest, DynamicStyle) {
const auto& params = GetParam();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(chrome::kChromeUIWatermarkURL)));
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
auto* watermark_ui =
web_contents->GetWebUI()->GetController()->GetAs<WatermarkUI>();
ASSERT_TRUE(watermark_ui);
auto* page_handler = watermark_ui->GetPageHandlerForTesting();
ASSERT_TRUE(page_handler);
auto style = watermark::mojom::WatermarkStyle::New();
style->fill_opacity = params.fill_opacity;
style->outline_opacity = params.outline_opacity;
style->font_size = params.font_size;
page_handler->SetWatermarkStyle(std::move(style));
ShowAndVerifyUi();
}
INSTANTIATE_TEST_SUITE_P(
All,
WatermarkTestPageDynamicBrowserTest,
testing::Values(
WatermarkStyleParams{"HighOpacity", 80,
90, 24},
WatermarkStyleParams{"LargeFont", 4,
6, 72},
WatermarkStyleParams{"ZeroOpacity", 0,
0, 24}));
class WatermarkSettingsBrowserTest : public InProcessBrowserTest,
public testing::WithParamInterface<bool> {
public:
WatermarkSettingsBrowserTest() {
if (IsCustomizationEnabled()) {
scoped_feature_list_.InitAndEnableFeature(kEnableWatermarkCustomization);
} else {
scoped_feature_list_.InitAndDisableFeature(kEnableWatermarkCustomization);
}
}
bool IsCustomizationEnabled() const { return GetParam(); }
SkAlpha PercentageToSkAlpha(int percent_value) {
return std::clamp(percent_value, 0, 100) * 255 / 100;
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_P(WatermarkSettingsBrowserTest, GetStyleSettings) {
PrefService* prefs = browser()->profile()->GetPrefs();
SkColor expected_fill_color = GetDefaultFillColor();
SkColor expected_outline_color = GetDefaultOutlineColor();
int expected_font_size = GetDefaultFontSize();
EXPECT_EQ(GetFillColor(prefs), expected_fill_color);
EXPECT_EQ(GetOutlineColor(prefs), expected_outline_color);
EXPECT_EQ(GetFontSize(prefs), expected_font_size);
prefs->SetInteger(enterprise_connectors::kWatermarkStyleFillOpacityPref, 30);
prefs->SetInteger(enterprise_connectors::kWatermarkStyleOutlineOpacityPref,
40);
prefs->SetInteger(enterprise_connectors::kWatermarkStyleFontSizePref, 50);
if (IsCustomizationEnabled()) {
expected_fill_color =
SkColorSetA(SkColorSetRGB(0x00, 0x00, 0x00), PercentageToSkAlpha(30));
expected_outline_color =
SkColorSetA(SkColorSetRGB(0xff, 0xff, 0xff), PercentageToSkAlpha(40));
expected_font_size = 50;
}
EXPECT_EQ(GetFillColor(prefs), expected_fill_color);
EXPECT_EQ(GetOutlineColor(prefs), expected_outline_color);
EXPECT_EQ(GetFontSize(prefs), expected_font_size);
}
INSTANTIATE_TEST_SUITE_P(All, WatermarkSettingsBrowserTest, testing::Bool());
class WatermarkSettingsCommandLineBrowserTest : public InProcessBrowserTest {
public:
SkAlpha PercentageToSkAlpha(int percent_value) {
return std::clamp(percent_value, 0, 100) * 255 / 100;
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII("watermark-fill-opacity", "50");
command_line->AppendSwitchASCII("watermark-outline-opacity", "60");
}
private:
base::test::ScopedFeatureList scoped_feature_list_{
kEnableWatermarkCustomization};
};
IN_PROC_BROWSER_TEST_F(WatermarkSettingsCommandLineBrowserTest, GetColors) {
PrefService* prefs = browser()->profile()->GetPrefs();
EXPECT_EQ(GetFillColor(prefs), SkColorSetA(SkColorSetRGB(0x00, 0x00, 0x00),
PercentageToSkAlpha(50)));
EXPECT_EQ(GetOutlineColor(prefs), SkColorSetA(SkColorSetRGB(0xff, 0xff, 0xff),
PercentageToSkAlpha(60)));
}
}