| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/profile_resetter/profile_resetter.h" |
| |
| #include <stddef.h> |
| |
| #include <functional> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_path_override.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/extension_service_test_base.h" |
| #include "chrome/browser/extensions/tab_helper.h" |
| #include "chrome/browser/google/google_brand.h" |
| #include "chrome/browser/prefs/session_startup_pref.h" |
| #include "chrome/browser/profile_resetter/brandcode_config_fetcher.h" |
| #include "chrome/browser/profile_resetter/profile_reset_report.pb.h" |
| #include "chrome/browser/profile_resetter/profile_resetter_test_base.h" |
| #include "chrome/browser/profile_resetter/resettable_settings_snapshot.h" |
| #include "chrome/browser/search/background/ntp_custom_background_service.h" |
| #include "chrome/browser/search/background/ntp_custom_background_service_factory.h" |
| #include "chrome/browser/search_engines/template_url_service_factory.h" |
| #include "chrome/browser/search_engines/template_url_service_test_util.h" |
| #include "chrome/browser/themes/test/theme_service_changed_waiter.h" |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/webdata_services/web_data_service_factory.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/content_settings/core/browser/content_settings_info.h" |
| #include "components/content_settings/core/browser/content_settings_registry.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/browser/website_settings_info.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/search_engines/template_url_service.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/disable_reason.h" |
| #include "extensions/browser/extension_registrar.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_status_code.h" |
| #include "net/http/http_util.h" |
| #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/mojom/url_response_head.mojom.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "services/network/test/test_utils.h" |
| #include "url/gurl.h" |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "base/files/file_util.h" |
| #include "base/path_service.h" |
| #include "base/process/process_handle.h" |
| #include "base/rand_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/win/scoped_com_initializer.h" |
| #include "base/win/shortcut.h" |
| #endif |
| |
| using extensions::mojom::ManifestLocation; |
| |
| namespace { |
| |
| const char kDistributionConfig[] = "{" |
| " \"homepage\" : \"http://www.foo.com\"," |
| " \"homepage_is_newtabpage\" : false," |
| " \"browser\" : {" |
| " \"show_home_button\" : true" |
| " }," |
| " \"session\" : {" |
| " \"restore_on_startup\" : 4," |
| " \"startup_urls\" : [\"http://goo.gl\", \"http://foo.de\"]" |
| " }," |
| " \"search_provider_overrides\" : [" |
| " {" |
| " \"name\" : \"first\"," |
| " \"keyword\" : \"firstkey\"," |
| " \"search_url\" : \"http://www.foo.com/s?q={searchTerms}\"," |
| " \"favicon_url\" : \"http://www.foo.com/favicon.ico\"," |
| " \"suggest_url\" : \"http://www.foo.com/s?q={searchTerms}\"," |
| " \"encoding\" : \"UTF-8\"," |
| " \"id\" : 1001" |
| " }" |
| " ]," |
| " \"extensions\" : {" |
| " \"settings\" : {" |
| " \"placeholder_for_id\": {" |
| " }" |
| " }" |
| " }" |
| "}"; |
| |
| const char kXmlConfig[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
| "<response protocol=\"3.0\" server=\"prod\">" |
| "<app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\">" |
| "<data index=\"skipfirstrunui-importsearch-defaultbrowser\" " |
| "name=\"install\" status=\"ok\">" |
| "placeholder_for_data" |
| "</data>" |
| "</app>" |
| "</response>"; |
| |
| using extensions::Extension; |
| using extensions::Manifest; |
| |
| class FakeNtpCustomBackgroundService : public NtpCustomBackgroundService { |
| public: |
| using NtpCustomBackgroundService::NtpCustomBackgroundService; |
| void FetchCustomBackgroundAndExtractBackgroundColor( |
| const GURL& image_url, |
| const GURL& fetch_url) override {} |
| }; |
| |
| std::unique_ptr<KeyedService> CreateFakeNtpCustomBackgroundService( |
| content::BrowserContext* context) { |
| Profile* profile = Profile::FromBrowserContext(context); |
| return std::make_unique<FakeNtpCustomBackgroundService>(profile); |
| } |
| |
| // ProfileResetterTest -------------------------------------------------------- |
| |
| // ProfileResetterTest sets up the extension, WebData and TemplateURL services. |
| class ProfileResetterTest : public extensions::ExtensionServiceTestBase, |
| public ProfileResetterTestBase { |
| public: |
| ProfileResetterTest(); |
| ~ProfileResetterTest() override; |
| |
| protected: |
| void SetUp() override; |
| |
| TestingProfile* profile() { return profile_.get(); } |
| |
| private: |
| #if BUILDFLAG(IS_WIN) |
| base::ScopedPathOverride user_desktop_override_; |
| base::ScopedPathOverride app_dir_override_; |
| base::ScopedPathOverride start_menu_override_; |
| base::ScopedPathOverride taskbar_pins_override_; |
| base::win::ScopedCOMInitializer com_init_; |
| #endif |
| }; |
| |
| ProfileResetterTest::ProfileResetterTest() |
| #if BUILDFLAG(IS_WIN) |
| : user_desktop_override_(base::DIR_USER_DESKTOP), |
| app_dir_override_(base::DIR_ROAMING_APP_DATA), |
| start_menu_override_(base::DIR_START_MENU), |
| taskbar_pins_override_(base::DIR_TASKBAR_PINS) |
| #endif |
| {} |
| |
| ProfileResetterTest::~ProfileResetterTest() = default; |
| |
| void ProfileResetterTest::SetUp() { |
| extensions::ExtensionServiceTestBase::SetUp(); |
| ExtensionServiceInitParams params; |
| params.testing_factories = { |
| TestingProfile::TestingFactory( |
| NtpCustomBackgroundServiceFactory::GetInstance(), |
| base::BindRepeating(&CreateFakeNtpCustomBackgroundService)), |
| TestingProfile::TestingFactory( |
| TemplateURLServiceFactory::GetInstance(), |
| TemplateURLServiceTestUtil::GetTemplateURLServiceTestingFactory()), |
| }; |
| |
| InitializeExtensionService(std::move(params)); |
| |
| google_brand::BrandForTesting brand_for_testing(""); |
| resetter_ = std::make_unique<ProfileResetter>(profile()); |
| } |
| |
| // ConfigParserTest ----------------------------------------------------------- |
| |
| class ConfigParserTest : public testing::Test { |
| protected: |
| ConfigParserTest(); |
| ~ConfigParserTest() override; |
| |
| std::unique_ptr<BrandcodeConfigFetcher> WaitForRequest(const GURL& url); |
| |
| network::TestURLLoaderFactory& test_url_loader_factory() { |
| return test_url_loader_factory_; |
| } |
| |
| private: |
| MOCK_METHOD0(Callback, void(void)); |
| |
| content::BrowserTaskEnvironment task_environment_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| data_decoder::test::InProcessDataDecoder data_decoder_; |
| }; |
| |
| ConfigParserTest::ConfigParserTest() |
| : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {} |
| |
| ConfigParserTest::~ConfigParserTest() = default; |
| |
| std::unique_ptr<BrandcodeConfigFetcher> ConfigParserTest::WaitForRequest( |
| const GURL& url) { |
| EXPECT_CALL(*this, Callback()); |
| std::string upload_data; |
| test_url_loader_factory_.SetInterceptor( |
| base::BindLambdaForTesting([&](const network::ResourceRequest& request) { |
| upload_data = network::GetUploadData(request); |
| })); |
| std::unique_ptr<BrandcodeConfigFetcher> fetcher(new BrandcodeConfigFetcher( |
| &test_url_loader_factory_, |
| base::BindOnce(&ConfigParserTest::Callback, base::Unretained(this)), url, |
| "ABCD")); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(fetcher->IsActive()); |
| // Look for the brand code in the request. |
| EXPECT_NE(std::string::npos, upload_data.find("ABCD")); |
| return fetcher; |
| } |
| |
| // A helper class to create/delete/check a Chrome desktop shortcut on Windows. |
| class ShortcutHandler { |
| public: |
| ShortcutHandler(); |
| |
| ShortcutHandler(const ShortcutHandler&) = delete; |
| ShortcutHandler& operator=(const ShortcutHandler&) = delete; |
| |
| ~ShortcutHandler(); |
| |
| static bool IsSupported(); |
| ShortcutCommand CreateWithArguments(const std::wstring& name, |
| const std::wstring& args); |
| void CheckShortcutHasArguments(const std::wstring& desired_args) const; |
| void Delete(); |
| void HideFile(); |
| bool IsFileHidden() const; |
| |
| private: |
| #if BUILDFLAG(IS_WIN) |
| base::FilePath shortcut_path_; |
| #endif |
| }; |
| |
| #if BUILDFLAG(IS_WIN) |
| ShortcutHandler::ShortcutHandler() = default; |
| |
| ShortcutHandler::~ShortcutHandler() { |
| if (!shortcut_path_.empty()) |
| Delete(); |
| } |
| |
| // static |
| bool ShortcutHandler::IsSupported() { |
| return true; |
| } |
| |
| ShortcutCommand ShortcutHandler::CreateWithArguments(const std::wstring& name, |
| const std::wstring& args) { |
| EXPECT_TRUE(shortcut_path_.empty()); |
| base::FilePath path_to_create; |
| EXPECT_TRUE(base::PathService::Get(base::DIR_USER_DESKTOP, &path_to_create)); |
| path_to_create = path_to_create.Append(name); |
| EXPECT_FALSE(base::PathExists(path_to_create)) << path_to_create.value(); |
| |
| base::FilePath path_exe; |
| EXPECT_TRUE(base::PathService::Get(base::FILE_EXE, &path_exe)); |
| base::win::ShortcutProperties shortcut_properties; |
| shortcut_properties.set_target(path_exe); |
| shortcut_properties.set_arguments(args); |
| EXPECT_TRUE(base::win::CreateOrUpdateShortcutLink( |
| path_to_create, shortcut_properties, |
| base::win::ShortcutOperation::kCreateAlways)) |
| << path_to_create.value(); |
| shortcut_path_ = path_to_create; |
| return ShortcutCommand(shortcut_path_, args); |
| } |
| |
| void ShortcutHandler::CheckShortcutHasArguments( |
| const std::wstring& desired_args) const { |
| EXPECT_FALSE(shortcut_path_.empty()); |
| std::wstring args; |
| EXPECT_TRUE(base::win::ResolveShortcut(shortcut_path_, NULL, &args)); |
| EXPECT_EQ(desired_args, args); |
| } |
| |
| void ShortcutHandler::Delete() { |
| EXPECT_FALSE(shortcut_path_.empty()); |
| EXPECT_TRUE(base::DeleteFile(shortcut_path_)); |
| shortcut_path_.clear(); |
| } |
| |
| void ShortcutHandler::HideFile() { |
| DWORD attributes = ::GetFileAttributes(shortcut_path_.value().c_str()); |
| ASSERT_NE(attributes, INVALID_FILE_ATTRIBUTES); |
| ASSERT_TRUE(::SetFileAttributes(shortcut_path_.value().c_str(), |
| attributes | FILE_ATTRIBUTE_HIDDEN)); |
| } |
| |
| bool ShortcutHandler::IsFileHidden() const { |
| DWORD attributes = ::GetFileAttributes(shortcut_path_.value().c_str()); |
| EXPECT_NE(attributes, INVALID_FILE_ATTRIBUTES); |
| return attributes & FILE_ATTRIBUTE_HIDDEN; |
| } |
| |
| #else |
| ShortcutHandler::ShortcutHandler() = default; |
| |
| ShortcutHandler::~ShortcutHandler() = default; |
| |
| // static |
| bool ShortcutHandler::IsSupported() { |
| return false; |
| } |
| |
| ShortcutCommand ShortcutHandler::CreateWithArguments(const std::wstring& name, |
| const std::wstring& args) { |
| return ShortcutCommand(); |
| } |
| |
| void ShortcutHandler::CheckShortcutHasArguments( |
| const std::wstring& desired_args) const {} |
| |
| void ShortcutHandler::Delete() { |
| } |
| |
| void ShortcutHandler::HideFile() {} |
| |
| bool ShortcutHandler::IsFileHidden() const { |
| return false; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| // helper functions ----------------------------------------------------------- |
| |
| scoped_refptr<Extension> CreateExtension(const std::u16string& name, |
| const base::FilePath& path, |
| ManifestLocation location, |
| extensions::Manifest::Type type, |
| bool installed_by_default) { |
| base::Value::Dict manifest; |
| manifest.Set(extensions::manifest_keys::kVersion, "1.0.0.0"); |
| manifest.Set(extensions::manifest_keys::kName, name); |
| manifest.Set(extensions::manifest_keys::kManifestVersion, 2); |
| switch (type) { |
| case extensions::Manifest::TYPE_THEME: |
| manifest.Set(extensions::manifest_keys::kTheme, base::Value::Dict()); |
| break; |
| case extensions::Manifest::TYPE_HOSTED_APP: |
| manifest.SetByDottedPath(extensions::manifest_keys::kLaunchWebURL, |
| "http://www.google.com"); |
| manifest.Set(extensions::manifest_keys::kUpdateURL, |
| "http://clients2.google.com/service/update2/crx"); |
| break; |
| case extensions::Manifest::TYPE_EXTENSION: |
| // do nothing |
| break; |
| default: |
| NOTREACHED(); |
| } |
| manifest.SetByDottedPath(extensions::manifest_keys::kOmniboxKeyword, name); |
| std::u16string error; |
| scoped_refptr<Extension> extension = Extension::Create( |
| path, |
| location, |
| manifest, |
| installed_by_default ? Extension::WAS_INSTALLED_BY_DEFAULT |
| : Extension::NO_FLAGS, |
| &error); |
| EXPECT_TRUE(extension.get() != nullptr) << error; |
| return extension; |
| } |
| |
| void ReplaceString(std::string* str, |
| const std::string& placeholder, |
| const std::string& substitution) { |
| ASSERT_NE(static_cast<std::string*>(nullptr), str); |
| size_t placeholder_pos = str->find(placeholder); |
| ASSERT_NE(std::string::npos, placeholder_pos); |
| str->replace(placeholder_pos, placeholder.size(), substitution); |
| } |
| |
| /********************* Tests *********************/ |
| |
| TEST_F(ProfileResetterTest, ResetNothing) { |
| // The callback should be called even if there is nothing to reset. |
| ResetAndWait(0); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) { |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE, kDistributionConfig); |
| |
| TemplateURLService* model = |
| TemplateURLServiceFactory::GetForProfile(profile()); |
| const TemplateURL* default_engine = model->GetDefaultSearchProvider(); |
| ASSERT_NE(static_cast<TemplateURL*>(nullptr), default_engine); |
| EXPECT_EQ(u"first", default_engine->short_name()); |
| EXPECT_EQ(u"firstkey", default_engine->keyword()); |
| EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", default_engine->url()); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetDefaultSearchEnginePartially) { |
| // Search engine's logic is tested by |
| // TemplateURLServiceTest.RepairPrepopulatedSearchEngines. |
| // Make sure TemplateURLService has loaded. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE); |
| |
| TemplateURLService* model = |
| TemplateURLServiceFactory::GetForProfile(profile()); |
| TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs(); |
| |
| // The second call should produce no effect. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE); |
| |
| EXPECT_EQ(urls, model->GetTemplateURLs()); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetHomepageNonOrganic) { |
| PrefService* prefs = profile()->GetPrefs(); |
| DCHECK(prefs); |
| prefs->SetBoolean(prefs::kHomePageIsNewTabPage, true); |
| prefs->SetString(prefs::kHomePage, "http://google.com"); |
| prefs->SetBoolean(prefs::kShowHomeButton, false); |
| |
| ResetAndWait(ProfileResetter::HOMEPAGE, kDistributionConfig); |
| |
| EXPECT_FALSE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage)); |
| EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage)); |
| EXPECT_TRUE(prefs->GetBoolean(prefs::kShowHomeButton)); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetHomepagePartially) { |
| PrefService* prefs = profile()->GetPrefs(); |
| DCHECK(prefs); |
| prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false); |
| prefs->SetString(prefs::kHomePage, "http://www.foo.com"); |
| prefs->SetBoolean(prefs::kShowHomeButton, true); |
| |
| ResetAndWait(ProfileResetter::HOMEPAGE); |
| |
| EXPECT_TRUE(prefs->GetBoolean(prefs::kHomePageIsNewTabPage)); |
| EXPECT_EQ("http://www.foo.com", prefs->GetString(prefs::kHomePage)); |
| EXPECT_FALSE(prefs->GetBoolean(prefs::kShowHomeButton)); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetContentSettings) { |
| HostContentSettingsMap* host_content_settings_map = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| GURL url("http://example.org"); |
| std::map<ContentSettingsType, ContentSetting> default_settings; |
| |
| // TODO(raymes): Clean up this test so that we don't have such ugly iteration |
| // over the content settings. |
| content_settings::ContentSettingsRegistry* registry = |
| content_settings::ContentSettingsRegistry::GetInstance(); |
| for (const content_settings::ContentSettingsInfo* info : *registry) { |
| ContentSettingsType content_type = info->website_settings_info()->type(); |
| if (content_type == ContentSettingsType::MIXEDSCRIPT || |
| content_type == ContentSettingsType::PROTOCOL_HANDLERS) { |
| // These types are excluded because one can't call |
| // GetDefaultContentSetting() for them. |
| continue; |
| } |
| ContentSetting default_setting = |
| host_content_settings_map->GetDefaultContentSetting(content_type, |
| nullptr); |
| default_settings[content_type] = default_setting; |
| ContentSetting wildcard_setting = default_setting == CONTENT_SETTING_BLOCK |
| ? CONTENT_SETTING_ALLOW |
| : CONTENT_SETTING_BLOCK; |
| ContentSetting site_setting = default_setting == CONTENT_SETTING_ALLOW |
| ? CONTENT_SETTING_ALLOW |
| : CONTENT_SETTING_BLOCK; |
| if (info->IsSettingValid(wildcard_setting)) { |
| host_content_settings_map->SetDefaultContentSetting(content_type, |
| wildcard_setting); |
| } |
| if (info->IsSettingValid(site_setting)) { |
| host_content_settings_map->SetContentSettingDefaultScope( |
| url, url, content_type, site_setting); |
| ContentSettingsForOneType host_settings = |
| host_content_settings_map->GetSettingsForOneType(content_type); |
| EXPECT_EQ(2U, host_settings.size()); |
| } |
| } |
| |
| ResetAndWait(ProfileResetter::CONTENT_SETTINGS); |
| |
| for (const content_settings::ContentSettingsInfo* info : *registry) { |
| ContentSettingsType content_type = info->website_settings_info()->type(); |
| if (content_type == ContentSettingsType::MIXEDSCRIPT || |
| content_type == ContentSettingsType::PROTOCOL_HANDLERS) |
| continue; |
| ContentSetting default_setting = |
| host_content_settings_map->GetDefaultContentSetting(content_type, |
| nullptr); |
| EXPECT_TRUE(default_settings.count(content_type)); |
| EXPECT_EQ(default_settings[content_type], default_setting); |
| ContentSetting site_setting = host_content_settings_map->GetContentSetting( |
| GURL("example.org"), GURL(), content_type); |
| EXPECT_EQ(default_setting, site_setting); |
| |
| ContentSettingsForOneType host_settings = |
| host_content_settings_map->GetSettingsForOneType(content_type); |
| EXPECT_EQ(1U, host_settings.size()); |
| } |
| } |
| |
| TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) { |
| service_->Init(); |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile()); |
| test::ThemeServiceChangedWaiter waiter(theme_service); |
| |
| scoped_refptr<Extension> theme = CreateExtension( |
| u"example1", temp_dir.GetPath(), ManifestLocation::kUnpacked, |
| extensions::Manifest::TYPE_THEME, false); |
| service_->FinishInstallationForTest(theme.get()); |
| waiter.WaitForThemeChanged(); |
| |
| EXPECT_FALSE(theme_service->UsingDefaultTheme()); |
| |
| scoped_refptr<Extension> ext2 = CreateExtension( |
| u"example2", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext2.get()); |
| // Component extensions and policy-managed extensions shouldn't be disabled. |
| scoped_refptr<Extension> ext3 = CreateExtension( |
| u"example3", base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), |
| ManifestLocation::kComponent, extensions::Manifest::TYPE_EXTENSION, |
| false); |
| registrar()->AddExtension(ext3.get()); |
| scoped_refptr<Extension> ext4 = CreateExtension( |
| u"example4", base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), |
| ManifestLocation::kExternalPolicyDownload, |
| extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext4.get()); |
| scoped_refptr<Extension> ext5 = CreateExtension( |
| u"example5", base::FilePath(FILE_PATH_LITERAL("//nonexistent4")), |
| ManifestLocation::kExternalComponent, |
| extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext5.get()); |
| scoped_refptr<Extension> ext6 = CreateExtension( |
| u"example6", base::FilePath(FILE_PATH_LITERAL("//nonexistent5")), |
| ManifestLocation::kExternalPolicy, extensions::Manifest::TYPE_EXTENSION, |
| false); |
| registrar()->AddExtension(ext6.get()); |
| EXPECT_EQ(6u, registry()->enabled_extensions().size()); |
| |
| ResetAndWait(ProfileResetter::EXTENSIONS); |
| EXPECT_EQ(4u, registry()->enabled_extensions().size()); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(theme->id())); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(ext2->id())); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext3->id())); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext4->id())); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext5->id())); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext6->id())); |
| EXPECT_TRUE(theme_service->UsingDefaultTheme()); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) { |
| scoped_refptr<Extension> ext2 = CreateExtension( |
| u"example2", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext2.get()); |
| // Components and external policy extensions shouldn't be deleted. |
| scoped_refptr<Extension> ext3 = CreateExtension( |
| u"example3", base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext3.get()); |
| EXPECT_EQ(2u, registry()->enabled_extensions().size()); |
| |
| std::string master_prefs(kDistributionConfig); |
| ReplaceString(&master_prefs, "placeholder_for_id", ext3->id()); |
| |
| ResetAndWait(ProfileResetter::EXTENSIONS, master_prefs); |
| |
| EXPECT_EQ(1u, registry()->enabled_extensions().size()); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext3->id())); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) { |
| service_->Init(); |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile()); |
| test::ThemeServiceChangedWaiter waiter(theme_service); |
| |
| scoped_refptr<Extension> ext1 = CreateExtension( |
| u"example1", temp_dir.GetPath(), ManifestLocation::kUnpacked, |
| extensions::Manifest::TYPE_THEME, false); |
| service_->FinishInstallationForTest(ext1.get()); |
| waiter.WaitForThemeChanged(); |
| |
| EXPECT_FALSE(theme_service->UsingDefaultTheme()); |
| |
| scoped_refptr<Extension> ext2 = CreateExtension( |
| u"example2", base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext2.get()); |
| |
| scoped_refptr<Extension> ext3 = CreateExtension( |
| u"example2", base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_HOSTED_APP, true); |
| registrar()->AddExtension(ext3.get()); |
| EXPECT_EQ(3u, registry()->enabled_extensions().size()); |
| |
| ResetAndWait(ProfileResetter::EXTENSIONS); |
| |
| EXPECT_EQ(1u, registry()->enabled_extensions().size()); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(ext1->id())); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(ext2->id())); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext3->id())); |
| EXPECT_TRUE(theme_service->UsingDefaultTheme()); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetExtensionsByReenablingExternalComponents) { |
| service_->Init(); |
| |
| base::ScopedTempDir temp_dir; |
| ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); |
| |
| scoped_refptr<Extension> ext = CreateExtension( |
| u"example", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kExternalComponent, |
| extensions::Manifest::TYPE_EXTENSION, false); |
| registrar()->AddExtension(ext.get()); |
| |
| registrar()->DisableExtension( |
| ext->id(), {extensions::disable_reason::DISABLE_USER_ACTION}); |
| EXPECT_FALSE(registry()->enabled_extensions().Contains(ext->id())); |
| EXPECT_TRUE(registry()->disabled_extensions().Contains(ext->id())); |
| |
| ResetAndWait(ProfileResetter::EXTENSIONS); |
| EXPECT_TRUE(registry()->enabled_extensions().Contains(ext->id())); |
| EXPECT_FALSE(registry()->disabled_extensions().Contains(ext->id())); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) { |
| PrefService* prefs = profile()->GetPrefs(); |
| DCHECK(prefs); |
| |
| SessionStartupPref startup_pref(SessionStartupPref::LAST); |
| SessionStartupPref::SetStartupPref(prefs, startup_pref); |
| |
| ResetAndWait(ProfileResetter::STARTUP_PAGES, kDistributionConfig); |
| |
| startup_pref = SessionStartupPref::GetStartupPref(prefs); |
| EXPECT_EQ(SessionStartupPref::URLS, startup_pref.type); |
| const GURL urls[] = {GURL("http://goo.gl"), GURL("http://foo.de")}; |
| EXPECT_EQ(std::vector<GURL>(std::begin(urls), std::end(urls)), |
| startup_pref.urls); |
| } |
| |
| |
| TEST_F(ProfileResetterTest, ResetStartPagePartially) { |
| PrefService* prefs = profile()->GetPrefs(); |
| DCHECK(prefs); |
| |
| const GURL urls[] = {GURL("http://foo"), GURL("http://bar")}; |
| SessionStartupPref startup_pref(SessionStartupPref::URLS); |
| startup_pref.urls.assign(std::begin(urls), std::end(urls)); |
| SessionStartupPref::SetStartupPref(prefs, startup_pref); |
| |
| ResetAndWait(ProfileResetter::STARTUP_PAGES, std::string()); |
| |
| startup_pref = SessionStartupPref::GetStartupPref(prefs); |
| EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), startup_pref.type); |
| EXPECT_EQ(std::vector<GURL>(std::begin(urls), std::end(urls)), |
| startup_pref.urls); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetShortcuts) { |
| ShortcutHandler shortcut; |
| ShortcutCommand command_line = shortcut.CreateWithArguments( |
| L"chrome.lnk", L"--profile-directory=Default foo.com"); |
| shortcut.HideFile(); |
| shortcut.CheckShortcutHasArguments(L"--profile-directory=Default foo.com"); |
| #if BUILDFLAG(IS_WIN) |
| ASSERT_TRUE(shortcut.IsFileHidden()); |
| #endif |
| |
| ResetAndWait(ProfileResetter::SHORTCUTS); |
| |
| shortcut.CheckShortcutHasArguments(L"--profile-directory=Default"); |
| EXPECT_FALSE(shortcut.IsFileHidden()); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetFewFlags) { |
| // mock_object_ is a StrictMock, so we verify that it is called only once. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | |
| ProfileResetter::HOMEPAGE | |
| ProfileResetter::CONTENT_SETTINGS); |
| } |
| |
| // Tries to load unavailable config file. |
| TEST_F(ConfigParserTest, NoConnectivity) { |
| const GURL url("http://test"); |
| test_url_loader_factory().AddResponse( |
| url, network::mojom::URLResponseHead::New(), "", |
| network::URLLoaderCompletionStatus(net::HTTP_INTERNAL_SERVER_ERROR)); |
| |
| std::unique_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(url); |
| EXPECT_FALSE(fetcher->GetSettings()); |
| } |
| |
| // Tries to load available config file. |
| TEST_F(ConfigParserTest, ParseConfig) { |
| const GURL url("http://test"); |
| std::string xml_config(kXmlConfig); |
| ReplaceString(&xml_config, "placeholder_for_data", kDistributionConfig); |
| ReplaceString(&xml_config, |
| "placeholder_for_id", |
| "abbaabbaabbaabbaabbaabbaabbaabba"); |
| auto head = network::mojom::URLResponseHead::New(); |
| std::string headers("HTTP/1.1 200 OK\nContent-type: text/xml\n\n"); |
| head->headers = base::MakeRefCounted<net::HttpResponseHeaders>( |
| net::HttpUtil::AssembleRawHeaders(headers)); |
| head->mime_type = "text/xml"; |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = xml_config.size(); |
| test_url_loader_factory().AddResponse(url, std::move(head), xml_config, |
| status); |
| |
| std::unique_ptr<BrandcodeConfigFetcher> fetcher = WaitForRequest(url); |
| std::unique_ptr<BrandcodedDefaultSettings> settings = fetcher->GetSettings(); |
| ASSERT_TRUE(settings); |
| |
| std::vector<std::string> extension_ids; |
| EXPECT_TRUE(settings->GetExtensions(&extension_ids)); |
| EXPECT_EQ(1u, extension_ids.size()); |
| EXPECT_EQ("abbaabbaabbaabbaabbaabbaabbaabba", extension_ids[0]); |
| |
| std::string homepage; |
| EXPECT_TRUE(settings->GetHomepage(&homepage)); |
| EXPECT_EQ("http://www.foo.com", homepage); |
| |
| std::optional<base::Value::List> startup_list( |
| settings->GetUrlsToRestoreOnStartup()); |
| EXPECT_TRUE(startup_list.has_value()); |
| std::vector<std::string> startup_pages; |
| for (const auto& entry : *startup_list) { |
| ASSERT_TRUE(entry.is_string()); |
| startup_pages.push_back(entry.GetString()); |
| } |
| ASSERT_EQ(2u, startup_pages.size()); |
| EXPECT_EQ("http://goo.gl", startup_pages[0]); |
| EXPECT_EQ("http://foo.de", startup_pages[1]); |
| } |
| |
| // Return an invalid response from the fetch request and delete the |
| // Fetcher object in the callback, which mimics how ResetSettingsHandler uses |
| // the class. See https://crbug.com/1491296. |
| TEST_F(ConfigParserTest, InvalidResponseDeleteFromCallback) { |
| const GURL url("http://test"); |
| auto head = network::mojom::URLResponseHead::New(); |
| head->headers = base::MakeRefCounted<net::HttpResponseHeaders>( |
| net::HttpUtil::AssembleRawHeaders( |
| "HTTP/1.1 200 OK\nContent-type: application/custom\n\n")); |
| head->mime_type = "application/custom"; |
| test_url_loader_factory().AddResponse(url, std::move(head), |
| "Custom app data, not XML", |
| network::URLLoaderCompletionStatus()); |
| |
| base::RunLoop run_loop; |
| std::unique_ptr<BrandcodeConfigFetcher> fetcher; |
| auto callback = base::BindLambdaForTesting([&fetcher, &run_loop] { |
| EXPECT_FALSE(fetcher->GetSettings()); |
| fetcher.reset(); |
| run_loop.Quit(); |
| }); |
| fetcher = std::make_unique<BrandcodeConfigFetcher>(&test_url_loader_factory(), |
| std::move(callback), url, "ABCD"); |
| run_loop.Run(); |
| } |
| |
| TEST_F(ProfileResetterTest, CheckSnapshots) { |
| ResettableSettingsSnapshot empty_snap(profile()); |
| EXPECT_EQ(0, empty_snap.FindDifferentFields(empty_snap)); |
| |
| scoped_refptr<Extension> ext = CreateExtension( |
| u"example", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| ASSERT_TRUE(ext.get()); |
| registrar()->AddExtension(ext.get()); |
| |
| std::string master_prefs(kDistributionConfig); |
| std::string ext_id = ext->id(); |
| ReplaceString(&master_prefs, "placeholder_for_id", ext_id); |
| |
| // Reset to non organic defaults. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | |
| ProfileResetter::HOMEPAGE | |
| ProfileResetter::STARTUP_PAGES, |
| master_prefs); |
| ShortcutHandler shortcut_hijacked; |
| ShortcutCommand command_line = shortcut_hijacked.CreateWithArguments( |
| L"chrome1.lnk", L"--profile-directory=Default foo.com"); |
| shortcut_hijacked.CheckShortcutHasArguments( |
| L"--profile-directory=Default foo.com"); |
| ShortcutHandler shortcut_ok; |
| shortcut_ok.CreateWithArguments(L"chrome2.lnk", |
| L"--profile-directory=Default1"); |
| |
| ResettableSettingsSnapshot nonorganic_snap(profile()); |
| nonorganic_snap.RequestShortcuts(base::OnceClosure()); |
| // Let it enumerate shortcuts on a blockable task runner. |
| content::RunAllTasksUntilIdle(); |
| int diff_fields = ResettableSettingsSnapshot::ALL_FIELDS; |
| if (!ShortcutHandler::IsSupported()) |
| diff_fields &= ~ResettableSettingsSnapshot::SHORTCUTS; |
| EXPECT_EQ(diff_fields, |
| empty_snap.FindDifferentFields(nonorganic_snap)); |
| empty_snap.Subtract(nonorganic_snap); |
| EXPECT_TRUE(empty_snap.startup_urls().empty()); |
| EXPECT_EQ(SessionStartupPref::GetDefaultStartupType(), |
| empty_snap.startup_type()); |
| EXPECT_TRUE(empty_snap.homepage().empty()); |
| EXPECT_TRUE(empty_snap.homepage_is_ntp()); |
| EXPECT_FALSE(empty_snap.show_home_button()); |
| EXPECT_NE(std::string::npos, empty_snap.dse_url().find("{google:baseURL}")); |
| EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList(), |
| empty_snap.enabled_extensions()); |
| EXPECT_EQ(std::vector<ShortcutCommand>(), empty_snap.shortcuts()); |
| |
| // Reset to organic defaults. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | |
| ProfileResetter::HOMEPAGE | |
| ProfileResetter::STARTUP_PAGES | |
| ProfileResetter::EXTENSIONS | |
| ProfileResetter::SHORTCUTS); |
| |
| ResettableSettingsSnapshot organic_snap(profile()); |
| organic_snap.RequestShortcuts(base::OnceClosure()); |
| // Let it enumerate shortcuts on a blockable task runner. |
| content::RunAllTasksUntilIdle(); |
| EXPECT_EQ(diff_fields, nonorganic_snap.FindDifferentFields(organic_snap)); |
| nonorganic_snap.Subtract(organic_snap); |
| const GURL urls[] = {GURL("http://foo.de"), GURL("http://goo.gl")}; |
| EXPECT_EQ(std::vector<GURL>(std::begin(urls), std::end(urls)), |
| nonorganic_snap.startup_urls()); |
| EXPECT_EQ(SessionStartupPref::URLS, nonorganic_snap.startup_type()); |
| EXPECT_EQ("http://www.foo.com", nonorganic_snap.homepage()); |
| EXPECT_FALSE(nonorganic_snap.homepage_is_ntp()); |
| EXPECT_TRUE(nonorganic_snap.show_home_button()); |
| EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", nonorganic_snap.dse_url()); |
| EXPECT_EQ(ResettableSettingsSnapshot::ExtensionList( |
| 1, std::make_pair(ext_id, "example")), |
| nonorganic_snap.enabled_extensions()); |
| if (ShortcutHandler::IsSupported()) { |
| std::vector<ShortcutCommand> shortcuts = nonorganic_snap.shortcuts(); |
| ASSERT_EQ(1u, shortcuts.size()); |
| EXPECT_EQ(command_line.first.value(), shortcuts[0].first.value()); |
| EXPECT_EQ(command_line.second, shortcuts[0].second); |
| } |
| } |
| |
| TEST_F(ProfileResetterTest, FeedbackSerializationAsProtoTest) { |
| // Reset to non organic defaults. |
| ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | |
| ProfileResetter::HOMEPAGE | |
| ProfileResetter::STARTUP_PAGES, |
| kDistributionConfig); |
| |
| scoped_refptr<Extension> ext = CreateExtension( |
| u"example", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| ASSERT_TRUE(ext.get()); |
| registrar()->AddExtension(ext.get()); |
| |
| ShortcutHandler shortcut; |
| ShortcutCommand command_line = shortcut.CreateWithArguments( |
| L"chrome.lnk", L"--profile-directory=Default foo.com"); |
| |
| ResettableSettingsSnapshot nonorganic_snap(profile()); |
| nonorganic_snap.RequestShortcuts(base::OnceClosure()); |
| // Let it enumerate shortcuts on a blockable task runner. |
| content::RunAllTasksUntilIdle(); |
| |
| static_assert(ResettableSettingsSnapshot::ALL_FIELDS == 31, |
| "this test needs to be expanded"); |
| for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS; |
| ++field_mask) { |
| std::unique_ptr<reset_report::ChromeResetReport> report = |
| SerializeSettingsReportToProto(nonorganic_snap, field_mask); |
| |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE), |
| report->startup_url_path_size() > 0); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE), |
| report->has_startup_type()); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE), |
| report->has_homepage_path()); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE), |
| report->has_homepage_is_new_tab_page()); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::HOMEPAGE), |
| report->has_show_home_button()); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::DSE_URL), |
| report->has_default_search_engine_path()); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::EXTENSIONS), |
| report->enabled_extensions_size() > 0); |
| EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::SHORTCUTS) && |
| ShortcutHandler::IsSupported(), |
| report->shortcuts_size() > 0); |
| } |
| } |
| |
| struct FeedbackCapture { |
| void SetFeedback(Profile* profile, |
| const ResettableSettingsSnapshot& snapshot) { |
| list_ = GetReadableFeedbackForSnapshot(profile, snapshot); |
| OnUpdatedList(); |
| } |
| |
| void Fail() { |
| ADD_FAILURE() << "This method shouldn't be called."; |
| } |
| |
| MOCK_METHOD0(OnUpdatedList, void(void)); |
| |
| base::Value::List list_; |
| }; |
| |
| // Make sure GetReadableFeedback handles non-ascii letters. |
| TEST_F(ProfileResetterTest, GetReadableFeedback) { |
| scoped_refptr<Extension> ext = CreateExtension( |
| u"Tiësto", base::FilePath(FILE_PATH_LITERAL("//nonexistent")), |
| ManifestLocation::kUnpacked, extensions::Manifest::TYPE_EXTENSION, false); |
| ASSERT_TRUE(ext.get()); |
| registrar()->AddExtension(ext.get()); |
| |
| PrefService* prefs = profile()->GetPrefs(); |
| DCHECK(prefs); |
| // The URL is "http://россия.рф". |
| std::wstring url(L"http://" |
| L"\u0440\u043e\u0441\u0441\u0438\u044f.\u0440\u0444"); |
| prefs->SetBoolean(prefs::kHomePageIsNewTabPage, false); |
| prefs->SetString(prefs::kHomePage, base::WideToUTF8(url)); |
| |
| SessionStartupPref startup_pref(SessionStartupPref::URLS); |
| startup_pref.urls.push_back(GURL(base::WideToUTF8(url))); |
| SessionStartupPref::SetStartupPref(prefs, startup_pref); |
| |
| ShortcutHandler shortcut; |
| ShortcutCommand command_line = shortcut.CreateWithArguments( |
| L"chrome.lnk", L"--profile-directory=Default foo.com"); |
| |
| FeedbackCapture capture; |
| EXPECT_CALL(capture, OnUpdatedList()); |
| ResettableSettingsSnapshot snapshot(profile()); |
| snapshot.RequestShortcuts(base::BindOnce(&FeedbackCapture::SetFeedback, |
| base::Unretained(&capture), |
| profile(), std::cref(snapshot))); |
| // Let it enumerate shortcuts on a blockable task runner. |
| content::RunAllTasksUntilIdle(); |
| EXPECT_TRUE(snapshot.shortcuts_determined()); |
| ::testing::Mock::VerifyAndClearExpectations(&capture); |
| // The homepage and the startup page are in punycode. They are unreadable. |
| // Trying to find the extension name. |
| base::Value::List list = std::move(capture.list_); |
| bool checked_extensions = false; |
| bool checked_shortcuts = false; |
| for (const auto& entry : list) { |
| const base::Value::Dict* dict = entry.GetIfDict(); |
| ASSERT_TRUE(dict); |
| const std::string* value = dict->FindString("key"); |
| ASSERT_TRUE(value); |
| if (*value == "Extensions") { |
| const std::string* extensions = dict->FindString("value"); |
| ASSERT_TRUE(extensions); |
| EXPECT_EQ(*extensions, "Tiësto"); |
| checked_extensions = true; |
| } else if (*value == "Shortcut targets") { |
| const std::string* targets = dict->FindString("value"); |
| ASSERT_TRUE(targets); |
| EXPECT_NE(std::string::npos, targets->find("foo.com")) << *targets; |
| checked_shortcuts = true; |
| } |
| } |
| EXPECT_TRUE(checked_extensions); |
| EXPECT_EQ(ShortcutHandler::IsSupported(), checked_shortcuts); |
| } |
| |
| TEST_F(ProfileResetterTest, DestroySnapshotFast) { |
| FeedbackCapture capture; |
| std::unique_ptr<ResettableSettingsSnapshot> deleted_snapshot( |
| new ResettableSettingsSnapshot(profile())); |
| deleted_snapshot->RequestShortcuts( |
| base::BindOnce(&FeedbackCapture::Fail, base::Unretained(&capture))); |
| deleted_snapshot.reset(); |
| // Running remaining tasks shouldn't trigger the callback to be called as |
| // |deleted_snapshot| was deleted before it could run. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ProfileResetterTest, ResetNTPCustomizationsTest) { |
| auto* ntp_custom_background_service = |
| NtpCustomBackgroundServiceFactory::GetForProfile(profile()); |
| ntp_custom_background_service->AddValidBackdropUrlForTesting( |
| GURL("https://background.com")); |
| ntp_custom_background_service->SetCustomBackgroundInfo( |
| /*background_url=*/GURL("https://background.com"), |
| /*thumbnail_url=*/GURL("https://thumbnail.com"), |
| /*attribution_line_1=*/"line 1", |
| /*attribution_line_2=*/"line 2", |
| /*action_url=*/GURL("https://action.com"), |
| /*collection_id=*/""); |
| EXPECT_TRUE(ntp_custom_background_service->GetCustomBackground().has_value()); |
| ResetAndWait(ProfileResetter::NTP_CUSTOMIZATIONS); |
| EXPECT_FALSE( |
| ntp_custom_background_service->GetCustomBackground().has_value()); |
| } |
| |
| } // namespace |