blob: f90c84a85f1c9451a30a74f35084d7ed4015d59a [file] [log] [blame]
// Copyright (c) 2012 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 <memory>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/api/content_settings/content_settings_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h"
#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/features.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_result.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/webplugininfo.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/test_extension_registry_observer.h"
#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/public/browser/plugin_service.h"
#endif
namespace extensions {
using ContextType = ExtensionApiTest::ContextType;
class ExtensionContentSettingsApiTest : public ExtensionApiTest {
public:
explicit ExtensionContentSettingsApiTest(
ContextType context_type = ContextType::kNone)
: ExtensionApiTest(context_type) {}
~ExtensionContentSettingsApiTest() override = default;
ExtensionContentSettingsApiTest(const ExtensionContentSettingsApiTest&) =
delete;
ExtensionContentSettingsApiTest& operator=(
const ExtensionContentSettingsApiTest&) = delete;
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
// The browser might get closed later (and therefore be destroyed), so we
// save the profile.
profile_ = browser()->profile();
// Closing the last browser window also releases a KeepAlive. Make
// sure it's not the last one, so the message loop doesn't quit
// unexpectedly.
keep_alive_ = std::make_unique<ScopedKeepAlive>(
KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED);
profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>(
profile_, ProfileKeepAliveOrigin::kBrowserWindow);
}
void TearDownOnMainThread() override {
profile_keep_alive_.reset();
// BrowserProcess::Shutdown() needs to be called in a message loop, so we
// post a task to release the keep alive, then run the message loop.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&std::unique_ptr<ScopedKeepAlive>::reset,
base::Unretained(&keep_alive_), nullptr));
content::RunAllPendingInMessageLoop();
ExtensionApiTest::TearDownOnMainThread();
}
protected:
void CheckContentSettingsSet() {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile_);
content_settings::CookieSettings* cookie_settings =
CookieSettingsFactory::GetForProfile(profile_).get();
// Check default content settings by using an unknown URL.
GURL example_url("http://www.example.com");
EXPECT_TRUE(
cookie_settings->IsFullCookieAccessAllowed(example_url, example_url));
EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(example_url));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::IMAGES));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::JAVASCRIPT));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::POPUPS));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::GEOLOCATION));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::NOTIFICATIONS));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::AUTOMATIC_DOWNLOADS));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(example_url, example_url,
ContentSettingsType::AUTOPLAY));
// Check content settings for www.google.com
GURL url("http://www.google.com");
EXPECT_FALSE(cookie_settings->IsFullCookieAccessAllowed(url, url));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::IMAGES));
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url, ContentSettingsType::JAVASCRIPT));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::POPUPS));
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION));
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
EXPECT_EQ(
CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url, ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url,
ContentSettingsType::AUTOMATIC_DOWNLOADS));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::AUTOPLAY));
}
void CheckContentSettingsDefault() {
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile_);
content_settings::CookieSettings* cookie_settings =
CookieSettingsFactory::GetForProfile(profile_).get();
// Check content settings for www.google.com
GURL url("http://www.google.com");
EXPECT_TRUE(cookie_settings->IsFullCookieAccessAllowed(url, url));
EXPECT_FALSE(cookie_settings->IsCookieSessionOnly(url));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::IMAGES));
EXPECT_EQ(
CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::JAVASCRIPT));
EXPECT_EQ(CONTENT_SETTING_BLOCK,
map->GetContentSetting(url, url, ContentSettingsType::POPUPS));
EXPECT_EQ(
CONTENT_SETTING_ASK,
map->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION));
EXPECT_EQ(
CONTENT_SETTING_ASK,
map->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
EXPECT_EQ(
CONTENT_SETTING_ASK,
map->GetContentSetting(url, url, ContentSettingsType::MEDIASTREAM_MIC));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(url, url,
ContentSettingsType::MEDIASTREAM_CAMERA));
EXPECT_EQ(CONTENT_SETTING_ASK,
map->GetContentSetting(url, url,
ContentSettingsType::AUTOMATIC_DOWNLOADS));
EXPECT_EQ(CONTENT_SETTING_ALLOW,
map->GetContentSetting(url, url, ContentSettingsType::AUTOPLAY));
}
// Returns a snapshot of content settings for a given URL.
std::vector<int> GetContentSettingsSnapshot(const GURL& url) {
std::vector<int> content_settings;
HostContentSettingsMap* map =
HostContentSettingsMapFactory::GetForProfile(profile_);
content_settings::CookieSettings* cookie_settings =
CookieSettingsFactory::GetForProfile(profile_).get();
content_settings.push_back(
cookie_settings->IsFullCookieAccessAllowed(url, url));
content_settings.push_back(cookie_settings->IsCookieSessionOnly(url));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::IMAGES));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::JAVASCRIPT));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::POPUPS));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::GEOLOCATION));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::MEDIASTREAM_MIC));
content_settings.push_back(map->GetContentSetting(
url, url, ContentSettingsType::MEDIASTREAM_CAMERA));
content_settings.push_back(map->GetContentSetting(
url, url, ContentSettingsType::AUTOMATIC_DOWNLOADS));
content_settings.push_back(
map->GetContentSetting(url, url, ContentSettingsType::AUTOPLAY));
return content_settings;
}
private:
raw_ptr<Profile> profile_ = nullptr;
std::unique_ptr<ScopedKeepAlive> keep_alive_;
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
};
class ExtensionContentSettingsApiTestWithContextType
: public ExtensionContentSettingsApiTest,
public testing::WithParamInterface<ContextType> {
public:
ExtensionContentSettingsApiTestWithContextType()
: ExtensionContentSettingsApiTest(GetParam()) {}
~ExtensionContentSettingsApiTestWithContextType() override = default;
ExtensionContentSettingsApiTestWithContextType(
const ExtensionContentSettingsApiTestWithContextType&) = delete;
ExtensionContentSettingsApiTestWithContextType& operator=(
const ExtensionContentSettingsApiTestWithContextType&) = delete;
};
INSTANTIATE_TEST_SUITE_P(PersistentBackground,
ExtensionContentSettingsApiTestWithContextType,
::testing::Values(ContextType::kPersistentBackground));
INSTANTIATE_TEST_SUITE_P(ServiceWorker,
ExtensionContentSettingsApiTestWithContextType,
::testing::Values(ContextType::kServiceWorker));
IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, Standard) {
CheckContentSettingsDefault();
static constexpr char kExtensionPath[] = "content_settings/standard";
EXPECT_TRUE(RunExtensionTest(kExtensionPath, {.page_url = "test.html"}))
<< message_;
CheckContentSettingsSet();
// The settings should not be reset when the extension is reloaded.
ReloadExtension(last_loaded_extension_id());
CheckContentSettingsSet();
// Uninstalling and installing the extension (without running the test that
// calls the extension API) should clear the settings.
TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()),
last_loaded_extension_id());
UninstallExtension(last_loaded_extension_id());
observer.WaitForExtensionUninstalled();
CheckContentSettingsDefault();
LoadExtension(test_data_dir_.AppendASCII(kExtensionPath));
CheckContentSettingsDefault();
}
IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiTestWithContextType,
UnsupportedDefaultSettings) {
const char kExtensionPath[] = "content_settings/unsupporteddefaultsettings";
EXPECT_TRUE(RunExtensionTest(kExtensionPath)) << message_;
}
// Tests if an extension clearing content settings for one content type leaves
// the others unchanged.
IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiTestWithContextType,
ClearProperlyGranular) {
const char kExtensionPath[] = "content_settings/clearproperlygranular";
EXPECT_TRUE(RunExtensionTest(kExtensionPath)) << message_;
}
// Tests if changing permissions in incognito mode keeps the previous state of
// regular mode.
IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, IncognitoIsolation) {
GURL url("http://www.example.com");
// Record previous state of content settings.
std::vector<int> content_settings_before = GetContentSettingsSnapshot(url);
// Run extension, set all permissions to allow, and check if they are changed.
ASSERT_TRUE(RunExtensionTest("content_settings/incognitoisolation",
{.page_url = "test.html",
.custom_arg = "allow",
.open_in_incognito = true},
{.allow_in_incognito = true}))
<< message_;
// Get content settings after running extension to ensure nothing is changed.
std::vector<int> content_settings_after = GetContentSettingsSnapshot(url);
EXPECT_EQ(content_settings_before, content_settings_after);
// Run extension, set all permissions to block, and check if they are changed.
ASSERT_TRUE(RunExtensionTest("content_settings/incognitoisolation",
{.page_url = "test.html",
.custom_arg = "block",
.open_in_incognito = true},
{.allow_in_incognito = true}))
<< message_;
// Get content settings after running extension to ensure nothing is changed.
content_settings_after = GetContentSettingsSnapshot(url);
EXPECT_EQ(content_settings_before, content_settings_after);
}
// Tests if changing incognito mode permissions in regular profile are rejected.
IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest,
IncognitoNotAllowedInRegular) {
EXPECT_FALSE(
RunExtensionTest("content_settings/incognitoisolation",
{.page_url = "test.html", .custom_arg = "allow"}))
<< message_;
}
IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiTestWithContextType,
EmbeddedSettingsMetric) {
base::HistogramTester histogram_tester;
const char kExtensionPath[] = "content_settings/embeddedsettingsmetric";
EXPECT_TRUE(RunExtensionTest(kExtensionPath)) << message_;
size_t num_values = 0;
int images_type = ContentSettingTypeToHistogramValue(
ContentSettingsType::IMAGES, &num_values);
int geolocation_type = ContentSettingTypeToHistogramValue(
ContentSettingsType::GEOLOCATION, &num_values);
int cookies_type = ContentSettingTypeToHistogramValue(
ContentSettingsType::COOKIES, &num_values);
histogram_tester.ExpectBucketCount(
"ContentSettings.ExtensionEmbeddedSettingSet", images_type, 1);
histogram_tester.ExpectBucketCount(
"ContentSettings.ExtensionEmbeddedSettingSet", geolocation_type, 1);
histogram_tester.ExpectTotalCount(
"ContentSettings.ExtensionEmbeddedSettingSet", 2);
histogram_tester.ExpectBucketCount(
"ContentSettings.ExtensionNonEmbeddedSettingSet", images_type, 1);
histogram_tester.ExpectBucketCount(
"ContentSettings.ExtensionNonEmbeddedSettingSet", cookies_type, 1);
histogram_tester.ExpectTotalCount(
"ContentSettings.ExtensionNonEmbeddedSettingSet", 2);
}
#if BUILDFLAG(ENABLE_PLUGINS)
IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, ConsoleErrorTest) {
constexpr char kExtensionPath[] = "content_settings/disablepluginsapi";
const extensions::Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(kExtensionPath));
ASSERT_TRUE(extension);
auto* web_contents = extensions::ProcessManager::Get(profile())
->GetBackgroundHostForExtension(extension->id())
->host_contents();
content::WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern("*contentSettings.plugins is deprecated.*");
browsertest_util::ExecuteScriptInBackgroundPageNoWait(
profile(), extension->id(), "setPluginsSetting()");
console_observer.Wait();
EXPECT_EQ(1u, console_observer.messages().size());
}
#endif // BUILDFLAG(ENABLE_PLUGINS)
} // namespace extensions