910e62b5创建于 1月15日历史提交
// 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