blob: e31ca226d9f7c0bed77b8419d2b44833a8920366 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/content_settings/browser/page_specific_content_settings.h"
#include <optional>
#include <string>
#include <variant>
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/browsing_data/content/browsing_data_helper.h"
#include "components/content_settings/browser/test_page_specific_content_settings_delegate.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/security_state/core/security_state.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/cookie_access_details.h"
#include "content/public/browser/trust_token_access_details.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/prerender_test_util.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_options.h"
#include "net/shared_dictionary/shared_dictionary_isolation_key.h"
#include "services/network/public/mojom/shared_dictionary_access_observer.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
namespace content_settings {
using StorageType = mojom::ContentSettingsManager::StorageType;
namespace {
constexpr int kTopicsAPITestTaxonomyVersion = 1;
class MockSiteDataObserver
: public PageSpecificContentSettings::SiteDataObserver {
public:
explicit MockSiteDataObserver(content::WebContents* web_contents)
: SiteDataObserver(web_contents) {}
MockSiteDataObserver(const MockSiteDataObserver&) = delete;
MockSiteDataObserver& operator=(const MockSiteDataObserver&) = delete;
~MockSiteDataObserver() override = default;
MOCK_METHOD(void, OnSiteDataAccessed, (const AccessDetails& access_details));
MOCK_METHOD(void, OnStatefulBounceDetected, ());
};
class MockPageSpecificContentSettingsDelegate
: public TestPageSpecificContentSettingsDelegate {
public:
MockPageSpecificContentSettingsDelegate(PrefService* prefs,
HostContentSettingsMap* settings_map)
: TestPageSpecificContentSettingsDelegate(prefs, settings_map) {}
~MockPageSpecificContentSettingsDelegate() override = default;
MOCK_METHOD(void, UpdateLocationBar, ());
MOCK_METHOD(void, OnContentAllowed, (ContentSettingsType type));
MOCK_METHOD(void, OnContentBlocked, (ContentSettingsType type));
MOCK_METHOD(bool,
IsFrameAllowlistedForJavaScript,
(content::RenderFrameHost * render_frame_host));
};
blink::StorageKey CreateUnpartitionedStorageKey(const GURL& url) {
return blink::StorageKey::CreateFirstParty(url::Origin::Create(url));
}
} // namespace
class PageSpecificContentSettingsTest
: public content::RenderViewHostTestHarness {
public:
PageSpecificContentSettingsTest()
: content::RenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
void SetUp() override {
RenderViewHostTestHarness::SetUp();
HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
settings_map_ = base::MakeRefCounted<HostContentSettingsMap>(
&prefs_, false /* is_off_the_record */, false /* store_last_modified */,
false /* restore_session*/, false /* should_record_metrics */);
PageSpecificContentSettings::CreateForWebContents(
web_contents(),
std::make_unique<TestPageSpecificContentSettingsDelegate>(
&prefs_, settings_map_.get()));
}
void TearDown() override {
settings_map_->ShutdownOnUIThread();
RenderViewHostTestHarness::TearDown();
}
HostContentSettingsMap* settings_map() { return settings_map_.get(); }
PrefService* prefs() { return &prefs_; }
content::WebContentsObserver* GetHandle() {
return PageSpecificContentSettings::GetWebContentsObserverForTest(
web_contents());
}
MockPageSpecificContentSettingsDelegate* InstallMockDelegate() {
PageSpecificContentSettings::DeleteForWebContentsForTest(web_contents());
PageSpecificContentSettings::CreateForWebContents(
web_contents(),
std::make_unique<
testing::NiceMock<MockPageSpecificContentSettingsDelegate>>(
prefs(), settings_map()));
return static_cast<MockPageSpecificContentSettingsDelegate*>(
PageSpecificContentSettings::GetDelegateForWebContents(web_contents()));
}
private:
sync_preferences::TestingPrefServiceSyncable prefs_;
scoped_refptr<HostContentSettingsMap> settings_map_;
};
TEST_F(PageSpecificContentSettingsTest, BlockedContent) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
// Check that after initializing, nothing is blocked.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES));
#endif
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::SOUND));
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_FALSE(content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
// Set a cookie, block access to images, block mediastream access and block a
// popup.
GURL origin("http://google.com");
std::unique_ptr<net::CanonicalCookie> cookie1(
net::CanonicalCookie::CreateForTesting(origin, "A=B", base::Time::Now()));
ASSERT_TRUE(cookie1);
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie1}},
false});
content_settings = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
content_settings->OnContentBlocked(ContentSettingsType::IMAGES);
#endif
content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
PageSpecificContentSettings::MicrophoneCameraState
blocked_microphone_camera_state = {
PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked,
PageSpecificContentSettings::kCameraAccessed,
PageSpecificContentSettings::kCameraBlocked};
content_settings->OnMediaStreamPermissionSet(GURL("http://google.com"),
blocked_microphone_camera_state);
// Check that only the respective content types are affected.
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES));
#endif
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::SOUND));
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
EXPECT_TRUE(
content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_TRUE(content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie1}},
false});
// Block a cookie.
std::unique_ptr<net::CanonicalCookie> cookie2(
net::CanonicalCookie::CreateForTesting(origin, "C=D", base::Time::Now()));
ASSERT_TRUE(cookie2);
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie2}},
true});
EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// Block a javascript during a navigation.
// Create a pending navigation.
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("http://google.com"), web_contents());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
GetHandle()->OnServiceWorkerAccessed(
simulator->GetNavigationHandle(), GURL("http://google.com"),
content::AllowServiceWorkerResult::FromPolicy(true, false));
content_settings = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
simulator->Commit();
content_settings = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
// Block a javascript when page starts to start ServiceWorker.
GetHandle()->OnServiceWorkerAccessed(
web_contents()->GetPrimaryMainFrame(), GURL("http://google.com"),
content::AllowServiceWorkerResult::FromPolicy(true, false));
EXPECT_TRUE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
// Reset blocked content settings.
NavigateAndCommit(GURL("http://google.com"));
content_settings = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES));
#endif
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_FALSE(content_settings->IsContentBlocked(
ContentSettingsType::MEDIASTREAM_CAMERA));
}
TEST_F(PageSpecificContentSettingsTest, BlockedFileSystems) {
NavigateAndCommit(GURL("http://google.com"));
auto* rfh = web_contents()->GetPrimaryMainFrame();
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(rfh);
auto google_storage_key = rfh->GetStorageKey();
// Access a file system.
PageSpecificContentSettings::StorageAccessed(
StorageType::FILE_SYSTEM, rfh->GetGlobalId(), google_storage_key, false);
EXPECT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// Block access to a file system.
PageSpecificContentSettings::StorageAccessed(
StorageType::FILE_SYSTEM, rfh->GetGlobalId(), google_storage_key, true);
EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
}
TEST_F(PageSpecificContentSettingsTest, AllowedContent) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
// Test default settings.
ASSERT_FALSE(content_settings->IsContentAllowed(ContentSettingsType::IMAGES));
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::MEDIASTREAM_MIC));
ASSERT_FALSE(content_settings->IsContentAllowed(
ContentSettingsType::MEDIASTREAM_CAMERA));
// Record a cookie.
GURL origin("http://google.com");
std::unique_ptr<net::CanonicalCookie> cookie1(
net::CanonicalCookie::CreateForTesting(origin, "A=B", base::Time::Now()));
ASSERT_TRUE(cookie1);
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie1}},
false});
ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// Record a blocked cookie.
std::unique_ptr<net::CanonicalCookie> cookie2(
net::CanonicalCookie::CreateForTesting(origin, "C=D", base::Time::Now()));
ASSERT_TRUE(cookie2);
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie2}},
true});
ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
}
TEST_F(PageSpecificContentSettingsTest, AllowlistJavaScript) {
const GURL url("http://google.com");
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
// PageSpecificContentSettingsDelegate::IsFrameAllowlistedForJavaScript() is
// called once per navigation.
EXPECT_CALL(*mock_delegate, IsFrameAllowlistedForJavaScript(::testing::_))
.Times(2)
.WillOnce(testing::Return(false))
.WillOnce(testing::Return(true));
content::NavigationHandleObserver observer(web_contents(), url);
// Disable JavaScript. The secondary URL is ignored. This call is functionally
// equivalent to setting `secondary_url` = wildcard.
settings_map()->SetContentSettingDefaultScope(
url, url, ContentSettingsType::JAVASCRIPT, CONTENT_SETTING_BLOCK);
NavigateAndCommit(url);
// The first mock call returns false for allowing JavaScript. The first
// navigation should not allow JavaScript in the main frame.
EXPECT_FALSE(observer.content_settings()->allow_script);
// The second mock call returns true for allowing JavaScript. The second
// navigation should allow JavaScript in the main frame.
NavigateAndCommit(url);
EXPECT_TRUE(observer.content_settings()->allow_script);
}
TEST_F(PageSpecificContentSettingsTest, InterestGroupJoin) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// Check that a blocking an interest join does not result in a blocked cookie
// report.
auto api_origin = url::Origin::Create(GURL("https://embedded.com"));
content_settings->OnInterestGroupJoined(api_origin,
/*blocked_by_policy=*/true);
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// But that a successful join results in cookie access.
content_settings->OnInterestGroupJoined(api_origin,
/*blocked_by_policy=*/false);
ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
}
TEST_F(PageSpecificContentSettingsTest, BrowsingDataAccessed) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// Only some browsing data types should be reported as blocked cookies.
auto origin = url::Origin::Create(GURL("https://embedded.com"));
content_settings->OnBrowsingDataAccessed(
origin, BrowsingDataModel::StorageType::kTrustTokens,
/*blocked=*/true);
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
auto storage_key = blink::StorageKey::CreateFirstParty(origin);
content_settings->OnBrowsingDataAccessed(
storage_key, BrowsingDataModel::StorageType::kLocalStorage,
/*blocked=*/true);
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
// But allowed accesses should be reported.
content_settings->OnBrowsingDataAccessed(
origin, BrowsingDataModel::StorageType::kTrustTokens,
/*blocked=*/false);
ASSERT_TRUE(content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
}
TEST_F(PageSpecificContentSettingsTest, EmptyCookieList) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
GetHandle()->OnCookiesAccessed(
web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead, GURL("http://google.com"),
GURL("http://google.com"), net::CookieAccessResultList(), true});
ASSERT_FALSE(
content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
ASSERT_FALSE(
content_settings->IsContentBlocked(ContentSettingsType::COOKIES));
}
TEST_F(PageSpecificContentSettingsTest, BlockedThirdPartyCookie) {
NavigateAndCommit(GURL("https://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
std::unique_ptr<net::CanonicalCookie> cookie(
net::CanonicalCookie::CreateForTesting(
GURL("https://google.com"),
"CookieName=CookieValue;Secure;SameSite=None", base::Time::Now()));
// 1P cookie should not be blocked.
GetHandle()->OnCookiesAccessed(
web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
/*url=*/GURL("https://google.com"),
/*first_party_url=*/GURL("https://google.com"),
{{*cookie}},
/*blocked_by_policy=*/true,
/*is_ad_tagged=*/false,
net::CookieSettingOverrides(),
net::SiteForCookies::FromUrl(GURL("https://google.com"))});
auto* blocked_data_model = pscs->blocked_browsing_data_model();
size_t count = 0u;
for (const auto& entry : *blocked_data_model) {
if (entry.data_details->blocked_third_party) {
++count;
}
}
EXPECT_EQ(0u, count);
// 1P cookie in ABA embed should be blocked.
GetHandle()->OnCookiesAccessed(
web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
/*url=*/GURL("https://google.com"),
/*first_party_url=*/GURL("https://google.com"),
{{*cookie}},
/*blocked_by_policy=*/true,
/*is_ad_tagged=*/false,
net::CookieSettingOverrides(),
net::SiteForCookies()});
count = 0u;
for (const auto& entry : *blocked_data_model) {
if (entry.data_details->blocked_third_party) {
++count;
}
}
EXPECT_EQ(1u, count);
std::unique_ptr<net::CanonicalCookie> third_party_cookie(
net::CanonicalCookie::CreateForTesting(
GURL("https://example.com"),
"CookieName=CookieValue;Secure;SameSite=None", base::Time::Now()));
// 3P cookie should be blocked.
GetHandle()->OnCookiesAccessed(
web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
/*url=*/GURL("https://google.com"),
/*first_party_url=*/GURL("https://example.com"),
{{*third_party_cookie}},
/*blocked_by_policy=*/true,
/*is_ad_tagged=*/false,
net::CookieSettingOverrides(),
net::SiteForCookies::FromUrl(GURL("https://example.com"))});
count = 0u;
for (const auto& entry : *blocked_data_model) {
if (entry.data_details->blocked_third_party) {
++count;
}
}
EXPECT_EQ(2u, count);
}
TEST_F(PageSpecificContentSettingsTest, SiteDataObserver) {
NavigateAndCommit(GURL("http://google.com"));
auto* rfh = web_contents()->GetPrimaryMainFrame();
MockSiteDataObserver mock_observer(web_contents());
EXPECT_CALL(mock_observer, OnSiteDataAccessed).Times(6);
bool blocked_by_policy = false;
GURL origin("http://google.com");
std::unique_ptr<net::CanonicalCookie> cookie(
net::CanonicalCookie::CreateForTesting(origin, "A=B", base::Time::Now()));
ASSERT_TRUE(cookie);
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie}},
blocked_by_policy});
net::CookieAccessResultList cookie_list;
std::unique_ptr<net::CanonicalCookie> other_cookie(
net::CanonicalCookie::CreateForTesting(GURL("http://google.com"),
"CookieName=CookieValue",
base::Time::Now()));
ASSERT_TRUE(other_cookie);
cookie_list.emplace_back(*other_cookie);
GetHandle()->OnCookiesAccessed(
rfh,
{content::CookieAccessDetails::Type::kRead, GURL("http://google.com"),
GURL("http://google.com"), cookie_list, blocked_by_policy});
auto google_storage_key = rfh->GetStorageKey();
PageSpecificContentSettings::StorageAccessed(
StorageType::FILE_SYSTEM, rfh->GetGlobalId(), google_storage_key,
blocked_by_policy);
PageSpecificContentSettings::StorageAccessed(
StorageType::INDEXED_DB, rfh->GetGlobalId(), google_storage_key,
blocked_by_policy);
PageSpecificContentSettings::StorageAccessed(
StorageType::LOCAL_STORAGE, rfh->GetGlobalId(), google_storage_key,
blocked_by_policy);
PageSpecificContentSettings::StorageAccessed(
StorageType::DATABASE, rfh->GetGlobalId(), google_storage_key,
blocked_by_policy);
}
TEST_F(PageSpecificContentSettingsTest, BrowsingDataModelTrustToken) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
// Before Trust Token accesses, there should be no objects here.
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
const url::Origin issuer =
url::Origin::Create(GURL("http://issuer.example/"));
// Access a Trust Token.
GetHandle()->OnTrustTokensAccessed(
web_contents()->GetPrimaryMainFrame(),
content::TrustTokenAccessDetails(
origin, network::mojom::TrustTokenOperationType::kIssuance, issuer,
false));
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
}
TEST_F(PageSpecificContentSettingsTest,
BrowsingDataModelTrustTokenPendingNavigation) {
NavigateAndCommit(GURL("http://google.com"));
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("http://other.com"), web_contents());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
const url::Origin issuer =
url::Origin::Create(GURL("http://issuer.example/"));
// Access a Trust Token.
GetHandle()->OnTrustTokensAccessed(
simulator->GetNavigationHandle(),
content::TrustTokenAccessDetails(
origin, network::mojom::TrustTokenOperationType::kIssuance, issuer,
false));
simulator->Commit();
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
simulator->GetFinalRenderFrameHost());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
// Before Trust Token accesses, there should be no objects here.
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
}
TEST_F(PageSpecificContentSettingsTest, BrowsingDataModelSharedDictionary) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
auto* blocked_browsing_data_model = pscs->blocked_browsing_data_model();
// Before Shared Dictionary accesses, there should be no objects here.
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
net::SharedDictionaryIsolationKey isolation_key(origin,
net::SchemefulSite(origin));
// Access a Shared Dictionary.
network::mojom::SharedDictionaryAccessDetailsPtr details =
network::mojom::SharedDictionaryAccessDetails::New(
network::mojom::SharedDictionaryAccessDetails::Type::kRead,
GURL("http://test.example/target"), isolation_key,
/*is_blocked=*/false);
GetHandle()->OnSharedDictionaryAccessed(web_contents()->GetPrimaryMainFrame(),
*details);
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
ASSERT_EQ(1u, allowed_browsing_data_model->size());
EXPECT_EQ("google.com",
*std::get_if<std::string>(
&*(*allowed_browsing_data_model->begin()).data_owner));
}
TEST_F(PageSpecificContentSettingsTest,
BrowsingDataModelSharedDictionaryBlocked) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
auto* blocked_browsing_data_model = pscs->blocked_browsing_data_model();
// Before Shared Dictionary accesses, there should be no objects here.
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
net::SharedDictionaryIsolationKey isolation_key(origin,
net::SchemefulSite(origin));
// Blocked a Shared Dictionary access.
network::mojom::SharedDictionaryAccessDetailsPtr details =
network::mojom::SharedDictionaryAccessDetails::New(
network::mojom::SharedDictionaryAccessDetails::Type::kRead,
GURL("http://test.example/target"), isolation_key,
/*is_blocked=*/true);
GetHandle()->OnSharedDictionaryAccessed(web_contents()->GetPrimaryMainFrame(),
*details);
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
ASSERT_EQ(1u, blocked_browsing_data_model->size());
EXPECT_EQ("google.com",
*std::get_if<std::string>(
&*(*blocked_browsing_data_model->begin()).data_owner));
}
TEST_F(PageSpecificContentSettingsTest,
BrowsingDataModelSharedDictionaryPendingNavigation) {
NavigateAndCommit(GURL("http://google.com"));
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("http://other.com"), web_contents());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
// Access a Shared Dictionary.
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
net::SharedDictionaryIsolationKey isolation_key(origin,
net::SchemefulSite(origin));
network::mojom::SharedDictionaryAccessDetailsPtr details =
network::mojom::SharedDictionaryAccessDetails::New(
network::mojom::SharedDictionaryAccessDetails::Type::kRead,
GURL("http://test.example/target"), isolation_key,
/*is_blocked=*/false);
GetHandle()->OnSharedDictionaryAccessed(simulator->GetNavigationHandle(),
*details);
simulator->Commit();
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
simulator->GetFinalRenderFrameHost());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
auto* blocked_browsing_data_model = pscs->blocked_browsing_data_model();
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
ASSERT_EQ(1u, allowed_browsing_data_model->size());
EXPECT_EQ("google.com",
*std::get_if<std::string>(
&*(*allowed_browsing_data_model->begin()).data_owner));
}
TEST_F(PageSpecificContentSettingsTest,
BrowsingDataModelSharedDictionaryPendingNavigationBlocked) {
NavigateAndCommit(GURL("http://google.com"));
std::unique_ptr<content::NavigationSimulator> simulator =
content::NavigationSimulator::CreateBrowserInitiated(
GURL("http://other.com"), web_contents());
simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
simulator->Start();
// Blocked a Shared Dictionary access.
const url::Origin origin = url::Origin::Create(GURL("http://google.com/"));
net::SharedDictionaryIsolationKey isolation_key(origin,
net::SchemefulSite(origin));
network::mojom::SharedDictionaryAccessDetailsPtr details =
network::mojom::SharedDictionaryAccessDetails::New(
network::mojom::SharedDictionaryAccessDetails::Type::kRead,
GURL("http://test.example/target"), isolation_key,
/*is_blocked=*/true);
GetHandle()->OnSharedDictionaryAccessed(simulator->GetNavigationHandle(),
*details);
simulator->Commit();
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
simulator->GetFinalRenderFrameHost());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
auto* blocked_browsing_data_model = pscs->blocked_browsing_data_model();
EXPECT_EQ(0, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
EXPECT_EQ(1, browsing_data::GetUniqueHostCount(*blocked_browsing_data_model));
ASSERT_EQ(1u, blocked_browsing_data_model->size());
EXPECT_EQ("google.com",
*std::get_if<std::string>(
&*(*blocked_browsing_data_model->begin()).data_owner));
}
#if !BUILDFLAG(IS_IOS)
TEST_F(PageSpecificContentSettingsTest,
IndicatorChangedOnContentSettingChange) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
// First trigger OnContentBlocked.
EXPECT_FALSE(content_settings->IsContentBlocked(
ContentSettingsType::CLIPBOARD_READ_WRITE));
content_settings->OnContentBlocked(ContentSettingsType::CLIPBOARD_READ_WRITE);
EXPECT_TRUE(content_settings->IsContentBlocked(
ContentSettingsType::CLIPBOARD_READ_WRITE));
// Simulate the user modifying the setting.
HostContentSettingsMap* map = settings_map();
ContentSettingsPattern pattern =
ContentSettingsPattern::FromURL(web_contents()->GetVisibleURL());
map->SetWebsiteSettingCustomScope(pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::CLIPBOARD_READ_WRITE,
base::Value(CONTENT_SETTING_ALLOW));
// Now the indicator is set to allowed.
EXPECT_TRUE(content_settings->IsContentAllowed(
ContentSettingsType::CLIPBOARD_READ_WRITE));
EXPECT_FALSE(content_settings->IsContentBlocked(
ContentSettingsType::CLIPBOARD_READ_WRITE));
// Simulate the user modifying the setting back to blocked.
map->SetWebsiteSettingCustomScope(pattern, ContentSettingsPattern::Wildcard(),
ContentSettingsType::CLIPBOARD_READ_WRITE,
base::Value(CONTENT_SETTING_BLOCK));
// Now the indicator is set to allowed.
EXPECT_TRUE(content_settings->IsContentBlocked(
ContentSettingsType::CLIPBOARD_READ_WRITE));
EXPECT_FALSE(content_settings->IsContentAllowed(
ContentSettingsType::CLIPBOARD_READ_WRITE));
}
#endif
TEST_F(PageSpecificContentSettingsTest, AllowedSitesCountedFromBothModels) {
// Populate containers with hosts.
bool blocked_by_policy = false;
auto googleURL = GURL("http://google.com");
auto exampleURL = GURL("https://example.com");
auto cookie1 = net::CanonicalCookie::CreateForTesting(googleURL, "k1=v",
base::Time::Now());
auto cookie2 = net::CanonicalCookie::CreateForTesting(exampleURL, "k2=v",
base::Time::Now());
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
googleURL,
googleURL,
{{*cookie1}},
blocked_by_policy});
GetHandle()->OnCookiesAccessed(web_contents()->GetPrimaryMainFrame(),
{content::CookieAccessDetails::Type::kRead,
exampleURL,
exampleURL,
{{*cookie2}},
blocked_by_policy});
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
auto* allowed_browsing_data_model = pscs->allowed_browsing_data_model();
allowed_browsing_data_model->AddBrowsingData(
url::Origin::Create(exampleURL),
BrowsingDataModel::StorageType::kTrustTokens, /*storage_size=*/0);
// Verify the size is counted without duplication of hosts.
EXPECT_EQ(2, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
}
TEST_F(PageSpecificContentSettingsTest, BrowsingDataModelStorageAccess) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* content_settings =
PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
bool blocked_by_policy = false;
content_settings->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://www.google.com")),
BrowsingDataModel::StorageType::kQuotaStorage, blocked_by_policy);
content_settings->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("http://example.com")),
BrowsingDataModel::StorageType::kLocalStorage, blocked_by_policy);
content_settings->OnBrowsingDataAccessed(
CreateUnpartitionedStorageKey(GURL("https://www.youtube.com")),
BrowsingDataModel::StorageType::kSharedStorage, blocked_by_policy);
const auto* allowed_browsing_data_model =
content_settings->allowed_browsing_data_model();
EXPECT_EQ(3, browsing_data::GetUniqueHostCount(*allowed_browsing_data_model));
}
class PageSpecificContentSettingsWithPrerenderTest
: public PageSpecificContentSettingsTest {
public:
PageSpecificContentSettingsWithPrerenderTest() = default;
content::RenderFrameHost* AddPrerender(const GURL& prerender_url) {
web_contents_delegate_ =
std::make_unique<content::test::ScopedPrerenderWebContentsDelegate>(
*web_contents());
content::RenderFrameHost* prerender_frame =
content::WebContentsTester::For(web_contents())
->AddPrerenderAndCommitNavigation(prerender_url);
DCHECK(prerender_frame);
DCHECK_EQ(prerender_frame->GetLifecycleState(),
content::RenderFrameHost::LifecycleState::kPrerendering);
DCHECK_EQ(prerender_frame->GetLastCommittedURL(), prerender_url);
return prerender_frame;
}
private:
content::test::ScopedPrerenderFeatureList prerender_feature_list_;
std::unique_ptr<content::test::ScopedPrerenderWebContentsDelegate>
web_contents_delegate_;
};
TEST_F(PageSpecificContentSettingsWithPrerenderTest, SiteDataAccessed) {
NavigateAndCommit(GURL("http://google.com"));
const GURL& prerender_url = GURL("http://google.com/foo");
content::RenderFrameHost* prerender_frame = AddPrerender(prerender_url);
PageSpecificContentSettings* pscs =
PageSpecificContentSettings::GetForFrame(prerender_frame);
ASSERT_NE(pscs, nullptr);
// Simulate cookie access.
{
MockSiteDataObserver mock_observer(web_contents());
// OnSiteDataAccessed should not be called for prerendering page.
EXPECT_CALL(mock_observer, OnSiteDataAccessed).Times(0);
// Set a cookie, block access to images, block mediastream access and block
// a popup.
GURL origin("http://google.com");
std::unique_ptr<net::CanonicalCookie> cookie1(
net::CanonicalCookie::CreateForTesting(origin, "A=B",
base::Time::Now()));
ASSERT_TRUE(cookie1);
pscs->OnCookiesAccessed({content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie1}},
false});
}
// Activate prerendering page.
{
MockSiteDataObserver mock_observer(web_contents());
// OnSiteDataAccessed should be called after page is activated.
EXPECT_CALL(mock_observer, OnSiteDataAccessed).Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
prerender_url, web_contents()->GetPrimaryMainFrame());
// TODO(crbug.com/40170513): Investigate how default referrer value
// is set and update here accordingly.
navigation->SetReferrer(blink::mojom::Referrer::New(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
}
TEST_F(PageSpecificContentSettingsWithPrerenderTest,
DelegateUpdatesSentAfterActivation) {
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
NavigateAndCommit(GURL("http://google.com"));
const GURL& prerender_url = GURL("http://google.com/foo");
content::RenderFrameHost* prerender_frame = AddPrerender(prerender_url);
PageSpecificContentSettings* pscs =
PageSpecificContentSettings::GetForFrame(prerender_frame);
ASSERT_NE(pscs, nullptr);
EXPECT_CALL(*mock_delegate, OnContentAllowed).Times(0);
EXPECT_CALL(*mock_delegate, OnContentBlocked).Times(0);
const GURL url = GURL("http://google.com");
auto cookie =
net::CanonicalCookie::CreateForTesting(url, "k=v", base::Time::Now());
pscs->OnCookiesAccessed({content::CookieAccessDetails::Type::kRead,
url,
url,
{{*cookie}},
/*blocked_by_policy=*/false});
PageSpecificContentSettings::StorageAccessed(StorageType::INDEXED_DB,
prerender_frame->GetGlobalId(),
prerender_frame->GetStorageKey(),
/*blocked_by_policy=*/true);
EXPECT_CALL(*mock_delegate, OnContentAllowed(ContentSettingsType::COOKIES))
.Times(1);
EXPECT_CALL(*mock_delegate, OnContentBlocked(ContentSettingsType::COOKIES))
.Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
prerender_url, web_contents()->GetPrimaryMainFrame());
navigation->SetReferrer(blink::mojom::Referrer::New(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
TEST_F(PageSpecificContentSettingsWithPrerenderTest,
UpdateLocationBarAfterActivation) {
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
NavigateAndCommit(GURL("http://google.com"));
const GURL& prerender_url = GURL("http://google.com/foo");
content::RenderFrameHost* prerender_frame = AddPrerender(prerender_url);
PageSpecificContentSettings* pscs =
PageSpecificContentSettings::GetForFrame(prerender_frame);
ASSERT_NE(pscs, nullptr);
EXPECT_CALL(*mock_delegate, UpdateLocationBar()).Times(0);
pscs->OnContentBlocked(ContentSettingsType::JAVASCRIPT);
EXPECT_CALL(*mock_delegate, UpdateLocationBar()).Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
prerender_url, web_contents()->GetPrimaryMainFrame());
navigation->Commit();
}
TEST_F(PageSpecificContentSettingsWithPrerenderTest, ContentAllowedAndBlocked) {
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
NavigateAndCommit(GURL("http://google.com"));
const GURL& prerender_url = GURL("http://google.com/foo");
content::RenderFrameHost* prerender_frame = AddPrerender(prerender_url);
PageSpecificContentSettings* pscs =
PageSpecificContentSettings::GetForFrame(prerender_frame);
ASSERT_NE(pscs, nullptr);
EXPECT_CALL(*mock_delegate, OnContentAllowed).Times(0);
EXPECT_CALL(*mock_delegate, OnContentBlocked).Times(0);
pscs->OnContentBlocked(ContentSettingsType::JAVASCRIPT);
pscs->OnContentAllowed(ContentSettingsType::COOKIES);
EXPECT_CALL(*mock_delegate, OnContentBlocked(ContentSettingsType::JAVASCRIPT))
.Times(1);
EXPECT_CALL(*mock_delegate, OnContentAllowed(ContentSettingsType::COOKIES))
.Times(1);
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(
prerender_url, web_contents()->GetPrimaryMainFrame());
// TODO(crbug.com/40170513): Investigate how default referrer value is
// set and update here accordingly.
navigation->SetReferrer(blink::mojom::Referrer::New(
web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
navigation->Commit();
}
TEST_F(PageSpecificContentSettingsTest, Topics) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
EXPECT_FALSE(pscs->HasAccessedTopics());
EXPECT_THAT(pscs->GetAccessedTopics(), testing::IsEmpty());
privacy_sandbox::CanonicalTopic topic(browsing_topics::Topic(1),
kTopicsAPITestTaxonomyVersion);
pscs->OnTopicAccessed(url::Origin::Create(GURL("https://foo.com")), false,
topic);
EXPECT_TRUE(pscs->HasAccessedTopics());
EXPECT_THAT(pscs->GetAccessedTopics(), testing::Contains(topic));
// Check that pscs->GetAccessedTopics() does not return the same topic ID
// twice.
privacy_sandbox::CanonicalTopic duplicate_topic(
browsing_topics::Topic(1), kTopicsAPITestTaxonomyVersion - 1);
pscs->OnTopicAccessed(url::Origin::Create(GURL("https://foo.com")), false,
duplicate_topic);
EXPECT_TRUE(pscs->HasAccessedTopics());
auto accessed_topics = pscs->GetAccessedTopics();
EXPECT_EQ(accessed_topics.size(), 1U);
EXPECT_THAT(accessed_topics, testing::Contains(topic));
}
class PageSpecificContentSettingsWithFencedFrameTest
: public PageSpecificContentSettingsTest {
public:
PageSpecificContentSettingsWithFencedFrameTest() {
scoped_feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kFencedFrames, {{"implementation_type", "mparch"}});
}
~PageSpecificContentSettingsWithFencedFrameTest() override = default;
content::RenderFrameHost* CreateFencedFrame(const GURL& url) {
content::RenderFrameHost* fenced_frame_root =
content::RenderFrameHostTester::For(
web_contents()->GetPrimaryMainFrame())
->AppendFencedFrame();
std::unique_ptr<content::NavigationSimulator> navigation_simulator =
content::NavigationSimulator::CreateRendererInitiated(
url, fenced_frame_root);
navigation_simulator->Commit();
return navigation_simulator->GetFinalRenderFrameHost();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(PageSpecificContentSettingsWithFencedFrameTest, SiteDataAccessed) {
NavigateAndCommit(GURL("http://google.com"));
const GURL& fenced_frame_url = GURL("http://foo.com");
content::RenderFrameHost* fenced_frame_root =
CreateFencedFrame(fenced_frame_url);
PageSpecificContentSettings* ff_pscs =
PageSpecificContentSettings::GetForFrame(fenced_frame_root);
ASSERT_NE(ff_pscs, nullptr);
// Simulate cookie access in fenced frame.
{
MockSiteDataObserver mock_observer(web_contents());
EXPECT_CALL(mock_observer, OnSiteDataAccessed).Times(1);
// Set a cookie, block access to images, block mediastream access and block
// a popup.
GURL origin("http://google.com");
std::unique_ptr<net::CanonicalCookie> cookie1(
net::CanonicalCookie::CreateForTesting(origin, "A=B",
base::Time::Now()));
ASSERT_TRUE(cookie1);
ff_pscs->OnCookiesAccessed({content::CookieAccessDetails::Type::kChange,
origin,
origin,
{{*cookie1}},
false});
}
}
TEST_F(PageSpecificContentSettingsWithFencedFrameTest, DelegateUpdatesSent) {
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
const GURL main_url("http://google.com");
const GURL& ff_url = GURL("http://foo.com");
const url::Origin& ff_origin = url::Origin::Create(ff_url);
NavigateAndCommit(main_url);
content::RenderFrameHost* fenced_frame_root = CreateFencedFrame(ff_url);
PageSpecificContentSettings* ff_pscs =
PageSpecificContentSettings::GetForFrame(fenced_frame_root);
ASSERT_NE(ff_pscs, nullptr);
EXPECT_CALL(*mock_delegate, OnContentAllowed(ContentSettingsType::COOKIES))
.Times(1);
EXPECT_CALL(*mock_delegate, OnContentBlocked(ContentSettingsType::COOKIES))
.Times(1);
auto cookie =
net::CanonicalCookie::CreateForTesting(ff_url, "k=v", base::Time::Now());
ff_pscs->OnCookiesAccessed({content::CookieAccessDetails::Type::kRead,
ff_url,
ff_url,
{{*cookie}},
/*blocked_by_policy=*/false});
PageSpecificContentSettings::StorageAccessed(
StorageType::INDEXED_DB, fenced_frame_root->GetGlobalId(),
fenced_frame_root->GetStorageKey(),
/*blocked_by_policy=*/true);
}
TEST_F(PageSpecificContentSettingsWithFencedFrameTest,
ContentAllowedAndBlocked) {
MockPageSpecificContentSettingsDelegate* mock_delegate =
InstallMockDelegate();
NavigateAndCommit(GURL("http://google.com"));
const GURL& fenced_frame_url = GURL("http://foo.com");
content::RenderFrameHost* fenced_frame_root =
CreateFencedFrame(fenced_frame_url);
PageSpecificContentSettings* ff_pscs =
PageSpecificContentSettings::GetForFrame(fenced_frame_root);
ASSERT_NE(ff_pscs, nullptr);
EXPECT_CALL(*mock_delegate, OnContentAllowed(ContentSettingsType::COOKIES))
.Times(1);
EXPECT_CALL(*mock_delegate, OnContentBlocked(ContentSettingsType::JAVASCRIPT))
.Times(1);
ff_pscs->OnContentBlocked(ContentSettingsType::JAVASCRIPT);
ff_pscs->OnContentAllowed(ContentSettingsType::COOKIES);
}
TEST_F(PageSpecificContentSettingsTest,
MediaIndicatorsBlockedDoNotOverrideInUse) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndEnableFeature(
content_settings::features::kLeftHandSideActivityIndicators);
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
pscs->OnMediaStreamPermissionSet(
web_contents()->GetLastCommittedURL(),
{PageSpecificContentSettings::kCameraAccessed});
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnMediaStreamPermissionSet(
web_contents()->GetLastCommittedURL(),
{PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked});
EXPECT_FALSE(pscs->GetMicrophoneCameraState().HasAny(
{PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked}));
}
TEST_F(PageSpecificContentSettingsTest, MediaIndicatorsMinHoldDurationDelay) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
pscs->set_media_stream_access_origin_for_testing(
web_contents()->GetLastCommittedURL());
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
// `kCameraAccessed` is true because of min hold duration.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
// Min hold duration equals to 5 seconds.
task_environment()->AdvanceClock(base::Seconds(2));
base::RunLoop().RunUntilIdle();
// `kCameraAccessed` is still true because only 2 seconds passed.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
task_environment()->AdvanceClock(base::Seconds(4));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
}
// Tests that if media indicator display time almost equals to the min hold
// duration, a delay should be not less than 1 second.
TEST_F(PageSpecificContentSettingsTest, AlmostExpiredMinHoldDurationDelay) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
pscs->set_media_stream_access_origin_for_testing(
web_contents()->GetLastCommittedURL());
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
task_environment()->AdvanceClock(base::Milliseconds(4800));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
// `kCameraAccessed` is true because of 1 second delay.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
task_environment()->AdvanceClock(base::Milliseconds(800));
base::RunLoop().RunUntilIdle();
// `kCameraAccessed` is true because waited only 800 ms.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
task_environment()->AdvanceClock(base::Milliseconds(300));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
}
TEST_F(PageSpecificContentSettingsTest,
MediaIndicatorsHoldAfterUseDurationDelay) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
pscs->set_media_stream_access_origin_for_testing(
web_contents()->GetLastCommittedURL());
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
// Min hold duration equals to 5 seconds.
task_environment()->AdvanceClock(base::Seconds(6));
base::RunLoop().RunUntilIdle();
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
// `kCameraAccessed` is true because of hold after use duration.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
// Hold after use duration equals to 1 seconds.
task_environment()->AdvanceClock(base::Seconds(2));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
}
TEST_F(PageSpecificContentSettingsTest,
MediaIndicatorsReenableCameraWhileMinHoldDurationDelay) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
pscs->set_media_stream_access_origin_for_testing(
web_contents()->GetLastCommittedURL());
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, false);
// `kCameraAccessed` is true because of the min hold duration.
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
// Reenabling a camera indicator
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
task_environment()->AdvanceClock(base::Seconds(6));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kCameraAccessed));
}
// Tests that `PageSpecificContentSettings` will return locally saved last used
// time if it exists.
TEST_F(PageSpecificContentSettingsTest, GetLastUsedTimeLocalTimeTest) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
base::Time time = base::Time::Now() - base::Seconds(30);
pscs->set_last_used_time_for_testing(ContentSettingsType::MEDIASTREAM_CAMERA,
time);
EXPECT_EQ(time,
pscs->GetLastUsedTime(ContentSettingsType::MEDIASTREAM_CAMERA));
}
TEST_F(PageSpecificContentSettingsTest, GetLastUsedReturnsDefaultTimeTest) {
NavigateAndCommit(GURL("http://google.com"));
HostContentSettingsMap* map = settings_map();
const GURL& url = web_contents()->GetLastCommittedURL();
map->SetContentSettingDefaultScope(
url, url, ContentSettingsType::MEDIASTREAM_CAMERA, CONTENT_SETTING_ALLOW);
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
EXPECT_EQ(base::Time(),
pscs->GetLastUsedTime(ContentSettingsType::MEDIASTREAM_CAMERA));
}
TEST_F(PageSpecificContentSettingsTest, GetLastUsedReturnCorrectTimeTest) {
NavigateAndCommit(GURL("http://google.com"));
HostContentSettingsMap* map = settings_map();
const GURL& url = web_contents()->GetLastCommittedURL();
map->SetContentSettingDefaultScope(
url, url, ContentSettingsType::MEDIASTREAM_CAMERA, CONTENT_SETTING_ALLOW);
base::Time time = base::Time::Now() - base::Hours(20);
map->UpdateLastUsedTime(url, url, ContentSettingsType::MEDIASTREAM_CAMERA,
time);
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
EXPECT_EQ(time,
pscs->GetLastUsedTime(ContentSettingsType::MEDIASTREAM_CAMERA));
}
// Tests that a permission blocked indicator is visible only for 60 seconds.
TEST_F(PageSpecificContentSettingsTest, MediaBlockedIndicatorsDismissDelay) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitWithFeatures(
{}, {content_settings::features::kLeftHandSideActivityIndicators});
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
EXPECT_FALSE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
PageSpecificContentSettings::MicrophoneCameraState
blocked_microphone_camera_state = {
PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked};
pscs->OnMediaStreamPermissionSet(GURL("http://google.com"),
blocked_microphone_camera_state);
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
task_environment()->AdvanceClock(base::Seconds(57));
base::RunLoop().RunUntilIdle();
// The timer is still there because a delay is 60 seconds.
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
task_environment()->AdvanceClock(base::Seconds(4));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
}
// Tests that a permission indicator will not be dismissed by a timer if was
// opened.
TEST_F(PageSpecificContentSettingsTest,
MediaIndicatorsDoNotDismissIfOpenedDelay) {
base::test::ScopedFeatureList scoped_feature_list_;
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
PageSpecificContentSettings::MicrophoneCameraState
blocked_microphone_camera_state = {
PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked};
pscs->OnMediaStreamPermissionSet(GURL("http://google.com"),
blocked_microphone_camera_state);
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
// 60 secodns timer is on.
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_MIC]
.IsRunning());
pscs->OnActivityIndicatorBubbleOpened(ContentSettingsType::MEDIASTREAM_MIC);
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
// A mic timer was stopped.
EXPECT_FALSE(pscs->get_media_blocked_indicator_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_MIC]
.IsRunning());
// A blockage indicator timer was stopped because an activity indicator popup
// bubble was opened.
task_environment()->AdvanceClock(base::Seconds(65));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneAccessed));
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneBlocked));
// An indicator popup bubble is closed.
pscs->OnActivityIndicatorBubbleClosed(ContentSettingsType::MEDIASTREAM_MIC);
// A mic timer was restarted.
EXPECT_TRUE(pscs->get_media_blocked_indicator_timer_for_testing()
[ContentSettingsType::MEDIASTREAM_MIC]
.IsRunning());
task_environment()->AdvanceClock(base::Seconds(61));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(pscs->get_media_blocked_indicator_timer_for_testing().contains(
ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneAccessed));
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneBlocked));
}
// Tests that a permission blocked state is reset after media started to be
// used.
TEST_F(PageSpecificContentSettingsTest, MediaBlockedStateIsResetIfMediaUsed) {
base::test::ScopedFeatureList scoped_feature_list_;
scoped_feature_list_.InitAndEnableFeature(
content_settings::features::kLeftHandSideActivityIndicators);
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
PageSpecificContentSettings::MicrophoneCameraState
blocked_microphone_camera_state = {
PageSpecificContentSettings::kMicrophoneAccessed,
PageSpecificContentSettings::kMicrophoneBlocked};
pscs->OnMediaStreamPermissionSet(GURL("http://google.com"),
blocked_microphone_camera_state);
EXPECT_TRUE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneBlocked));
// Camera is capturing, it should reset blocked microphone state.
pscs->OnCapturingStateChanged(ContentSettingsType::MEDIASTREAM_CAMERA, true);
EXPECT_FALSE(pscs->GetMicrophoneCameraState().Has(
PageSpecificContentSettings::kMicrophoneBlocked));
}
// The only currently implemented object-based type is for now ChromeOS only
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(PageSpecificContentSettingsTest, ObjectBasedInUseIndicator) {
NavigateAndCommit(GURL("http://google.com"));
PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame(
web_contents()->GetPrimaryMainFrame());
ASSERT_NE(pscs, nullptr);
// Initially, no indicator should be visible and `IsInUse` should be false.
EXPECT_FALSE(pscs->ShouldShowDeviceInUseIndicator(
ContentSettingsType::SMART_CARD_GUARD));
EXPECT_FALSE(pscs->IsInUse(ContentSettingsType::SMART_CARD_GUARD));
// When the first connection is used, indicator should start appearing and
// `IsInUse` should return `true`.
pscs->OnDeviceUsed(ContentSettingsType::SMART_CARD_GUARD);
EXPECT_TRUE(pscs->ShouldShowDeviceInUseIndicator(
ContentSettingsType::SMART_CARD_GUARD));
EXPECT_TRUE(pscs->IsInUse(ContentSettingsType::SMART_CARD_GUARD));
// When the last connection closes, the indicator should still be visible
// initially, even though `IsInUse` should be false already...
pscs->OnLastDeviceConnectionLost(ContentSettingsType::SMART_CARD_GUARD);
EXPECT_TRUE(pscs->ShouldShowDeviceInUseIndicator(
ContentSettingsType::SMART_CARD_GUARD));
EXPECT_FALSE(pscs->IsInUse(ContentSettingsType::SMART_CARD_GUARD));
// ...but 15 seconds later it should disappear.
task_environment()->AdvanceClock(base::Seconds(15));
EXPECT_FALSE(pscs->ShouldShowDeviceInUseIndicator(
ContentSettingsType::SMART_CARD_GUARD));
}
#endif
class PageSpecificContentSettingsIframeTest
: public PageSpecificContentSettingsTest {
public:
PageSpecificContentSettingsIframeTest() = default;
void SetUp() override { PageSpecificContentSettingsTest::SetUp(); }
// Navigates both the parent and child frames, and then gets content settings
// from the child frame.
blink::mojom::RendererContentSettingsPtr NavigateAndGetContentSettings(
GURL parent_url,
GURL child_url) {
NavigateAndCommit(parent_url);
content::NavigationHandleObserver observer(web_contents(), child_url);
content::RenderFrameHost* child_rfh =
content::RenderFrameHostTester::For(main_rfh())->AppendChild("child");
auto simulator = content::NavigationSimulator::CreateRendererInitiated(
child_url, child_rfh);
simulator->SetTransition(ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
simulator->Commit();
return observer.content_settings()->Clone();
}
};
// Tests that the content settings are correctly set if a secondary url is
// blocked.
TEST_F(PageSpecificContentSettingsIframeTest, SecondaryUrlBlocked) {
GURL parent_url("https://parent.com");
GURL child_url("https://child.com");
settings_map()->SetContentSettingDefaultScope(parent_url, child_url,
ContentSettingsType::JAVASCRIPT,
CONTENT_SETTING_BLOCK);
blink::mojom::RendererContentSettingsPtr content_settings =
NavigateAndGetContentSettings(parent_url, child_url);
EXPECT_FALSE(content_settings->allow_script);
}
// Tests that the content settings are correctly set if an unrelated secondary
// url is blocked.
TEST_F(PageSpecificContentSettingsIframeTest, UnrelatedSecondaryUrlBlocked) {
GURL other_url("https://other.com");
GURL parent_url("https://parent.com");
GURL child_url("https://child.com");
settings_map()->SetContentSettingDefaultScope(other_url, child_url,
ContentSettingsType::JAVASCRIPT,
CONTENT_SETTING_BLOCK);
blink::mojom::RendererContentSettingsPtr content_settings =
NavigateAndGetContentSettings(parent_url, child_url);
EXPECT_TRUE(content_settings->allow_script);
}
// Tests that the content settings are correctly set if the primary and
// secondary urls are identical.
TEST_F(PageSpecificContentSettingsIframeTest, PrimarySecondaryIdentical) {
GURL parent_url("https://parent.com");
GURL child_url = parent_url;
GURL other_url("https://other.com");
// All content settings that are sent to the renderer are top-origin scoped.
// Secondary_url is ignored. This call is functionally equivalent to setting
// secondary_url = wildcard.
settings_map()->SetContentSettingDefaultScope(parent_url, other_url,
ContentSettingsType::JAVASCRIPT,
CONTENT_SETTING_BLOCK);
blink::mojom::RendererContentSettingsPtr content_settings =
NavigateAndGetContentSettings(parent_url, child_url);
EXPECT_FALSE(content_settings->allow_script);
}
} // namespace content_settings