blob: 3ef95ad96ffd9918184b5f990e24e807eaa7f7bc [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/content_settings/sound_content_setting_observer.h"
#include <memory>
#include <string>
#include "build/build_config.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/recently_audible_helper.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/ukm/content/source_url_recorder.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/test/test_service_manager_context.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(OS_ANDROID)
#include "chrome/browser/ui/tabs/tab_utils.h"
#endif
namespace {
constexpr char kURL1[] = "http://google.com/";
constexpr char kURL2[] = "http://youtube.com/";
constexpr char kSiteMutedEvent[] = "Media.SiteMuted";
constexpr char kSiteMutedReason[] = "MuteReason";
#if !defined(OS_ANDROID)
constexpr char kChromeURL[] = "chrome://dino";
constexpr char kExtensionId[] = "extensionid";
#endif
} // anonymous namespace
class SoundContentSettingObserverTest : public ChromeRenderViewHostTestHarness {
public:
SoundContentSettingObserverTest() = default;
~SoundContentSettingObserverTest() override = default;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
test_service_manager_context_ =
std::make_unique<content::TestServiceManagerContext>();
RecentlyAudibleHelper::CreateForWebContents(web_contents());
SoundContentSettingObserver::CreateForWebContents(web_contents());
ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
host_content_settings_map_ = HostContentSettingsMapFactory::GetForProfile(
Profile::FromBrowserContext(web_contents()->GetBrowserContext()));
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
NavigateAndCommit(GURL(kURL1));
}
void TearDown() override {
// Must be reset before browser thread teardown.
test_service_manager_context_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
protected:
void ChangeSoundContentSettingTo(ContentSetting setting) {
GURL url = web_contents()->GetLastCommittedURL();
host_content_settings_map_->SetContentSettingDefaultScope(
url, url, CONTENT_SETTINGS_TYPE_SOUND, std::string(), setting);
}
void ChangeDefaultSoundContentSettingTo(ContentSetting setting) {
host_content_settings_map_->SetDefaultContentSetting(
CONTENT_SETTINGS_TYPE_SOUND, setting);
}
void SimulateAudioStarting() {
SoundContentSettingObserver::FromWebContents(web_contents())
->OnAudioStateChanged(true);
}
void SimulateAudioPlaying() {
content::WebContentsTester::For(web_contents())
->SetIsCurrentlyAudible(true);
}
bool RecordedSiteMuted() {
auto entries = test_ukm_recorder_->GetEntriesByName(kSiteMutedEvent);
return !entries.empty();
}
void ExpectRecordedForReason(SoundContentSettingObserver::MuteReason reason) {
auto entries = test_ukm_recorder_->GetEntriesByName(kSiteMutedEvent);
EXPECT_EQ(1u, entries.size());
for (const auto* const entry : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, GURL(kURL1));
test_ukm_recorder_->ExpectEntryMetric(entry, kSiteMutedReason, reason);
}
}
// TabMutedReason does not exist on Android.
#if !defined(OS_ANDROID)
void SetMuteStateForReason(bool state, TabMutedReason reason) {
chrome::SetTabAudioMuted(web_contents(), state, reason, kExtensionId);
}
#endif
private:
HostContentSettingsMap* host_content_settings_map_;
std::unique_ptr<ukm::TestUkmRecorder> test_ukm_recorder_;
// WebContentsImpl accesses
// content::ServiceManagerConnection::GetForProcess(), so
// we must make sure it is instantiated.
std::unique_ptr<content::TestServiceManagerContext>
test_service_manager_context_;
DISALLOW_COPY_AND_ASSIGN(SoundContentSettingObserverTest);
};
TEST_F(SoundContentSettingObserverTest, AudioMutingUpdatesWithContentSetting) {
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Block site.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Allow site.
ChangeSoundContentSettingTo(CONTENT_SETTING_ALLOW);
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Set site to default.
ChangeSoundContentSettingTo(CONTENT_SETTING_DEFAULT);
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Block by default.
ChangeDefaultSoundContentSettingTo(CONTENT_SETTING_BLOCK);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Should not affect site if explicitly allowed.
ChangeSoundContentSettingTo(CONTENT_SETTING_ALLOW);
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Change site back to default.
ChangeSoundContentSettingTo(CONTENT_SETTING_DEFAULT);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Allow by default.
ChangeDefaultSoundContentSettingTo(CONTENT_SETTING_ALLOW);
EXPECT_FALSE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest, AudioMutingUpdatesWithNavigation) {
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Block for kURL1.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Should not be muted for kURL2.
NavigateAndCommit(GURL(kURL2));
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Should be muted for kURL1.
NavigateAndCommit(GURL(kURL1));
EXPECT_TRUE(web_contents()->IsAudioMuted());
}
// TabMutedReason does not exist on Android.
#if !defined(OS_ANDROID)
TEST_F(SoundContentSettingObserverTest, DontMuteWhenUnmutedByExtension) {
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Mute kURL1 via content setting.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Unmute by extension.
SetMuteStateForReason(false, TabMutedReason::EXTENSION);
EXPECT_FALSE(web_contents()->IsAudioMuted());
// Navigating to a new URL and back to kURL1 should not mute the tab unmuted
// by an extension.
NavigateAndCommit(GURL(kURL2));
EXPECT_FALSE(web_contents()->IsAudioMuted());
NavigateAndCommit(GURL(kURL1));
EXPECT_FALSE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest, DontUnmuteWhenMutedByExtension) {
EXPECT_FALSE(web_contents()->IsAudioMuted());
SetMuteStateForReason(true, TabMutedReason::EXTENSION);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Navigating to a new URL should not unmute the tab muted by an extension.
NavigateAndCommit(GURL(kURL2));
EXPECT_TRUE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest, DontUnmuteWhenMutedForMediaCapture) {
EXPECT_FALSE(web_contents()->IsAudioMuted());
SetMuteStateForReason(true, TabMutedReason::MEDIA_CAPTURE);
EXPECT_TRUE(web_contents()->IsAudioMuted());
// Navigating to a new URL should not unmute the tab muted for media capture.
NavigateAndCommit(GURL(kURL2));
EXPECT_TRUE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest, DontUnmuteChromeTabWhenMuted) {
NavigateAndCommit(GURL(kChromeURL));
EXPECT_FALSE(web_contents()->IsAudioMuted());
SetMuteStateForReason(true, TabMutedReason::CONTENT_SETTING_CHROME);
EXPECT_TRUE(web_contents()->IsAudioMuted());
NavigateAndCommit(GURL(kChromeURL));
EXPECT_TRUE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest,
UnmuteChromeTabWhenNavigatingToNonChromeUrl) {
NavigateAndCommit(GURL(kChromeURL));
EXPECT_FALSE(web_contents()->IsAudioMuted());
SetMuteStateForReason(true, TabMutedReason::CONTENT_SETTING_CHROME);
EXPECT_TRUE(web_contents()->IsAudioMuted());
NavigateAndCommit(GURL(kURL1));
EXPECT_FALSE(web_contents()->IsAudioMuted());
}
TEST_F(SoundContentSettingObserverTest,
UnmuteNonChromeTabWhenNavigatingToChromeUrl) {
NavigateAndCommit(GURL(kURL1));
EXPECT_FALSE(web_contents()->IsAudioMuted());
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
EXPECT_TRUE(web_contents()->IsAudioMuted());
NavigateAndCommit(GURL(kChromeURL));
EXPECT_FALSE(web_contents()->IsAudioMuted());
}
#endif // !defined(OS_ANDROID)
TEST_F(SoundContentSettingObserverTest,
UnmutedAudioPlayingDoesNotRecordSiteMuted) {
// Play audio while sound content setting is allowed.
ChangeSoundContentSettingTo(CONTENT_SETTING_ALLOW);
SimulateAudioStarting();
EXPECT_FALSE(RecordedSiteMuted());
}
TEST_F(SoundContentSettingObserverTest, MutedAudioBlockedBySiteException) {
// Play audio while sound content setting is blocked.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
SimulateAudioStarting();
EXPECT_TRUE(RecordedSiteMuted());
ExpectRecordedForReason(
SoundContentSettingObserver::MuteReason::kSiteException);
}
TEST_F(SoundContentSettingObserverTest,
MutingAudioWhileSoundIsPlayingBlocksSound) {
// Unmuted audio starts playing.
SimulateAudioPlaying();
// Sound is not blocked.
EXPECT_FALSE(RecordedSiteMuted());
// User mutes the site.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
// Sound is blocked.
EXPECT_TRUE(RecordedSiteMuted());
ExpectRecordedForReason(
SoundContentSettingObserver::MuteReason::kSiteException);
}
TEST_F(SoundContentSettingObserverTest, MuteByDefaultRecordsCorrectly) {
// Blocking audio via default content setting records properly.
ChangeDefaultSoundContentSettingTo(CONTENT_SETTING_BLOCK);
SimulateAudioStarting();
EXPECT_TRUE(RecordedSiteMuted());
ExpectRecordedForReason(
SoundContentSettingObserver::MuteReason::kMuteByDefault);
}
TEST_F(SoundContentSettingObserverTest,
MuteByDefaultAndExceptionRecordsException) {
// Block audio via default and exception.
ChangeSoundContentSettingTo(CONTENT_SETTING_BLOCK);
ChangeDefaultSoundContentSettingTo(CONTENT_SETTING_BLOCK);
SimulateAudioStarting();
EXPECT_TRUE(RecordedSiteMuted());
ExpectRecordedForReason(
SoundContentSettingObserver::MuteReason::kSiteException);
}