|  | // Copyright 2018 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <tuple> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/functional/callback_helpers.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/test/bind.h" | 
|  | #include "base/test/gmock_callback_support.h" | 
|  | #include "base/test/test_future.h" | 
|  | #include "base/time/time.h" | 
|  | #include "content/browser/idle/idle_manager_impl.h" | 
|  | #include "content/browser/permissions/permission_controller_impl.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/permission_controller.h" | 
|  | #include "content/public/browser/permission_result.h" | 
|  | #include "content/public/test/browser_task_environment.h" | 
|  | #include "content/public/test/mock_permission_manager.h" | 
|  | #include "content/public/test/test_browser_context.h" | 
|  | #include "content/public/test/test_renderer_host.h" | 
|  | #include "mojo/public/cpp/bindings/receiver.h" | 
|  | #include "mojo/public/cpp/test_support/test_utils.h" | 
|  | #include "services/service_manager/public/cpp/bind_source_info.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "third_party/blink/public/common/permissions/permission_utils.h" | 
|  | #include "third_party/blink/public/mojom/idle/idle_manager.mojom.h" | 
|  | #include "ui/base/idle/idle_time_provider.h" | 
|  | #include "ui/base/test/idle_test_utils.h" | 
|  |  | 
|  | using blink::mojom::IdleManagerError; | 
|  | using blink::mojom::IdleStatePtr; | 
|  | using ::testing::_; | 
|  | using ::testing::NiceMock; | 
|  | using ::testing::Return; | 
|  |  | 
|  | MATCHER_P(PermissionTypeMatcher, id, "") { | 
|  | return ::testing::Matches(::testing::Eq(id))( | 
|  | blink::PermissionDescriptorToPermissionType(arg)); | 
|  | } | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kTestUrl[] = "https://www.google.com"; | 
|  |  | 
|  | enum UserIdleState { | 
|  | kActive, | 
|  | kIdle, | 
|  | }; | 
|  |  | 
|  | enum ScreenIdleState { | 
|  | kUnlocked, | 
|  | kLocked, | 
|  | }; | 
|  |  | 
|  | class MockIdleMonitor : public blink::mojom::IdleMonitor { | 
|  | public: | 
|  | MOCK_METHOD(void, Update, (IdleStatePtr, bool)); | 
|  | }; | 
|  |  | 
|  | class MockIdleTimeProvider : public ui::IdleTimeProvider { | 
|  | public: | 
|  | MockIdleTimeProvider() = default; | 
|  | ~MockIdleTimeProvider() override = default; | 
|  | MockIdleTimeProvider(const MockIdleTimeProvider&) = delete; | 
|  | MockIdleTimeProvider& operator=(const MockIdleTimeProvider&) = delete; | 
|  |  | 
|  | MOCK_METHOD(base::TimeDelta, CalculateIdleTime, ()); | 
|  | MOCK_METHOD(bool, CheckIdleStateIsLocked, ()); | 
|  | }; | 
|  |  | 
|  | class IdleManagerTest : public RenderViewHostTestHarness { | 
|  | public: | 
|  | IdleManagerTest(const IdleManagerTest&) = delete; | 
|  | IdleManagerTest& operator=(const IdleManagerTest&) = delete; | 
|  |  | 
|  | protected: | 
|  | IdleManagerTest() | 
|  | : RenderViewHostTestHarness( | 
|  | base::test::TaskEnvironment::TimeSource::MOCK_TIME) {} | 
|  | ~IdleManagerTest() override = default; | 
|  |  | 
|  | void SetUp() override { | 
|  | RenderViewHostTestHarness::SetUp(); | 
|  |  | 
|  | NavigateAndCommit(url_); | 
|  |  | 
|  | permission_manager_ = new NiceMock<MockPermissionManager>(); | 
|  | auto* test_browser_context = | 
|  | static_cast<TestBrowserContext*>(browser_context()); | 
|  | test_browser_context->SetPermissionControllerDelegate( | 
|  | base::WrapUnique(permission_manager_.get())); | 
|  |  | 
|  | idle_time_provider_ = new NiceMock<MockIdleTimeProvider>(); | 
|  | idle_manager_ = std::make_unique<IdleManagerImpl>(main_rfh()); | 
|  | scoped_idle_time_provider_ = | 
|  | std::make_unique<ui::test::ScopedIdleProviderForTest>( | 
|  | base::WrapUnique(idle_time_provider_.get())); | 
|  | idle_manager_->CreateService(service_remote_.BindNewPipeAndPassReceiver()); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | permission_manager_ = nullptr; | 
|  | idle_time_provider_ = nullptr; | 
|  | scoped_idle_time_provider_.reset(); | 
|  | idle_manager_.reset(); | 
|  | RenderViewHostTestHarness::TearDown(); | 
|  | } | 
|  |  | 
|  | IdleManagerImpl* GetIdleManager() { return idle_manager_.get(); } | 
|  |  | 
|  | void SetPermissionStatus(blink::mojom::PermissionStatus permission_status) { | 
|  | ON_CALL(*permission_manager_, | 
|  | GetPermissionResultForCurrentDocument( | 
|  | PermissionTypeMatcher(blink::PermissionType::IDLE_DETECTION), | 
|  | main_rfh(), | 
|  | /*should_include_device_status*/ false)) | 
|  | .WillByDefault(Return(PermissionResult(permission_status))); | 
|  | } | 
|  |  | 
|  | std::tuple<UserIdleState, ScreenIdleState> AddMonitorRequest() { | 
|  | base::test::TestFuture<IdleManagerError, IdleStatePtr> future; | 
|  | service_remote_->AddMonitor(monitor_receiver_.BindNewPipeAndPassRemote(), | 
|  | future.GetCallback()); | 
|  | EXPECT_EQ(IdleManagerError::kSuccess, future.Get<0>()); | 
|  | return std::make_tuple( | 
|  | future.Get<1>()->idle_time.has_value() ? UserIdleState::kIdle | 
|  | : UserIdleState::kActive, | 
|  | future.Get<1>()->screen_locked ? ScreenIdleState::kLocked | 
|  | : ScreenIdleState::kUnlocked); | 
|  | } | 
|  |  | 
|  | std::tuple<UserIdleState, ScreenIdleState> GetIdleStatus( | 
|  | bool expect_override) { | 
|  | base::RunLoop loop; | 
|  | IdleStatePtr result; | 
|  |  | 
|  | EXPECT_CALL(idle_monitor_, Update(_, expect_override)) | 
|  | .WillOnce([&loop, &result](IdleStatePtr state, | 
|  | bool is_overridden_by_devtools) { | 
|  | result = std::move(state); | 
|  | loop.Quit(); | 
|  | }); | 
|  |  | 
|  | if (!expect_override) { | 
|  | // If we aren't expecting an override then we need to fast forward in | 
|  | // order to run the polling task. | 
|  | task_environment()->FastForwardBy(base::Seconds(1)); | 
|  | } | 
|  |  | 
|  | loop.Run(); | 
|  | return std::make_tuple(result->idle_time.has_value() | 
|  | ? UserIdleState::kIdle | 
|  | : UserIdleState::kActive, | 
|  | result->screen_locked ? ScreenIdleState::kLocked | 
|  | : ScreenIdleState::kUnlocked); | 
|  | } | 
|  |  | 
|  | void DisconnectRenderer() { | 
|  | base::RunLoop loop; | 
|  |  | 
|  | // Simulates the renderer disconnecting. | 
|  | monitor_receiver_.reset(); | 
|  |  | 
|  | // Wait for the IdleManager to observe the pipe close. | 
|  | loop.RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | MockIdleTimeProvider* idle_time_provider() const { | 
|  | return idle_time_provider_; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | mojo::Remote<blink::mojom::IdleManager> service_remote_; | 
|  |  | 
|  | private: | 
|  | std::unique_ptr<IdleManagerImpl> idle_manager_; | 
|  | raw_ptr<MockPermissionManager> permission_manager_; | 
|  | raw_ptr<MockIdleTimeProvider> idle_time_provider_; | 
|  | std::unique_ptr<ui::test::ScopedIdleProviderForTest> | 
|  | scoped_idle_time_provider_; | 
|  | NiceMock<MockIdleMonitor> idle_monitor_; | 
|  | mojo::Receiver<blink::mojom::IdleMonitor> monitor_receiver_{&idle_monitor_}; | 
|  | GURL url_ = GURL(kTestUrl); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(IdleManagerTest, AddMonitor) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, Idle) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Simulates a user going idle. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(60))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  |  | 
|  | // Simulates a user going active, calling a callback under the threshold. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, UnlockingScreen) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(70))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kLocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Simulates a user unlocking the screen. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, LockingScreen) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Simulates a user locking the screen. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(10))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, LockingScreenThenIdle) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Simulates a user locking screen. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(10))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  |  | 
|  | // Simulates a user going idle, while the screen is still locked. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(70))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, LockingScreenAfterIdle) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(0))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Simulates a user going idle, but with the screen still unlocked. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(60))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(false)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kUnlocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  |  | 
|  | // Simulates the screen getting locked by the system after the user goes | 
|  | // idle (e.g. screensaver kicks in first, throwing idleness, then getting | 
|  | // locked). | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()) | 
|  | .WillOnce(Return(base::Seconds(60))); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()) | 
|  | .WillOnce(Return(true)); | 
|  |  | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, RemoveMonitorStopsPolling) { | 
|  | // Simulates the renderer disconnecting (e.g. on page reload) and verifies | 
|  | // that the polling stops for the idle detection. | 
|  |  | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | AddMonitorRequest(); | 
|  |  | 
|  | EXPECT_TRUE(ui::IdlePollingService::GetInstance()->IsPollingForTest()); | 
|  |  | 
|  | DisconnectRenderer(); | 
|  |  | 
|  | EXPECT_FALSE(ui::IdlePollingService::GetInstance()->IsPollingForTest()); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, PermissionDenied) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::DENIED); | 
|  |  | 
|  | MockIdleMonitor monitor; | 
|  | mojo::Receiver<blink::mojom::IdleMonitor> monitor_receiver(&monitor); | 
|  |  | 
|  | // Should not start initial state of the system. | 
|  | EXPECT_CALL(*idle_time_provider(), CalculateIdleTime()).Times(0); | 
|  | EXPECT_CALL(*idle_time_provider(), CheckIdleStateIsLocked()).Times(0); | 
|  |  | 
|  | base::RunLoop loop; | 
|  | service_remote_->AddMonitor( | 
|  | monitor_receiver.BindNewPipeAndPassRemote(), | 
|  | base::BindLambdaForTesting( | 
|  | [&loop](IdleManagerError error, IdleStatePtr state) { | 
|  | EXPECT_EQ(IdleManagerError::kPermissionDisabled, error); | 
|  | EXPECT_FALSE(state); | 
|  | loop.Quit(); | 
|  | })); | 
|  | loop.Run(); | 
|  | } | 
|  |  | 
|  | TEST_F(IdleManagerTest, SetAndClearOverrides) { | 
|  | SetPermissionStatus(blink::mojom::PermissionStatus::GRANTED); | 
|  |  | 
|  | // Verify initial state without overrides. | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | AddMonitorRequest()); | 
|  |  | 
|  | // Set overrides and verify overriden values returned. | 
|  | auto* impl = GetIdleManager(); | 
|  | impl->SetIdleOverride(/*is_user_active=*/false, /*is_screen_unlocked=*/false); | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kIdle, ScreenIdleState::kLocked), | 
|  | GetIdleStatus(/*expect_override=*/true)); | 
|  |  | 
|  | // Clear overrides and verify initial values returned. | 
|  | impl->ClearIdleOverride(); | 
|  | EXPECT_EQ(std::make_tuple(UserIdleState::kActive, ScreenIdleState::kUnlocked), | 
|  | GetIdleStatus(/*expect_override=*/false)); | 
|  | } | 
|  |  | 
|  | }  // namespace content |