blob: da49e639d473efe49610f4084f971e250a37952e [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "chrome/browser/browsing_data/browsing_data_remover_browsertest_base.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/collected_cookies_views.h"
#include "chrome/browser/ui/views/site_data/page_specific_site_data_dialog_controller.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/page_info/core/features.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include "ui/views/controls/tree/tree_view.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/test/widget_test_api.h"
#include "ui/views/widget/any_widget_observer.h"
namespace {
const char kCookiesDialogHistogramName[] = "Privacy.CookiesInUseDialog.Action";
constexpr std::array<const char*, 7> kSiteDataTypes{
"Cookie", "LocalStorage", "SessionStorage", "IndexedDb",
"WebSql", "ServiceWorker", "CacheStorage"};
} // namespace
class CollectedCookiesViewsTest : public InProcessBrowserTest {
public:
CollectedCookiesViewsTest() {
// TODO(crbug.com/1344787): Clean up when PageSpecificSiteDataDialog is
// launched. Disable features for the new version of "Cookies in use"
// dialog. These tests are for the current version of the dialog only.
feature_list_.InitWithFeatures({}, {page_info::kPageSpecificSiteDataDialog,
page_info::kPageInfoCookiesSubpage});
}
CollectedCookiesViewsTest(const CollectedCookiesViewsTest&) = delete;
CollectedCookiesViewsTest& operator=(const CollectedCookiesViewsTest&) =
delete;
~CollectedCookiesViewsTest() override = default;
// InProcessBrowserTest:
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
// Disable cookies.
CookieSettingsFactory::GetForProfile(browser()->profile())
->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
// Load a page with cookies.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/cookie1.html")));
// Spawn a cookies dialog.
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
PageSpecificSiteDataDialogController::CreateAndShowForWebContents(
web_contents);
cookies_dialog_ =
PageSpecificSiteDataDialogController::GetDialogViewForTesting(
web_contents);
}
// Closing dialog with modified data will shows infobar.
void SetDialogChanged() { cookies_dialog_->set_status_changed_for_testing(); }
void CloseCookiesDialog() { cookies_dialog_->GetWidget()->Close(); }
size_t infobar_count() const {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
return web_contents
? infobars::ContentInfoBarManager::FromWebContents(web_contents)
->infobar_count()
: 0;
}
private:
raw_ptr<CollectedCookiesViews, DanglingAcrossTasks> cookies_dialog_ = nullptr;
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, CloseDialog) {
// Test closing dialog without changing data.
CloseCookiesDialog();
EXPECT_EQ(0u, infobar_count());
}
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, ChangeAndCloseDialog) {
// Test closing dialog with changing data. Dialog will show infobar.
SetDialogChanged();
CloseCookiesDialog();
EXPECT_EQ(1u, infobar_count());
}
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, ChangeAndNavigateAway) {
// Test navigation after changing dialog data. Changed dialog should not show
// infobar or crash because infobars::ContentInfoBarManager is gone.
SetDialogChanged();
// Navigation in the owning tab will close dialog.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/cookie2.html")));
EXPECT_EQ(0u, infobar_count());
}
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, ChangeAndCloseTab) {
// Test closing tab after changing dialog data. Changed dialog should not
// show infobar or crash because infobars::ContentInfoBarManager is gone.
SetDialogChanged();
// Closing the owning tab will close dialog.
browser()->tab_strip_model()->GetActiveWebContents()->Close();
EXPECT_EQ(0u, infobar_count());
}
// Closing the widget asynchronously destroys the CollectedCookiesViews object,
// but synchronously removes it from the WebContentsModalDialogManager. Make
// sure there's no crash when trying to re-open the CollectedCookiesViews right
// after closing it. Regression test for https://crbug.com/989888
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsTest, CloseDialogAndReopen) {
CloseCookiesDialog();
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
PageSpecificSiteDataDialogController::CreateAndShowForWebContents(
web_contents);
// If the test didn't crash, it has passed.
}
class CollectedCookiesViewsMetricsTest : public InProcessBrowserTest {
public:
CollectedCookiesViewsMetricsTest() {
// Disable features for the new version of "Cookies in use" dialog. These
// tests are for the current version of the dialog only.
feature_list_.InitWithFeatures({}, {page_info::kPageSpecificSiteDataDialog,
page_info::kPageInfoCookiesSubpage});
}
CollectedCookiesViewsMetricsTest(const CollectedCookiesViewsMetricsTest&) =
delete;
CollectedCookiesViewsMetricsTest& operator=(
const CollectedCookiesViewsMetricsTest&) = delete;
~CollectedCookiesViewsMetricsTest() override = default;
// InProcessBrowserTest:
void SetUpOnMainThread() override {
base::FilePath path;
base::PathService::Get(content::DIR_TEST_DATA, &path);
embedded_test_server()->ServeFilesFromDirectory(path);
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/browsing_data/site_data.html")));
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
SetupSiteData(web_contents);
}
void ClickButton(ui::ElementIdentifier button_id) {
auto* button =
static_cast<views::Button*>(GetViewByElementIdentifier(button_id));
ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
views::test::ButtonTestApi(button).NotifyClick(event);
}
views::View* GetViewByElementIdentifier(ui::ElementIdentifier id) {
auto* element_tracker = ui::ElementTracker::GetElementTracker();
auto* tracked_element = element_tracker->GetFirstMatchingElement(
id, browser()->window()->GetElementContext());
auto* view = tracked_element->AsA<views::TrackedElementViews>()->view();
return view;
}
void OpenCookiesInUseDialog() {
views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
"CollectedCookiesViews");
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
PageSpecificSiteDataDialogController::CreateAndShowForWebContents(
web_contents);
waiter.WaitIfNeededAndGet();
}
void WaitForData() {
auto* allowed_cookies_tree =
static_cast<views::TreeView*>(GetViewByElementIdentifier(
CollectedCookiesViews::kAllowedCookiesTreeElementId));
auto* model = static_cast<CookiesTreeModel*>(allowed_cookies_tree->model());
auto* site_node = model->GetChildren(model->GetRoot()).back();
// Wait until data is loaded.
while (model->GetChildren(site_node).size() != 7) {
base::RunLoop().RunUntilIdle();
base::PlatformThread::Sleep(TestTimeouts::tiny_timeout());
}
}
void SetupSiteData(content::WebContents* web_contents) {
for (const char* data_type : kSiteDataTypes) {
SetDataForType(data_type, web_contents);
EXPECT_TRUE(HasDataForType(data_type, web_contents));
}
}
void SetDataForType(const std::string& type,
content::WebContents* web_contents) {
if (!web_contents)
web_contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(RunScriptAndGetBool("set" + type + "()", web_contents))
<< "Couldn't create data for: " << type;
}
bool HasDataForType(const std::string& type,
content::WebContents* web_contents) {
if (!web_contents)
web_contents = browser()->tab_strip_model()->GetActiveWebContents();
return RunScriptAndGetBool("has" + type + "()", web_contents);
}
bool RunScriptAndGetBool(const std::string& script,
content::WebContents* web_contents) {
EXPECT_TRUE(web_contents);
return content::EvalJs(web_contents, script).ExtractBool();
}
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsMetricsTest, OpenDialog) {
base::HistogramTester histograms;
base::UserActionTester user_actions;
const std::string open_action = "CookiesInUseDialog.Opened";
// The histogram should start empty and no actions recorded.
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 0);
EXPECT_EQ(0, user_actions.GetActionCount(open_action));
// Open Cookies in use dialog.
OpenCookiesInUseDialog();
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 1);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kDialogOpened), 1);
EXPECT_EQ(1, user_actions.GetActionCount(open_action));
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_DeleteFolder DISABLED_DeleteFolder
#else
#define MAYBE_DeleteFolder DeleteFolder
#endif
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsMetricsTest, MAYBE_DeleteFolder) {
base::HistogramTester histograms;
// The histogram should start empty.
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 0);
// Open Cookies in use dialog.
OpenCookiesInUseDialog();
// Wait until data is loaded.
WaitForData();
auto* allowed_cookies_tree =
static_cast<views::TreeView*>(GetViewByElementIdentifier(
CollectedCookiesViews::kAllowedCookiesTreeElementId));
auto* model = static_cast<CookiesTreeModel*>(allowed_cookies_tree->model());
allowed_cookies_tree->Expand(model->GetRoot());
// Select the last folder in the list.
auto* site_node = model->GetChildren(model->GetRoot()).back();
allowed_cookies_tree->SetSelectedNode(model->GetChildren(site_node).back());
// Delete folder.
ClickButton(CollectedCookiesViews::kRemoveButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kFolderDeleted), 1);
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_RemoveButton DISABLED_RemoveButton
#else
#define MAYBE_RemoveButton RemoveButton
#endif
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsMetricsTest, MAYBE_RemoveButton) {
base::HistogramTester histograms;
base::UserActionTester user_actions;
const std::string remove_action = "CookiesInUseDialog.RemoveButtonClicked";
// The histogram should start empty and no actions recorded.
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 0);
EXPECT_EQ(0, user_actions.GetActionCount(remove_action));
// Opening Cookies in use dialog.
OpenCookiesInUseDialog();
// Wait until data is loaded.
WaitForData();
// Click remove on site level which is automatically selected upon dialog
// spawn.
ClickButton(CollectedCookiesViews::kRemoveButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kSiteDeleted), 1);
EXPECT_EQ(1, user_actions.GetActionCount(remove_action));
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_BlockAllowSite DISABLED_BlockAllowSite
#else
#define MAYBE_BlockAllowSite BlockAllowSite
#endif
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsMetricsTest, MAYBE_BlockAllowSite) {
base::HistogramTester histograms;
// The histogram should start empty.
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 0);
// Opening Cookies in use dialog.
OpenCookiesInUseDialog();
// Wait until data is loaded.
WaitForData();
// Block site.
ClickButton(CollectedCookiesViews::kBlockButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kSiteBlocked), 1);
// Close Cookies in use Dialog.
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
PageSpecificSiteDataDialogController::GetDialogViewForTesting(web_contents)
->GetWidget()
->Close();
// Reload site.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/browsing_data/site_data.html")));
// Re-open Cookies in use dialog.
OpenCookiesInUseDialog();
// Select blocked tab.
auto* tabbed_pane = static_cast<views::TabbedPane*>(
GetViewByElementIdentifier(CollectedCookiesViews::kTabbedPaneElementId));
tabbed_pane->SelectTabAt(1);
// Allow site.
ClickButton(CollectedCookiesViews::kAllowButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kSiteAllowed), 1);
}
#if BUILDFLAG(IS_MAC)
#define MAYBE_BlockClearOnExit DISABLED_BlockClearOnExit
#else
#define MAYBE_BlockClearOnExit BlockClearOnExit
#endif
IN_PROC_BROWSER_TEST_F(CollectedCookiesViewsMetricsTest,
MAYBE_BlockClearOnExit) {
base::HistogramTester histograms;
// The histogram should start empty.
histograms.ExpectTotalCount(kCookiesDialogHistogramName, 0);
// Opening Cookies in use dialog.
OpenCookiesInUseDialog();
// Wait until data is loaded.
WaitForData();
// Block site.
ClickButton(CollectedCookiesViews::kBlockButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kSiteBlocked), 1);
// Close Cookies in use Dialog.
auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
PageSpecificSiteDataDialogController::GetDialogViewForTesting(web_contents)
->GetWidget()
->Close();
// Reload site.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/browsing_data/site_data.html")));
// Re-open Cookies in use dialog.
OpenCookiesInUseDialog();
// Select blocked tab.
auto* tabbed_pane = static_cast<views::TabbedPane*>(
GetViewByElementIdentifier(CollectedCookiesViews::kTabbedPaneElementId));
tabbed_pane->SelectTabAt(1);
// Clear site on exit.
ClickButton(CollectedCookiesViews::kClearOnExitButtonId);
histograms.ExpectBucketCount(
kCookiesDialogHistogramName,
static_cast<int>(PageSpecificSiteDataDialogAction::kSiteClearedOnExit),
1);
}