blob: a75cb131c968e1f5ebf525e05b79371f52f8479f [file] [log] [blame]
// Copyright 2019 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 <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/engagement/site_engagement_score.h"
#include "chrome/browser/engagement/site_engagement_service.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_test_utils.h"
#include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h"
#include "chrome/browser/lookalikes/safety_tips/safety_tip_test_utils.h"
#include "chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.h"
#include "chrome/browser/lookalikes/safety_tips/safety_tips.pb.h"
#include "chrome/browser/lookalikes/safety_tips/safety_tips_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
#include "chrome/browser/ui/views/page_info/page_info_bubble_view_base.h"
#include "chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/safe_browsing/db/v4_protocol_manager_util.h"
#include "components/security_interstitials/core/common_string_util.h"
#include "components/security_state/core/features.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/referrer.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/gfx/range/range.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace {
enum class UIStatus {
kDisabled,
kEnabled,
kEnabledWithAllFeatures,
};
// An engagement score above MEDIUM.
const int kHighEngagement = 20;
// An engagement score below MEDIUM.
const int kLowEngagement = 1;
class ClickEvent : public ui::Event {
public:
ClickEvent() : ui::Event(ui::ET_UNKNOWN, base::TimeTicks(), 0) {}
};
// Simulates a link click navigation. We don't use
// ui_test_utils::NavigateToURL(const GURL&) because it simulates the user
// typing the URL, causing the site to have a site engagement score of at
// least LOW.
//
// This function waits for the load to complete since it is based on the
// synchronous ui_test_utils::NavigatToURL.
void NavigateToURL(Browser* browser,
const GURL& url,
WindowOpenDisposition disposition) {
NavigateParams params(browser, url, ui::PAGE_TRANSITION_LINK);
params.initiator_origin = url::Origin::Create(GURL("about:blank"));
params.disposition = disposition;
params.is_renderer_initiated = true;
ui_test_utils::NavigateToURL(&params);
}
void PerformMouseClickOnView(views::View* view) {
ui::AXActionData data;
data.action = ax::mojom::Action::kDoDefault;
view->HandleAccessibleAction(data);
}
bool IsUIShowing() {
return PageInfoBubbleViewBase::BUBBLE_SAFETY_TIP ==
PageInfoBubbleViewBase::GetShownBubbleType();
}
void CloseWarningIgnore(views::Widget::ClosedReason reason) {
if (!PageInfoBubbleViewBase::GetPageInfoBubbleForTesting()) {
return;
}
auto* widget =
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting()->GetWidget();
views::test::WidgetDestroyedWaiter waiter(widget);
widget->CloseWithReason(reason);
waiter.Wait();
}
// Sets the absolute Site Engagement |score| for the testing origin.
void SetEngagementScore(Browser* browser, const GURL& url, double score) {
SiteEngagementService::Get(browser->profile())
->ResetBaseScoreForURL(url, score);
}
// Clicks the location icon to open the page info bubble.
void OpenPageInfoBubble(Browser* browser) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
LocationIconView* location_icon_view =
browser_view->toolbar()->location_bar()->location_icon_view();
ASSERT_TRUE(location_icon_view);
ClickEvent event;
location_icon_view->ShowBubble(event);
views::BubbleDialogDelegateView* page_info =
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting();
EXPECT_NE(nullptr, page_info);
page_info->set_close_on_deactivate(false);
}
// Go to |url| in such a way as to trigger a bad reputation safety tip. This is
// just for convenience, since how we trigger warnings will change. Even if the
// warning is triggered, it may not be shown if the URL is opened in the
// background.
//
// This function blocks the entire host + path, ignoring query parameters.
void TriggerWarning(Browser* browser,
const GURL& url,
WindowOpenDisposition disposition) {
std::string host;
std::string path;
std::string query;
safe_browsing::V4ProtocolManagerUtil::CanonicalizeUrl(url, &host, &path,
&query);
// For simplicity, ignore query
SetSafetyTipBadRepPatterns({host + path});
SetEngagementScore(browser, url, kLowEngagement);
NavigateToURL(browser, url, disposition);
}
} // namespace
class SafetyTipPageInfoBubbleViewBrowserTest
: public InProcessBrowserTest,
public testing::WithParamInterface<UIStatus> {
protected:
UIStatus ui_status() const { return GetParam(); }
void SetUp() override {
switch (ui_status()) {
case UIStatus::kDisabled:
feature_list_.InitAndDisableFeature(
security_state::features::kSafetyTipUI);
break;
case UIStatus::kEnabled:
feature_list_.InitWithFeaturesAndParameters(
{{security_state::features::kSafetyTipUI, {}},
{features::kLookalikeUrlNavigationSuggestionsUI,
{{"topsites", "true"}}}},
{});
break;
case UIStatus::kEnabledWithAllFeatures:
feature_list_.InitWithFeaturesAndParameters(
{{security_state::features::kSafetyTipUI,
{{"topsites", "true"},
{"editdistance", "true"},
{"editdistance_siteengagement", "true"}}},
{features::kLookalikeUrlNavigationSuggestionsUI,
{{"topsites", "true"}}}},
{});
}
InitializeSafetyTipConfig();
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
GURL GetURL(const char* hostname) const {
return embedded_test_server()->GetURL(hostname, "/title1.html");
}
GURL GetURLWithoutPath(const char* hostname) const {
return GetURL(hostname).GetWithEmptyPath();
}
void ClickLeaveButton() {
// This class is a friend to SafetyTipPageInfoBubbleView.
auto* bubble = static_cast<SafetyTipPageInfoBubbleView*>(
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting());
PerformMouseClickOnView(bubble->GetLeaveButtonForTesting());
}
void ClickLearnMoreLink() {
// This class is a friend to SafetyTipPageInfoBubbleView.
auto* bubble = static_cast<SafetyTipPageInfoBubbleView*>(
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting());
bubble->StyledLabelLinkClicked(bubble->GetLearnMoreLinkForTesting(),
gfx::Range(), 0);
}
void CloseWarningLeaveSite(Browser* browser) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
content::TestNavigationObserver navigation_observer(
browser->tab_strip_model()->GetActiveWebContents(), 1);
ClickLeaveButton();
navigation_observer.Wait();
}
bool IsUIShowingIfEnabled() {
return ui_status() == UIStatus::kDisabled ? true : IsUIShowing();
}
bool IsUIShowingOnlyIfFeaturesEnabled() {
return ui_status() == UIStatus::kEnabledWithAllFeatures ? IsUIShowing()
: !IsUIShowing();
}
void CheckPageInfoShowsSafetyTipInfo(
Browser* browser,
security_state::SafetyTipStatus expected_safety_tip_status,
const GURL& expected_safe_url) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
OpenPageInfoBubble(browser);
auto* page_info = static_cast<SafetyTipPageInfoBubbleView*>(
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting());
ASSERT_TRUE(page_info);
switch (expected_safety_tip_status) {
case security_state::SafetyTipStatus::kBadReputation:
EXPECT_EQ(page_info->GetWindowTitle(),
l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE));
break;
case security_state::SafetyTipStatus::kLookalike:
EXPECT_EQ(page_info->GetWindowTitle(),
l10n_util::GetStringFUTF16(
IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_TITLE,
security_interstitials::common_string_util::
GetFormattedHostName(expected_safe_url)));
break;
case security_state::SafetyTipStatus::kBadKeyword:
case security_state::SafetyTipStatus::kUnknown:
case security_state::SafetyTipStatus::kNone:
NOTREACHED();
break;
}
EXPECT_EQ(page_info->GetSecurityDescriptionType(),
PageInfoUI::SecurityDescriptionType::SAFETY_TIP);
}
void CheckPageInfoDoesNotShowSafetyTipInfo(Browser* browser) {
OpenPageInfoBubble(browser);
auto* page_info = static_cast<PageInfoBubbleViewBase*>(
PageInfoBubbleViewBase::GetPageInfoBubbleForTesting());
ASSERT_TRUE(page_info);
EXPECT_NE(page_info->GetWindowTitle(),
l10n_util::GetStringUTF16(
IDS_PAGE_INFO_SAFETY_TIP_BAD_REPUTATION_TITLE));
EXPECT_NE(page_info->GetSecurityDescriptionType(),
PageInfoUI::SecurityDescriptionType::SAFETY_TIP);
}
private:
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(,
SafetyTipPageInfoBubbleViewBrowserTest,
::testing::Values(UIStatus::kDisabled,
UIStatus::kEnabled,
UIStatus::kEnabledWithAllFeatures));
// Ensure normal sites with low engagement are not blocked.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NoShowOnLowEngagement) {
auto kNavigatedUrl = GetURL("site1.com");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// Ensure blocked sites with high engagement are not blocked.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NoShowOnHighEngagement) {
auto kNavigatedUrl = GetURL("site1.com");
SetSafetyTipBadRepPatterns({"site1.com/"});
SetEngagementScore(browser(), kNavigatedUrl, kHighEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// Ensure blocked sites get blocked.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest, ShowOnBlock) {
auto kNavigatedUrl = GetURL("site1.com");
SetSafetyTipBadRepPatterns({"site1.com/"});
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingIfEnabled());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kBadReputation, GURL()));
}
// Ensure explicitly-allowed sites don't get blocked when the site is otherwise
// blocked server-side.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NoShowOnAllowlist) {
auto kNavigatedUrl = GetURL("site1.com");
// Ensure a Safety Tip is triggered initially...
SetSafetyTipBadRepPatterns({"site1.com/"});
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingIfEnabled());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kBadReputation, GURL()));
// ...but suppressed by the allowlist.
SetSafetyTipAllowlistPatterns({"site1.com/"});
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// After the user clicks 'leave site', the user should end up on a safe domain.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
LeaveSiteLeavesSite) {
auto kNavigatedUrl = GetURL("site1.com");
if (ui_status() == UIStatus::kDisabled) {
return;
}
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningLeaveSite(browser());
EXPECT_FALSE(IsUIShowing());
EXPECT_NE(kNavigatedUrl,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// Test that clicking 'learn more' opens a help center article.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
LearnMoreOpensHelpCenter) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
content::WebContentsAddedObserver new_tab_observer;
ClickLearnMoreLink();
EXPECT_NE(kNavigatedUrl, new_tab_observer.GetWebContents()->GetURL());
}
// If the user clicks 'leave site', the warning should re-appear when the user
// re-visits the page.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
LeaveSiteStillWarnsAfter) {
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningLeaveSite(browser());
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingIfEnabled());
EXPECT_EQ(kNavigatedUrl,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kBadReputation, GURL()));
}
// After the user closes the warning, they should still be on the same domain.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
IgnoreWarningStaysOnPage) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked);
EXPECT_FALSE(IsUIShowing());
EXPECT_EQ(kNavigatedUrl,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kBadReputation, GURL()));
}
// If the user closes the bubble, the warning should not re-appear when the user
// re-visits the page.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
IgnoreWarningStopsWarning) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
EXPECT_EQ(kNavigatedUrl,
browser()->tab_strip_model()->GetActiveWebContents()->GetURL());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// Non main-frame navigations should be ignored.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
IgnoreIFrameNavigations) {
const GURL kNavigatedUrl =
embedded_test_server()->GetURL("a.com", "/iframe_cross_site.html");
const GURL kFrameUrl =
embedded_test_server()->GetURL("b.com", "/title1.html");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
SetSafetyTipBadRepPatterns({"a.com/"});
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingIfEnabled());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kBadReputation, GURL()));
}
// Background tabs shouldn't open a bubble initially, but should when they
// become visible.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
BubbleWaitsForVisible) {
auto kFlaggedUrl = GetURL("site1.com");
TriggerWarning(browser(), kFlaggedUrl,
WindowOpenDisposition::NEW_BACKGROUND_TAB);
EXPECT_FALSE(IsUIShowing());
auto* tab_strip = browser()->tab_strip_model();
tab_strip->ActivateTabAt(tab_strip->active_index() + 1);
EXPECT_TRUE(IsUIShowingIfEnabled());
}
// Tests that Safety Tips do NOT trigger on lookalike domains that trigger an
// interstitial.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
SkipLookalikeInterstitialed) {
const GURL kNavigatedUrl = GetURL("googlé.com");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
}
// Tests that Safety Tips trigger on lookalike domains that don't qualify for an
// interstitial, but do not impact Page Info.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
TriggersOnLookalike) {
// This domain is a lookalike of a top domain not in the top 500.
const GURL kNavigatedUrl = GetURL("googlé.sk");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingOnlyIfFeaturesEnabled());
if (ui_status() == UIStatus::kEnabledWithAllFeatures) {
ASSERT_NO_FATAL_FAILURE(CheckPageInfoShowsSafetyTipInfo(
browser(), security_state::SafetyTipStatus::kLookalike,
GURL("https://google.sk")));
} else {
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
}
// Tests that Safety Tips don't trigger on lookalike domains that are explicitly
// allowed by the allowlist.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NoTriggersOnLookalikeAllowlist) {
// This domain is a lookalike of a top domain not in the top 500.
const GURL kNavigatedUrl = GetURL("googlé.sk");
// Ensure a Safety Tip is triggered initially...
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_TRUE(IsUIShowingOnlyIfFeaturesEnabled());
// ...but suppressed by the allowlist.
SetSafetyTipAllowlistPatterns({"xn--googl-fsa.sk/"});
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
}
// Tests that Safety Tips trigger (or not) on lookalike domains with edit
// distance when enabled, and not otherwise.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
TriggersOnEditDistance) {
// This domain is an edit distance of one from the top 500.
const GURL kNavigatedUrl = GetURL("goooglé.com");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_EQ(IsUIShowing(), ui_status() == UIStatus::kEnabledWithAllFeatures);
}
// Tests that the SafetyTipShown histogram triggers correctly.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
SafetyTipShownHistogram) {
const char kHistogramName[] = "Security.SafetyTips.SafetyTipShown";
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site1.com");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
histograms.ExpectBucketCount(kHistogramName,
security_state::SafetyTipStatus::kNone, 1);
auto kBadRepUrl = GetURL("site2.com");
TriggerWarning(browser(), kBadRepUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningLeaveSite(browser());
histograms.ExpectBucketCount(
kHistogramName, security_state::SafetyTipStatus::kBadReputation, 1);
const GURL kLookalikeUrl = GetURL("googlé.sk");
SetEngagementScore(browser(), kLookalikeUrl, kLowEngagement);
NavigateToURL(browser(), kLookalikeUrl, WindowOpenDisposition::CURRENT_TAB);
histograms.ExpectBucketCount(
kHistogramName, security_state::SafetyTipStatus::kLookalike,
ui_status() == UIStatus::kEnabledWithAllFeatures ? 1 : 0);
histograms.ExpectTotalCount(kHistogramName, 3);
}
// Tests that the SafetyTipIgnoredPageLoad histogram triggers correctly.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
SafetyTipIgnoredPageLoadHistogram) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
histograms.ExpectBucketCount("Security.SafetyTips.SafetyTipIgnoredPageLoad",
security_state::SafetyTipStatus::kBadReputation,
1);
}
// Tests that Safety Tip interactions are recorded in a histogram.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
InteractionsHistogram) {
const std::string kHistogramPrefix = "Security.SafetyTips.Interaction.";
// These histograms are only recorded when the UI feature is enabled, so bail
// out when disabled.
if (ui_status() != UIStatus::kEnabledWithAllFeatures) {
return;
}
{
// This domain is an edit distance of one from the top 500.
const GURL kNavigatedUrl = GetURL("goooglé.com");
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
base::HistogramTester histogram_tester;
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
// The histogram should not be recorded until the user has interacted with
// the safety tip.
histogram_tester.ExpectTotalCount(kHistogramPrefix + "SafetyTip_Lookalike",
0);
CloseWarningLeaveSite(browser());
histogram_tester.ExpectUniqueSample(
kHistogramPrefix + "SafetyTip_Lookalike",
safety_tips::SafetyTipInteraction::kLeaveSite, 1);
}
{
base::HistogramTester histogram_tester;
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
// The histogram should not be recorded until the user has interacted with
// the safety tip.
histogram_tester.ExpectTotalCount(
kHistogramPrefix + "SafetyTip_BadReputation", 0);
CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismiss, 1);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismissWithClose, 1);
}
// Test that the specific dismissal type is recorded correctly.
{
base::HistogramTester histogram_tester;
auto kNavigatedUrl = GetURL("site2.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
CloseWarningIgnore(views::Widget::ClosedReason::kEscKeyPressed);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismiss, 1);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismissWithEsc, 1);
}
{
base::HistogramTester histogram_tester;
auto kNavigatedUrl = GetURL("site3.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
CloseWarningIgnore(views::Widget::ClosedReason::kCancelButtonClicked);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismiss, 1);
histogram_tester.ExpectBucketCount(
kHistogramPrefix + "SafetyTip_BadReputation",
safety_tips::SafetyTipInteraction::kDismissWithIgnore, 1);
}
}
// Tests that the histograms recording how long the Safety Tip is open are
// recorded properly.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
TimeOpenHistogram) {
if (ui_status() == UIStatus::kDisabled) {
return;
}
const base::TimeDelta kMinWarningTime = base::TimeDelta::FromMilliseconds(10);
// Test the histogram for no user action taken.
{
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
// Ensure that the tab is open for more than 0 ms, even in the face of bots
// with bad clocks.
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), kMinWarningTime);
run_loop.Run();
NavigateToURL(browser(), GURL("about:blank"),
WindowOpenDisposition::CURRENT_TAB);
auto samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.NoAction.SafetyTip_BadReputation");
ASSERT_EQ(1u, samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), samples.front().min);
}
// Test the histogram for when the user adheres to the warning.
{
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), kMinWarningTime);
run_loop.Run();
CloseWarningLeaveSite(browser());
auto samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.LeaveSite.SafetyTip_BadReputation");
ASSERT_EQ(1u, samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), samples.front().min);
}
// Test the histogram for when the user dismisses the warning.
{
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site1.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), kMinWarningTime);
run_loop.Run();
CloseWarningIgnore(views::Widget::ClosedReason::kCloseButtonClicked);
auto base_samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.Dismiss.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, base_samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), base_samples.front().min);
auto samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.DismissWithClose.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), samples.front().min);
}
{
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site2.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), kMinWarningTime);
run_loop.Run();
CloseWarningIgnore(views::Widget::ClosedReason::kEscKeyPressed);
auto base_samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.Dismiss.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, base_samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), base_samples.front().min);
auto samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.DismissWithEsc.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), samples.front().min);
}
{
base::HistogramTester histograms;
auto kNavigatedUrl = GetURL("site3.com");
TriggerWarning(browser(), kNavigatedUrl,
WindowOpenDisposition::CURRENT_TAB);
base::RunLoop run_loop;
base::PostDelayedTask(FROM_HERE, run_loop.QuitClosure(), kMinWarningTime);
run_loop.Run();
CloseWarningIgnore(views::Widget::ClosedReason::kCancelButtonClicked);
auto base_samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.Dismiss.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, base_samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), base_samples.front().min);
auto samples = histograms.GetAllSamples(
"Security.SafetyTips.OpenTime.DismissWithIgnore.SafetyTip_"
"BadReputation");
ASSERT_EQ(1u, samples.size());
EXPECT_LE(kMinWarningTime.InMilliseconds(), samples.front().min);
}
}
// Tests that Safety Tips aren't triggered on 'unknown' flag types from the
// component updater. This permits us to add new flag types to the component
// without breaking this release.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NotShownOnUnknownFlag) {
auto kNavigatedUrl = GetURL("site1.com");
SetSafetyTipPatternsWithFlagType(
{"site1.com/"}, chrome_browser_safety_tips::FlaggedPage::UNKNOWN);
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}
// Tests that Safety Tips aren't triggered on domains flagged as 'YOUNG_DOMAIN'
// in the component. This permits us to use this flag in the component without
// breaking this release.
IN_PROC_BROWSER_TEST_P(SafetyTipPageInfoBubbleViewBrowserTest,
NotShownOnYoungDomain) {
auto kNavigatedUrl = GetURL("site1.com");
SetSafetyTipPatternsWithFlagType(
{"site1.com/"}, chrome_browser_safety_tips::FlaggedPage::YOUNG_DOMAIN);
SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement);
NavigateToURL(browser(), kNavigatedUrl, WindowOpenDisposition::CURRENT_TAB);
EXPECT_FALSE(IsUIShowing());
ASSERT_NO_FATAL_FAILURE(CheckPageInfoDoesNotShowSafetyTipInfo(browser()));
}