blob: 080921c50b57465cc9f301fef7e5011e45b595bc [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 "content/browser/media/media_keys_listener_manager_impl.h"
#include "base/containers/flat_set.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/media/hardware_key_media_controller.h"
#include "content/public/test/content_browser_test.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/cpp/test/test_media_controller.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/media_keys_listener.h"
namespace content {
using media_session::mojom::MediaPlaybackState;
using media_session::mojom::MediaSessionAction;
using media_session::mojom::MediaSessionInfo;
using media_session::mojom::MediaSessionInfoPtr;
using media_session::test::TestMediaController;
namespace {
class MockMediaKeysListener : public ui::MediaKeysListener {
public:
explicit MockMediaKeysListener(ui::MediaKeysListener::Delegate* delegate)
: delegate_(delegate) {}
~MockMediaKeysListener() override = default;
// MediaKeysListener implementation.
bool StartWatchingMediaKey(ui::KeyboardCode key_code) override {
key_codes_.insert(key_code);
return true;
}
void StopWatchingMediaKey(ui::KeyboardCode key_code) override {
key_codes_.erase(key_code);
}
void SimulateAccelerator(ui::Accelerator accelerator) {
if (IsWatching(accelerator.key_code()))
delegate_->OnMediaKeysAccelerator(accelerator);
}
bool IsWatching(ui::KeyboardCode key_code) {
return key_codes_.contains(key_code);
}
private:
ui::MediaKeysListener::Delegate* delegate_;
base::flat_set<ui::KeyboardCode> key_codes_;
DISALLOW_COPY_AND_ASSIGN(MockMediaKeysListener);
};
class MockMediaKeysListenerDelegate : public ui::MediaKeysListener::Delegate {
public:
MockMediaKeysListenerDelegate() = default;
~MockMediaKeysListenerDelegate() override = default;
// MediaKeysListener::Delegate implementation.
void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override {
received_keys_.push_back(accelerator.key_code());
}
// Expect that we have received the correct number of key events.
void ExpectReceivedKeysCount(uint32_t count) {
EXPECT_EQ(count, received_keys_.size());
}
// Expect that the key event received at |index| has the specified key code.
void ExpectReceivedKey(uint32_t index, ui::KeyboardCode code) {
ASSERT_LT(index, received_keys_.size());
EXPECT_EQ(code, received_keys_[index]);
}
private:
std::vector<ui::KeyboardCode> received_keys_;
DISALLOW_COPY_AND_ASSIGN(MockMediaKeysListenerDelegate);
};
} // anonymous namespace
class MediaKeysListenerManagerImplTest : public ContentBrowserTest {
public:
MediaKeysListenerManagerImplTest() = default;
~MediaKeysListenerManagerImplTest() override = default;
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
ContentBrowserTest::SetUpCommandLine(command_line);
scoped_feature_list_.InitAndEnableFeature(media::kHardwareMediaKeyHandling);
}
void SetUpOnMainThread() override {
media_keys_listener_manager_ =
BrowserMainLoop::GetInstance()->media_keys_listener_manager();
std::unique_ptr<MockMediaKeysListener> listener =
std::make_unique<MockMediaKeysListener>(media_keys_listener_manager_);
media_keys_listener_ = listener.get();
media_keys_listener_manager_->SetMediaKeysListenerForTesting(
std::move(listener));
media_controller_ = std::make_unique<TestMediaController>();
media_keys_listener_manager_->hardware_key_media_controller_for_testing()
->SetMediaControllerForTesting(
media_controller_->CreateMediaControllerPtr());
ContentBrowserTest::SetUpOnMainThread();
}
void SetMediaSessionInfo(MediaSessionInfoPtr session_info) {
media_keys_listener_manager_->hardware_key_media_controller_for_testing()
->MediaSessionInfoChanged(std::move(session_info));
}
void SetSupportedMediaSessionActions(
const std::vector<MediaSessionAction>& actions) {
media_keys_listener_manager_->hardware_key_media_controller_for_testing()
->MediaSessionActionsChanged(actions);
}
void FlushForTesting() {
media_keys_listener_manager_->hardware_key_media_controller_for_testing()
->FlushForTesting();
}
MediaKeysListenerManagerImpl* media_keys_listener_manager() {
return media_keys_listener_manager_;
}
MockMediaKeysListener* media_keys_listener() { return media_keys_listener_; }
TestMediaController* media_controller() { return media_controller_.get(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
MediaKeysListenerManagerImpl* media_keys_listener_manager_;
MockMediaKeysListener* media_keys_listener_;
std::unique_ptr<TestMediaController> media_controller_;
DISALLOW_COPY_AND_ASSIGN(MediaKeysListenerManagerImplTest);
};
IN_PROC_BROWSER_TEST_F(MediaKeysListenerManagerImplTest, PressPlayPauseKey) {
// Tell the HardwareKeyMediaController that there is media playing that can be
// paused.
{
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->playback_state = MediaPlaybackState::kPlaying;
SetMediaSessionInfo(std::move(session_info));
SetSupportedMediaSessionActions({MediaSessionAction::kPause});
}
// There should not have been any calls to the media controller yet.
EXPECT_EQ(0, media_controller()->suspend_count());
EXPECT_EQ(0, media_controller()->resume_count());
// Press the play/pause media key.
media_keys_listener()->SimulateAccelerator(
ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, 0));
FlushForTesting();
// The media controller should have been told to pause.
EXPECT_EQ(1, media_controller()->suspend_count());
EXPECT_EQ(0, media_controller()->resume_count());
// Tell the HardwareKeyMediaController that the media is now paused and can be
// played.
{
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->playback_state = MediaPlaybackState::kPaused;
SetMediaSessionInfo(std::move(session_info));
SetSupportedMediaSessionActions({MediaSessionAction::kPlay});
}
// Press play/pause.
media_keys_listener()->SimulateAccelerator(
ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, 0));
FlushForTesting();
// The media controller should have been told to play.
EXPECT_EQ(1, media_controller()->suspend_count());
EXPECT_EQ(1, media_controller()->resume_count());
}
IN_PROC_BROWSER_TEST_F(MediaKeysListenerManagerImplTest,
ListensToTheCorrectMediaKeys) {
// Before any media session starts, we should not be listening for key input.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Tell the HardwareKeyMediaController that there is media playing that can be
// paused.
{
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->playback_state = MediaPlaybackState::kPlaying;
SetMediaSessionInfo(std::move(session_info));
SetSupportedMediaSessionActions({MediaSessionAction::kPause});
}
// We should now be listening for the play/pause key, but no others.
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Update the list of supported actions.
SetSupportedMediaSessionActions({MediaSessionAction::kPause,
MediaSessionAction::kStop,
MediaSessionAction::kNextTrack});
// We should now be listening for the correct media keys.
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Update the list of supported actions.
SetSupportedMediaSessionActions({MediaSessionAction::kStop});
// We should now be listening for the correct media keys.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Disable media key handling for the HardwareKeyMediaController.
media_keys_listener_manager()->DisableInternalMediaKeyHandling();
// We should no longer be listening for key input.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Re-enable media key handling for the HardwareKeyMediaController.
media_keys_listener_manager()->EnableInternalMediaKeyHandling();
// We should now be listening for the correct media keys.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Have a different delegate besides the HardwareKeyMediaController request
// keys.
MockMediaKeysListenerDelegate delegate;
media_keys_listener_manager()->StartWatchingMediaKey(
ui::VKEY_MEDIA_PLAY_PAUSE, &delegate);
// We should now be listening for only the new delegate's keys.
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Unregister the delegate.
media_keys_listener_manager()->StopWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE,
&delegate);
// We should now be listening for the HardwareKeyMediaController's keys.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_TRUE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
// Tell the HardwareKeyMediaController there is no longer an active session.
SetMediaSessionInfo(nullptr);
SetSupportedMediaSessionActions({});
// We should no longer be listening for key input.
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PLAY_PAUSE));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_STOP));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_NEXT_TRACK));
EXPECT_FALSE(media_keys_listener()->IsWatching(ui::VKEY_MEDIA_PREV_TRACK));
}
IN_PROC_BROWSER_TEST_F(MediaKeysListenerManagerImplTest,
OtherDelegatesPreemptHardwareKeyMediaController) {
// Tell the HardwareKeyMediaController that there is media playing that can be
// paused or sent to the next track.
{
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->playback_state = MediaPlaybackState::kPlaying;
SetMediaSessionInfo(std::move(session_info));
SetSupportedMediaSessionActions({MediaSessionAction::kPause});
}
// Set up a delegate that listens to Play/Pause.
MockMediaKeysListenerDelegate delegate;
media_keys_listener_manager()->StartWatchingMediaKey(
ui::VKEY_MEDIA_PLAY_PAUSE, &delegate);
// There should not have been any calls to the media controller or the
// delegate yet.
EXPECT_EQ(0, media_controller()->suspend_count());
EXPECT_EQ(0, media_controller()->next_track_count());
delegate.ExpectReceivedKeysCount(0);
// Press play/pause.
media_keys_listener()->SimulateAccelerator(
ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, 0));
FlushForTesting();
// The media controller should not have been told to pause.
EXPECT_EQ(0, media_controller()->suspend_count());
EXPECT_EQ(0, media_controller()->next_track_count());
// The delegate should have received the event instead.
delegate.ExpectReceivedKeysCount(1);
delegate.ExpectReceivedKey(/*index=*/0, ui::VKEY_MEDIA_PLAY_PAUSE);
// Unregister the delegate.
media_keys_listener_manager()->StopWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE,
&delegate);
// Press play/pause.
media_keys_listener()->SimulateAccelerator(
ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, 0));
FlushForTesting();
// The media controller should have been told to pause.
EXPECT_EQ(1, media_controller()->suspend_count());
// The delegate should not have been told to pause, since it was unregistered.
delegate.ExpectReceivedKeysCount(1);
}
} // namespace content