blob: f1bef8852145afd379be43b3df948ab5c8c018fa [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/test_with_browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/permissions/chip/chip_controller.h"
#include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h"
#include "chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h"
#include "chrome/browser/ui/views/permissions/chip/permission_dashboard_view.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_chip.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/animation/animation_test_api.h"
#include "ui/views/test/ax_event_counter.h"
#include "ui/views/test/button_test_api.h"
class AnimationObserver : public PermissionChipView::Observer {
public:
explicit AnimationObserver(base::OnceClosure quit_closure)
: animation_complete_callback_(std::move(quit_closure)) {}
void OnAnimationEnded() { std::move(animation_complete_callback_).Run(); }
// PermissionChipView::Observer
void OnChipVisibilityChanged(bool is_visible) override {}
void OnExpandAnimationEnded() override { OnAnimationEnded(); }
void OnCollapseAnimationEnded() override { OnAnimationEnded(); }
private:
base::OnceClosure animation_complete_callback_;
};
class PermissionDashboardUnitTest : public TestWithBrowserView {
public:
PermissionDashboardUnitTest()
: TestWithBrowserView(base::test::TaskEnvironment::TimeSource::MOCK_TIME,
base::test::TaskEnvironment::MainThreadType::UI),
animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
gfx::Animation::RichAnimationRenderMode::FORCE_ENABLED)) {
feature_list_.InitAndEnableFeature(
content_settings::features::kLeftHandSideActivityIndicators);
}
PermissionDashboardUnitTest(const PermissionDashboardUnitTest&) = delete;
PermissionDashboardUnitTest& operator=(const PermissionDashboardUnitTest&) =
delete;
void SetUp() override {
TestingBrowserProcess::GetGlobal()->CreateGlobalFeaturesForTesting();
TestWithBrowserView::SetUp();
AddTab(browser(), GURL("http://a.com"));
}
void WaitForAnimationCompletion() {
PermissionDashboardView* dashboard_view =
location_bar_view()
->permission_dashboard_controller()
->permission_dashboard_view();
base::RunLoop run_loop;
std::unique_ptr<AnimationObserver> observer =
std::make_unique<AnimationObserver>(run_loop.QuitWhenIdleClosure());
dashboard_view->GetIndicatorChip()->AddObserver(observer.get());
run_loop.Run();
dashboard_view->GetIndicatorChip()->RemoveObserver(observer.get());
}
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetWebContentsAt(0);
}
LocationBarView* location_bar_view() {
BrowserView* browser_view =
BrowserView::GetBrowserViewForBrowser(browser());
return browser_view->GetLocationBarView();
}
PermissionDashboardController* dashboard_controller() {
return location_bar_view()->permission_dashboard_controller();
}
private:
base::test::ScopedFeatureList feature_list_;
// Some of these tests rely on animation being enabled. This forces
// animation on even if it's turned off in the OS.
gfx::AnimationTestApi::RenderModeResetter animation_mode_reset_;
};
// TODO(crbug.com/41492809): Test LHS indicators animation on macOS as well.
#if !BUILDFLAG(IS_MAC)
// This test verifies:
// 1. Camera activity indicator chip is shown in verbose form after
// `PageSpecificContentSettings` updates camera usage.
// 2. The chip's verbose state collapses after 4 seconds.
// 3. The chip disappears after `PageSpecificContentSettings` resets camera
// usage.
TEST_F(PermissionDashboardUnitTest, DisplayLHSIndicatorForCamera) {
PermissionDashboardController* dashboard_controller =
location_bar_view()->permission_dashboard_controller();
PermissionChipView* indicator_chip =
dashboard_controller->permission_dashboard_view()->GetIndicatorChip();
content_settings::PageSpecificContentSettings* pscs =
content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_TRUE(pscs);
pscs->OnMediaStreamPermissionSet(
GURL("http://a.com"),
{content_settings::PageSpecificContentSettings::kCameraAccessed});
// Wait for the expand animation to finish.
WaitForAnimationCompletion();
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_TRUE(dashboard_controller->is_verbose());
EXPECT_TRUE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(
dashboard_controller->get_collapse_timer_for_testing().IsRunning());
EXPECT_FALSE(indicator_chip->is_animating());
// Wait longer than 4 seconds for collapse timer to fire and the collapse
// animation to finish.
task_environment()->AdvanceClock(base::Milliseconds(4100));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(indicator_chip->is_animating());
WaitForAnimationCompletion();
EXPECT_FALSE(indicator_chip->is_animating());
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_FALSE(
dashboard_controller->get_collapse_timer_for_testing().IsRunning());
EXPECT_FALSE(dashboard_controller->is_verbose());
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(pscs->get_indicators_hiding_delay_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(pscs->get_indicators_hiding_delay_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_CAMERA]
.IsRunning());
pscs->get_indicators_hiding_delay_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_CAMERA]
.FireNow();
EXPECT_FALSE(indicator_chip->GetVisible());
EXPECT_FALSE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
}
// This test verifies:
// 1. Camera & Mic activity indicator chip is shown.
// 2. The chip disappears after `PageSpecificContentSettings` resets camera &
// microphone usage.
TEST_F(PermissionDashboardUnitTest, DisplayLHSIndicatorForCameraMic) {
PermissionDashboardController* dashboard_controller =
location_bar_view()->permission_dashboard_controller();
PermissionChipView* indicator_chip =
dashboard_controller->permission_dashboard_view()->GetIndicatorChip();
content_settings::PageSpecificContentSettings* pscs =
content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_TRUE(pscs);
pscs->OnMediaStreamPermissionSet(
GURL("http://a.com"),
{content_settings::PageSpecificContentSettings::kCameraAccessed,
content_settings::PageSpecificContentSettings::kMicrophoneAccessed});
// Wait for the expand animation to finish.
WaitForAnimationCompletion();
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_TRUE(
dashboard_controller->get_collapse_timer_for_testing().IsRunning());
EXPECT_TRUE(dashboard_controller->is_verbose());
EXPECT_TRUE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_MIC));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
// Because indicator is displayed for both camera and mic, disabling only one
// does not trigger a delay timer.
EXPECT_FALSE(pscs->get_indicators_hiding_delay_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_CAMERA));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_MIC, false);
ASSERT_TRUE(pscs->get_indicators_hiding_delay_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
pscs->get_indicators_hiding_delay_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_MIC]
.FireNow();
// Wait for the collapse animation to finish.
WaitForAnimationCompletion();
EXPECT_FALSE(indicator_chip->GetVisible());
}
// This test verifies:
// 1. Camera activity indicator chip is shown.
// 2. After the Camera indicator collapsed, Microphone usage will not trigger
// expand animation because there is only one indicator for both camera and mic.
// 3. The chip does not disappears after `PageSpecificContentSettings` resets
// camera.
// 4. The chip disappears after `PageSpecificContentSettings` resets microphone
// usage.
TEST_F(PermissionDashboardUnitTest, DisplayLHSIndicatorForCameraAndThenMic) {
PermissionDashboardController* dashboard_controller =
location_bar_view()->permission_dashboard_controller();
PermissionChipView* indicator_chip =
dashboard_controller->permission_dashboard_view()->GetIndicatorChip();
content_settings::PageSpecificContentSettings* pscs =
content_settings::PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_TRUE(pscs);
pscs->OnMediaStreamPermissionSet(
GURL("http://a.com"),
{content_settings::PageSpecificContentSettings::kCameraAccessed});
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_FALSE(
dashboard_controller->get_collapse_timer_for_testing().IsRunning());
WaitForAnimationCompletion();
EXPECT_TRUE(dashboard_controller->is_verbose());
EXPECT_TRUE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_FALSE(pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_MIC));
// Wait longer than 4 seconds for collapse timer to fire and the collapse
// animation to finish.
task_environment()->AdvanceClock(base::Milliseconds(4100));
base::RunLoop().RunUntilIdle();
WaitForAnimationCompletion();
EXPECT_FALSE(dashboard_controller->is_verbose());
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_MIC, true);
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_FALSE(indicator_chip->is_animating());
// The indicator stays collapsed.
EXPECT_FALSE(dashboard_controller->is_verbose());
EXPECT_TRUE(pscs->GetMicrophoneCameraState().HasAll(
{content_settings::PageSpecificContentSettings::kCameraAccessed,
content_settings::PageSpecificContentSettings::kMicrophoneAccessed}));
EXPECT_TRUE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_MIC));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
EXPECT_TRUE(indicator_chip->GetVisible());
EXPECT_FALSE(indicator_chip->is_animating());
// The indicator stays collapsed.
EXPECT_FALSE(dashboard_controller->is_verbose());
EXPECT_FALSE(
pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_TRUE(pscs->IsIndicatorVisible(ContentSettingsType::MEDIASTREAM_MIC));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_MIC, false);
ASSERT_TRUE(pscs->get_indicators_hiding_delay_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
pscs->get_indicators_hiding_delay_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_MIC]
.FireNow();
EXPECT_FALSE(indicator_chip->GetVisible());
}
#endif