diff options
author | vasilii@chromium.org <vasilii@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-14 14:54:09 +0000 |
---|---|---|
committer | vasilii@chromium.org <vasilii@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-01-14 14:54:09 +0000 |
commit | 73ba82b5e91eaca97fbd517817e2fe2c87bc97dc (patch) | |
tree | 543c5da9c7066f91a35e7400b72e29bcf24dbc49 | |
parent | 867a32486223a69c28f0614ee9c834db43ea5c81 (diff) | |
download | chromium_src-73ba82b5e91eaca97fbd517817e2fe2c87bc97dc.zip chromium_src-73ba82b5e91eaca97fbd517817e2fe2c87bc97dc.tar.gz chromium_src-73ba82b5e91eaca97fbd517817e2fe2c87bc97dc.tar.bz2 |
Manual profile reset: reset shortcuts on Windows.
BUG=324931
Review URL: https://codereview.chromium.org/116143003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244708 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 800 insertions, 175 deletions
diff --git a/chrome/browser/extensions/extension_service_unittest.h b/chrome/browser/extensions/extension_service_unittest.h index 550a2f7..64d9e0d 100644 --- a/chrome/browser/extensions/extension_service_unittest.h +++ b/chrome/browser/extensions/extension_service_unittest.h @@ -82,11 +82,13 @@ class ExtensionServiceTestBase : public testing::Test { static ExtensionServiceInitParams CreateDefaultInitParamsInTempDir( base::ScopedTempDir* temp_dir); + // Destroy temp_dir_ after thread_bundle_ so clean-up tasks can still use the + // directory. + base::ScopedTempDir temp_dir_; // Destroying at_exit_manager_ will delete all LazyInstances, so it must come // after thread_bundle_ in the destruction order. base::ShadowingAtExitManager at_exit_manager_; content::TestBrowserThreadBundle thread_bundle_; - base::ScopedTempDir temp_dir_; scoped_ptr<TestingProfile> profile_; base::FilePath extensions_install_dir_; base::FilePath data_dir_; diff --git a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc index 63e4920..a51944d 100644 --- a/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc +++ b/chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc @@ -331,7 +331,7 @@ void AutomaticProfileResetterDelegateImpl::RunProfileSettingsReset( const base::Closure& completion) { DCHECK(brandcoded_defaults_); scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot( - send_feedback ? new ResettableSettingsSnapshot(profile_) : NULL); + send_feedback ? new ResettableSettingsSnapshot(profile_, true) : NULL); profile_resetter_->Reset( resettable_aspects_, brandcoded_defaults_.Pass(), @@ -358,7 +358,7 @@ void AutomaticProfileResetterDelegateImpl::OnProfileSettingsResetCompleted( scoped_ptr<ResettableSettingsSnapshot> old_settings_snapshot) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); if (old_settings_snapshot) { - ResettableSettingsSnapshot new_settings_snapshot(profile_); + ResettableSettingsSnapshot new_settings_snapshot(profile_, false); int difference = old_settings_snapshot->FindDifferentFields(new_settings_snapshot); if (difference) { diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc index a0fc142..c33b1a3 100644 --- a/chrome/browser/profile_resetter/profile_resetter.cc +++ b/chrome/browser/profile_resetter/profile_resetter.cc @@ -20,14 +20,47 @@ #include "chrome/browser/ui/browser_iterator.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/pref_names.h" +#include "chrome/installer/util/browser_distribution.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/management_policy.h" +#if defined(OS_WIN) +#include "base/base_paths.h" +#include "base/path_service.h" +#include "chrome/installer/util/shell_util.h" +#include "content/public/browser/browser_thread.h" + +namespace { + +void ResetShortcutsOnFileThread() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + // Get full path of chrome. + base::FilePath chrome_exe; + if (!PathService::Get(base::FILE_EXE, &chrome_exe)) + return; + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST; + location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) { + ShellUtil::ShortcutListMaybeRemoveUnknownArgs( + static_cast<ShellUtil::ShortcutLocation>(location), + dist, + ShellUtil::CURRENT_USER, + chrome_exe, + true, + NULL); + } +} + +} // namespace +#endif // defined(OS_WIN) + ProfileResetter::ProfileResetter(Profile* profile) : profile_(profile), template_url_service_(TemplateURLServiceFactory::GetForProfile(profile_)), pending_reset_flags_(0), - cookies_remover_(NULL) { + cookies_remover_(NULL), + weak_ptr_factory_(this) { DCHECK(CalledOnValidThread()); DCHECK(profile_); } @@ -73,6 +106,7 @@ void ProfileResetter::Reset( { EXTENSIONS, &ProfileResetter::ResetExtensions }, { STARTUP_PAGES, &ProfileResetter::ResetStartupPages }, { PINNED_TABS, &ProfileResetter::ResetPinnedTabs }, + { SHORTCUTS, &ProfileResetter::ResetShortcuts }, }; ResettableFlags reset_triggered_for_flags = 0; @@ -141,7 +175,7 @@ void ProfileResetter::ResetDefaultSearchEngine() { template_url_service_sub_ = template_url_service_->RegisterOnLoadedCallback( base::Bind(&ProfileResetter::OnTemplateURLServiceLoaded, - base::Unretained(this))); + weak_ptr_factory_.GetWeakPtr())); template_url_service_->Load(); } } @@ -250,6 +284,20 @@ void ProfileResetter::ResetPinnedTabs() { MarkAsDone(PINNED_TABS); } +void ProfileResetter::ResetShortcuts() { +#if defined(OS_WIN) + content::BrowserThread::PostTaskAndReply( + content::BrowserThread::FILE, + FROM_HERE, + base::Bind(&ResetShortcutsOnFileThread), + base::Bind(&ProfileResetter::MarkAsDone, + weak_ptr_factory_.GetWeakPtr(), + SHORTCUTS)); +#else + MarkAsDone(SHORTCUTS); +#endif +} + void ProfileResetter::OnTemplateURLServiceLoaded() { // TemplateURLService has loaded. If we need to clean search engines, it's // time to go on. @@ -263,3 +311,29 @@ void ProfileResetter::OnBrowsingDataRemoverDone() { cookies_remover_ = NULL; MarkAsDone(COOKIES_AND_SITE_DATA); } + +std::vector<ShortcutCommand> GetChromeLaunchShortcuts() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); +#if defined(OS_WIN) + // Get full path of chrome. + base::FilePath chrome_exe; + if (!PathService::Get(base::FILE_EXE, &chrome_exe)) + return std::vector<ShortcutCommand>(); + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + std::vector<ShortcutCommand> shortcuts; + for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST; + location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) { + ShellUtil::ShortcutListMaybeRemoveUnknownArgs( + static_cast<ShellUtil::ShortcutLocation>(location), + dist, + ShellUtil::CURRENT_USER, + chrome_exe, + false, + &shortcuts); + } + return shortcuts; +#else + return std::vector<ShortcutCommand>(); +#endif +} diff --git a/chrome/browser/profile_resetter/profile_resetter.h b/chrome/browser/profile_resetter/profile_resetter.h index 44170a8..5a49a43 100644 --- a/chrome/browser/profile_resetter/profile_resetter.h +++ b/chrome/browser/profile_resetter/profile_resetter.h @@ -5,8 +5,15 @@ #ifndef CHROME_BROWSER_PROFILE_RESETTER_PROFILE_RESETTER_H_ #define CHROME_BROWSER_PROFILE_RESETTER_PROFILE_RESETTER_H_ +#include <utility> +#include <vector> + #include "base/basictypes.h" #include "base/callback.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" #include "base/threading/non_thread_safe.h" #include "chrome/browser/browsing_data/browsing_data_remover.h" #include "chrome/browser/profile_resetter/brandcoded_default_settings.h" @@ -29,10 +36,12 @@ class ProfileResetter : public base::NonThreadSafe, EXTENSIONS = 1 << 4, STARTUP_PAGES = 1 << 5, PINNED_TABS = 1 << 6, + SHORTCUTS = 1 << 7, // Update ALL if you add new values and check whether the type of // ResettableFlags needs to be enlarged. ALL = DEFAULT_SEARCH_ENGINE | HOMEPAGE | CONTENT_SETTINGS | - COOKIES_AND_SITE_DATA | EXTENSIONS | STARTUP_PAGES | PINNED_TABS + COOKIES_AND_SITE_DATA | EXTENSIONS | STARTUP_PAGES | PINNED_TABS | + SHORTCUTS }; // Bit vector for Resettable enum. @@ -65,6 +74,7 @@ class ProfileResetter : public base::NonThreadSafe, void ResetExtensions(); void ResetStartupPages(); void ResetPinnedTabs(); + void ResetShortcuts(); // BrowsingDataRemover::Observer: virtual void OnBrowsingDataRemoverDone() OVERRIDE; @@ -89,7 +99,17 @@ class ProfileResetter : public base::NonThreadSafe, scoped_ptr<TemplateURLService::Subscription> template_url_service_sub_; + base::WeakPtrFactory<ProfileResetter> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ProfileResetter); }; +// Path to shortcut and command line arguments. +typedef std::pair<base::FilePath, base::string16> ShortcutCommand; + +// On Windows returns all the shortcuts which launch Chrome and corresponding +// arguments. +// Call on FILE thread. +std::vector<ShortcutCommand> GetChromeLaunchShortcuts(); + #endif // CHROME_BROWSER_PROFILE_RESETTER_PROFILE_RESETTER_H_ diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc index 08f204a..f976ec6 100644 --- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc +++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc @@ -7,6 +7,7 @@ #include "base/json/json_string_value_serializer.h" #include "base/prefs/pref_service.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_path_override.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/extensions/extension_service_unittest.h" #include "chrome/browser/extensions/tab_helper.h" @@ -33,7 +34,16 @@ #include "net/url_request/url_request_status.h" #include "url/gurl.h" -using base::ASCIIToUTF16; +#if defined(OS_WIN) +#include "base/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 + namespace { @@ -85,6 +95,10 @@ using extensions::Manifest; // ProfileResetterTest sets up the extension, WebData and TemplateURL services. class ProfileResetterTest : public ExtensionServiceTestBase, public ProfileResetterTestBase { + public: + ProfileResetterTest(); + virtual ~ProfileResetterTest(); + protected: virtual void SetUp() OVERRIDE; @@ -92,8 +106,29 @@ class ProfileResetterTest : public ExtensionServiceTestBase, static BrowserContextKeyedService* CreateTemplateURLService( content::BrowserContext* context); + + private: +#if defined(OS_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 defined(OS_WIN) + : user_desktop_override_(base::DIR_USER_DESKTOP), + app_dir_override_(base::DIR_APP_DATA), + start_menu_override_(base::DIR_START_MENU), + taskbar_pins_override_(base::DIR_TASKBAR_PINS) +#endif +{} + +ProfileResetterTest::~ProfileResetterTest() { +} + void ProfileResetterTest::SetUp() { ExtensionServiceTestBase::SetUp(); InitializeEmptyExtensionService(); @@ -230,6 +265,99 @@ scoped_ptr<net::FakeURLFetcher> ConfigParserTest::CreateFakeURLFetcher( return fetcher.Pass(); } +// A helper class to create/delete/check a Chrome desktop shortcut on Windows. +class ShortcutHandler { + public: + ShortcutHandler(); + ~ShortcutHandler(); + + static bool IsSupported(); + ShortcutCommand CreateWithArguments(const base::string16& name, + const base::string16& args); + void CheckShortcutHasArguments(const base::string16& desired_args) const; + void Delete(); + + private: +#if defined(OS_WIN) + base::FilePath shortcut_path_; +#endif + DISALLOW_COPY_AND_ASSIGN(ShortcutHandler); +}; + +#if defined(OS_WIN) +ShortcutHandler::ShortcutHandler() { +} + +ShortcutHandler::~ShortcutHandler() { + if (!shortcut_path_.empty()) + Delete(); +} + +// static +bool ShortcutHandler::IsSupported() { + return true; +} + +ShortcutCommand ShortcutHandler::CreateWithArguments( + const base::string16& name, + const base::string16& args) { + EXPECT_TRUE(shortcut_path_.empty()); + base::FilePath path_to_create; + EXPECT_TRUE(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(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::SHORTCUT_CREATE_ALWAYS)) << path_to_create.value(); + shortcut_path_ = path_to_create; + return ShortcutCommand(shortcut_path_, + args); +} + +void ShortcutHandler::CheckShortcutHasArguments( + const base::string16& desired_args) const { + EXPECT_FALSE(shortcut_path_.empty()); + base::FilePath target_path; + base::string16 args; + EXPECT_TRUE(base::win::ResolveShortcut(shortcut_path_, &target_path, &args)); + EXPECT_EQ(desired_args, args); +} + +void ShortcutHandler::Delete() { + EXPECT_FALSE(shortcut_path_.empty()); + EXPECT_TRUE(base::DeleteFile(shortcut_path_, false)); + shortcut_path_.clear(); +} +#else +ShortcutHandler::ShortcutHandler() {} + +ShortcutHandler::~ShortcutHandler() {} + +// static +bool ShortcutHandler::IsSupported() { + return false; +} + +ShortcutCommand ShortcutHandler::CreateWithArguments( + const base::string16& name, + const base::string16& args) { + return ShortcutCommand(); +} + +void ShortcutHandler::CheckShortcutHasArguments( + const base::string16& desired_args) const { +} + +void ShortcutHandler::Delete() { +} +#endif // defined(OS_WIN) + // helper functions ----------------------------------------------------------- @@ -299,8 +427,8 @@ TEST_F(ProfileResetterTest, ResetDefaultSearchEngineNonOrganic) { TemplateURLServiceFactory::GetForProfile(profile()); TemplateURL* default_engine = model->GetDefaultSearchProvider(); ASSERT_NE(static_cast<TemplateURL*>(NULL), default_engine); - EXPECT_EQ(ASCIIToUTF16("first"), default_engine->short_name()); - EXPECT_EQ(ASCIIToUTF16("firstkey"), default_engine->keyword()); + EXPECT_EQ(base::ASCIIToUTF16("first"), default_engine->short_name()); + EXPECT_EQ(base::ASCIIToUTF16("firstkey"), default_engine->keyword()); EXPECT_EQ("http://www.foo.com/s?q={searchTerms}", default_engine->url()); EXPECT_EQ("", prefs->GetString(prefs::kLastPromptedGoogleURL)); @@ -457,7 +585,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) { ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); scoped_refptr<Extension> theme = - CreateExtension(ASCIIToUTF16("example1"), + CreateExtension(base::ASCIIToUTF16("example1"), temp_dir.path(), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_THEME, @@ -471,7 +599,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) { EXPECT_FALSE(theme_service->UsingDefaultTheme()); scoped_refptr<Extension> ext2 = CreateExtension( - ASCIIToUTF16("example2"), + base::ASCIIToUTF16("example2"), base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -479,28 +607,28 @@ TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) { service_->AddExtension(ext2.get()); // Component extensions and policy-managed extensions shouldn't be disabled. scoped_refptr<Extension> ext3 = CreateExtension( - ASCIIToUTF16("example3"), + base::ASCIIToUTF16("example3"), base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), Manifest::COMPONENT, extensions::Manifest::TYPE_EXTENSION, false); service_->AddExtension(ext3.get()); - scoped_refptr<Extension> ext4 = CreateExtension( - ASCIIToUTF16("example4"), - base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), - Manifest::EXTERNAL_POLICY_DOWNLOAD, - extensions::Manifest::TYPE_EXTENSION, - false); + scoped_refptr<Extension> ext4 = + CreateExtension(base::ASCIIToUTF16("example4"), + base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), + Manifest::EXTERNAL_POLICY_DOWNLOAD, + extensions::Manifest::TYPE_EXTENSION, + false); service_->AddExtension(ext4.get()); scoped_refptr<Extension> ext5 = CreateExtension( - ASCIIToUTF16("example5"), + base::ASCIIToUTF16("example5"), base::FilePath(FILE_PATH_LITERAL("//nonexistent4")), Manifest::EXTERNAL_COMPONENT, extensions::Manifest::TYPE_EXTENSION, false); service_->AddExtension(ext5.get()); scoped_refptr<Extension> ext6 = CreateExtension( - ASCIIToUTF16("example6"), + base::ASCIIToUTF16("example6"), base::FilePath(FILE_PATH_LITERAL("//nonexistent5")), Manifest::EXTERNAL_POLICY, extensions::Manifest::TYPE_EXTENSION, @@ -521,7 +649,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsByDisabling) { TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) { scoped_refptr<Extension> ext2 = CreateExtension( - ASCIIToUTF16("example2"), + base::ASCIIToUTF16("example2"), base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -529,7 +657,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsByDisablingNonOrganic) { service_->AddExtension(ext2.get()); // Components and external policy extensions shouldn't be deleted. scoped_refptr<Extension> ext3 = CreateExtension( - ASCIIToUTF16("example3"), + base::ASCIIToUTF16("example3"), base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -553,7 +681,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) { ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); scoped_refptr<Extension> ext1 = - CreateExtension(ASCIIToUTF16("example1"), + CreateExtension(base::ASCIIToUTF16("example1"), temp_dir.path(), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_THEME, @@ -567,7 +695,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) { EXPECT_FALSE(theme_service->UsingDefaultTheme()); scoped_refptr<Extension> ext2 = - CreateExtension(ASCIIToUTF16("example2"), + CreateExtension(base::ASCIIToUTF16("example2"), base::FilePath(FILE_PATH_LITERAL("//nonexistent2")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -575,7 +703,7 @@ TEST_F(ProfileResetterTest, ResetExtensionsAndDefaultApps) { service_->AddExtension(ext2.get()); scoped_refptr<Extension> ext3 = - CreateExtension(ASCIIToUTF16("example2"), + CreateExtension(base::ASCIIToUTF16("example2"), base::FilePath(FILE_PATH_LITERAL("//nonexistent3")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_HOSTED_APP, @@ -626,7 +754,7 @@ TEST_F(ProfileResetterTest, ResetStartPagePartially) { TEST_F(PinnedTabsResetTest, ResetPinnedTabs) { scoped_refptr<Extension> extension_app = CreateExtension( - ASCIIToUTF16("hello!"), + base::ASCIIToUTF16("hello!"), base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_HOSTED_APP, @@ -662,6 +790,20 @@ TEST_F(PinnedTabsResetTest, ResetPinnedTabs) { EXPECT_EQ(1, tab_strip_model->IndexOfFirstNonMiniTab()); } +TEST_F(ProfileResetterTest, ResetShortcuts) { + ShortcutHandler shortcut; + ShortcutCommand command_line = shortcut.CreateWithArguments( + base::ASCIIToUTF16("chrome.lnk"), + base::ASCIIToUTF16("--profile-directory=Default foo.com")); + shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16( + "--profile-directory=Default foo.com")); + + ResetAndWait(ProfileResetter::SHORTCUTS); + + shortcut.CheckShortcutHasArguments(base::ASCIIToUTF16( + "--profile-directory=Default")); +} + TEST_F(ProfileResetterTest, ResetFewFlags) { // mock_object_ is a StrictMock, so we verify that it is called only once. ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | @@ -719,11 +861,11 @@ TEST_F(ConfigParserTest, ParseConfig) { } TEST_F(ProfileResetterTest, CheckSnapshots) { - ResettableSettingsSnapshot empty_snap(profile()); + ResettableSettingsSnapshot empty_snap(profile(), false); EXPECT_EQ(0, empty_snap.FindDifferentFields(empty_snap)); scoped_refptr<Extension> ext = CreateExtension( - ASCIIToUTF16("example"), + base::ASCIIToUTF16("example"), base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -740,9 +882,24 @@ TEST_F(ProfileResetterTest, CheckSnapshots) { ProfileResetter::HOMEPAGE | ProfileResetter::STARTUP_PAGES, master_prefs); - - ResettableSettingsSnapshot nonorganic_snap(profile()); - EXPECT_EQ(ResettableSettingsSnapshot::ALL_FIELDS, + ShortcutHandler shortcut_hijacked; + ShortcutCommand command_line = shortcut_hijacked.CreateWithArguments( + base::ASCIIToUTF16("chrome1.lnk"), + base::ASCIIToUTF16("--profile-directory=Default foo.com")); + shortcut_hijacked.CheckShortcutHasArguments( + base::ASCIIToUTF16("--profile-directory=Default foo.com")); + ShortcutHandler shortcut_ok; + shortcut_ok.CreateWithArguments( + base::ASCIIToUTF16("chrome2.lnk"), + base::ASCIIToUTF16("--profile-directory=Default1")); + + ResettableSettingsSnapshot nonorganic_snap(profile(), true); + // Let it enumerate shortcuts on the FILE thread. + base::MessageLoop::current()->RunUntilIdle(); + 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()); @@ -753,16 +910,19 @@ TEST_F(ProfileResetterTest, CheckSnapshots) { 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::EXTENSIONS | + ProfileResetter::SHORTCUTS); - ResettableSettingsSnapshot organic_snap(profile()); - EXPECT_EQ(ResettableSettingsSnapshot::ALL_FIELDS, - nonorganic_snap.FindDifferentFields(organic_snap)); + ResettableSettingsSnapshot organic_snap(profile(), true); + // Let it enumerate shortcuts on the FILE thread. + base::MessageLoop::current()->RunUntilIdle(); + 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>(urls, urls + arraysize(urls)), @@ -774,9 +934,15 @@ TEST_F(ProfileResetterTest, CheckSnapshots) { 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, FeedbackSerializtionTest) { +TEST_F(ProfileResetterTest, FeedbackSerializationTest) { // Reset to non organic defaults. ResetAndWait(ProfileResetter::DEFAULT_SEARCH_ENGINE | ProfileResetter::HOMEPAGE | @@ -784,7 +950,7 @@ TEST_F(ProfileResetterTest, FeedbackSerializtionTest) { kDistributionConfig); scoped_refptr<Extension> ext = CreateExtension( - ASCIIToUTF16("example"), + base::ASCIIToUTF16("example"), base::FilePath(FILE_PATH_LITERAL("//nonexistent")), Manifest::INVALID_LOCATION, extensions::Manifest::TYPE_EXTENSION, @@ -792,9 +958,16 @@ TEST_F(ProfileResetterTest, FeedbackSerializtionTest) { ASSERT_TRUE(ext); service_->AddExtension(ext.get()); - const ResettableSettingsSnapshot nonorganic_snap(profile()); + ShortcutHandler shortcut; + ShortcutCommand command_line = shortcut.CreateWithArguments( + base::ASCIIToUTF16("chrome.lnk"), + base::ASCIIToUTF16("--profile-directory=Default foo.com")); - COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15, + const ResettableSettingsSnapshot nonorganic_snap(profile(), true); + // Let it enumerate shortcuts on the FILE thread. + base::MessageLoop::current()->RunUntilIdle(); + + COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, expand_this_test); for (int field_mask = 0; field_mask <= ResettableSettingsSnapshot::ALL_FIELDS; ++field_mask) { @@ -813,7 +986,8 @@ TEST_F(ProfileResetterTest, FeedbackSerializtionTest) { std::string homepage; bool homepage_is_ntp = true; std::string default_search_engine; - base::ListValue* extensions; + base::ListValue* extensions = NULL; + base::ListValue* shortcuts = NULL; EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::STARTUP_MODE), dict->GetList("startup_urls", &startup_urls)); @@ -827,9 +1001,22 @@ TEST_F(ProfileResetterTest, FeedbackSerializtionTest) { dict->GetString("default_search_engine", &default_search_engine)); EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::EXTENSIONS), dict->GetList("enabled_extensions", &extensions)); + EXPECT_EQ(!!(field_mask & ResettableSettingsSnapshot::SHORTCUTS), + dict->GetList("shortcuts", &shortcuts)); } } +struct FeedbackCapture { + void SetList(scoped_ptr<base::ListValue> list) { + list_ = list.Pass(); + OnUpdatedList(); + } + + MOCK_METHOD0(OnUpdatedList, void(void)); + + scoped_ptr<base::ListValue> list_; +}; + // Make sure GetReadableFeedback handles non-ascii letters. TEST_F(ProfileResetterTest, GetReadableFeedback) { scoped_refptr<Extension> ext = CreateExtension( @@ -853,10 +1040,25 @@ TEST_F(ProfileResetterTest, GetReadableFeedback) { startup_pref.urls.push_back(GURL(base::WideToUTF8(url))); SessionStartupPref::SetStartupPref(prefs, startup_pref); + ShortcutHandler shortcut; + ShortcutCommand command_line = shortcut.CreateWithArguments( + base::ASCIIToUTF16("chrome.lnk"), + base::ASCIIToUTF16("--profile-directory=Default foo.com")); + + FeedbackCapture capture; + EXPECT_CALL(capture, OnUpdatedList()); + ResettableSettingsSnapshot::GetReadableFeedback( + profile(), + base::Bind(&FeedbackCapture::SetList, base::Unretained(&capture))); + // Let it enumerate shortcuts on the FILE thread. + base::MessageLoop::current()->RunUntilIdle(); + ::testing::Mock::VerifyAndClearExpectations(&capture); // The homepage and the startup page are in punycode. They are unreadable. // Trying to find the extension name. - scoped_ptr<base::ListValue> list(GetReadableFeedback(profile())); + scoped_ptr<base::ListValue> list = capture.list_.Pass(); ASSERT_TRUE(list); + bool checked_extensions = false; + bool checked_shortcuts = false; for (size_t i = 0; i < list->GetSize(); ++i) { base::DictionaryValue* dict = NULL; ASSERT_TRUE(list->GetDictionary(i, &dict)); @@ -866,8 +1068,17 @@ TEST_F(ProfileResetterTest, GetReadableFeedback) { base::string16 extensions; EXPECT_TRUE(dict->GetString("value", &extensions)); EXPECT_EQ(base::WideToUTF16(L"Tiƫsto"), extensions); + checked_extensions = true; + } else if (value == "Shortcut targets") { + base::string16 targets; + EXPECT_TRUE(dict->GetString("value", &targets)); + EXPECT_NE(base::string16::npos, + targets.find(base::ASCIIToUTF16("foo.com"))) << targets; + checked_shortcuts = true; } } + EXPECT_TRUE(checked_extensions); + EXPECT_EQ(ShortcutHandler::IsSupported(), checked_shortcuts); } } // namespace diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc index 741bf82..3f1944c 100644 --- a/chrome/browser/profile_resetter/resettable_settings_snapshot.cc +++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.cc @@ -17,7 +17,7 @@ #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/pref_names.h" -#include "extensions/common/extension_set.h" +#include "content/public/browser/browser_thread.h" #include "grit/generated_resources.h" #include "grit/google_chrome_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -33,6 +33,7 @@ const char kDefaultSearchEnginePath[] = "default_search_engine"; const char kEnabledExtensions[] = "enabled_extensions"; const char kHomepageIsNewTabPage[] = "homepage_is_ntp"; const char kHomepagePath[] = "homepage"; +const char kShortcuts[] = "shortcuts"; const char kStartupTypePath[] = "startup_type"; const char kStartupURLPath[] = "startup_urls"; @@ -46,10 +47,131 @@ void AddPair(base::ListValue* list, list->Append(results); } +scoped_ptr<base::ListValue> GetReadableFeedbackForSnapshot( + Profile* profile, + const ResettableSettingsSnapshot& snapshot) { + DCHECK(profile); + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + scoped_ptr<base::ListValue> list(new base::ListValue); + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE), + g_browser_process->GetApplicationLocale()); + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT), + content::GetUserAgent(GURL())); + chrome::VersionInfo version_info; + std::string version = version_info.Version(); + version += chrome::VersionInfo::GetVersionStringModifier(); + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), + version); + + // Add snapshot data. + const std::vector<GURL>& urls = snapshot.startup_urls(); + std::string startup_urls; + for (std::vector<GURL>::const_iterator i = urls.begin(); + i != urls.end(); ++i) { + if (!startup_urls.empty()) + startup_urls += ' '; + startup_urls += i->host(); + } + if (!startup_urls.empty()) { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS), + startup_urls); + } + + base::string16 startup_type; + switch (snapshot.startup_type()) { + case SessionStartupPref::DEFAULT: + startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB); + break; + case SessionStartupPref::LAST: + startup_type = l10n_util::GetStringUTF16( + IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION); + break; + case SessionStartupPref::URLS: + startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES); + break; + default: + break; + } + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE), + startup_type); + + if (!snapshot.homepage().empty()) { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE), + snapshot.homepage()); + } + + int is_ntp_message_id = snapshot.homepage_is_ntp() ? + IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE : + IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE; + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP), + l10n_util::GetStringUTF16(is_ntp_message_id)); + + TemplateURLService* service = + TemplateURLServiceFactory::GetForProfile(profile); + DCHECK(service); + TemplateURL* dse = service->GetDefaultSearchProvider(); + if (dse) { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE), + TemplateURLService::GenerateSearchURL(dse).host()); + } + + if (snapshot.shortcuts_determined()) { + base::string16 shortcut_targets; + const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts(); + for (std::vector<ShortcutCommand>::const_iterator i = + shortcuts.begin(); i != shortcuts.end(); ++i) { + if (!shortcut_targets.empty()) + shortcut_targets += base::ASCIIToUTF16("\n"); + shortcut_targets += i->first.LossyDisplayName(); + shortcut_targets += base::ASCIIToUTF16(" "); + shortcut_targets += i->second; + } + if (!shortcut_targets.empty()) { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), + shortcut_targets); + } + } else { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_SHORTCUTS), + l10n_util::GetStringUTF16( + IDS_RESET_PROFILE_SETTINGS_PROCESSING_SHORTCUTS)); + } + + const ResettableSettingsSnapshot::ExtensionList& extensions = + snapshot.enabled_extensions(); + std::string extension_names; + for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = + extensions.begin(); i != extensions.end(); ++i) { + if (!extension_names.empty()) + extension_names += '\n'; + extension_names += i->second; + } + if (!extension_names.empty()) { + AddPair(list.get(), + l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS), + extension_names); + } + return list.Pass(); +} + } // namespace -ResettableSettingsSnapshot::ResettableSettingsSnapshot(Profile* profile) - : startup_(SessionStartupPref::GetStartupPref(profile)) { +ResettableSettingsSnapshot::ResettableSettingsSnapshot( + Profile* profile, + bool async_request_shortcuts) + : startup_(SessionStartupPref::GetStartupPref(profile)), + shortcuts_determined_(false), + weak_ptr_factory_(this) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // URLs are always stored sorted. std::sort(startup_.urls.begin(), startup_.urls.end()); @@ -76,12 +198,25 @@ ResettableSettingsSnapshot::ResettableSettingsSnapshot(Profile* profile) // ExtensionSet is sorted but it seems to be an implementation detail. std::sort(enabled_extensions_.begin(), enabled_extensions_.end()); + if (async_request_shortcuts) { + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::FILE, + FROM_HERE, + base::Bind(&GetChromeLaunchShortcuts), + base::Bind(&ResettableSettingsSnapshot::SetShortcutsReportFullFeedback, + weak_ptr_factory_.GetWeakPtr(), + profile, + base::Callback<void(scoped_ptr<base::ListValue>)>())); + } } -ResettableSettingsSnapshot::~ResettableSettingsSnapshot() {} +ResettableSettingsSnapshot::~ResettableSettingsSnapshot() { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); +} void ResettableSettingsSnapshot::Subtract( const ResettableSettingsSnapshot& snapshot) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); ExtensionList extensions = base::STLSetDifference<ExtensionList>( enabled_extensions_, snapshot.enabled_extensions_); enabled_extensions_.swap(extensions); @@ -89,6 +224,7 @@ void ResettableSettingsSnapshot::Subtract( int ResettableSettingsSnapshot::FindDifferentFields( const ResettableSettingsSnapshot& snapshot) const { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); int bit_mask = 0; if (startup_.type != snapshot.startup_.type || @@ -105,14 +241,54 @@ int ResettableSettingsSnapshot::FindDifferentFields( if (enabled_extensions_ != snapshot.enabled_extensions_) bit_mask |= EXTENSIONS; - COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15, + if (shortcuts_ != snapshot.shortcuts_) + bit_mask |= SHORTCUTS; + + COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, add_new_field_here); return bit_mask; } +// static +scoped_ptr<base::ListValue> ResettableSettingsSnapshot::GetReadableFeedback( + Profile* profile, + const base::Callback<void(scoped_ptr<base::ListValue>)>& callback) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + scoped_ptr<ResettableSettingsSnapshot> snapshot( + new ResettableSettingsSnapshot(profile, false)); + scoped_ptr<base::ListValue> list( + GetReadableFeedbackForSnapshot(profile, *snapshot)); + content::BrowserThread::PostTaskAndReplyWithResult( + content::BrowserThread::FILE, + FROM_HERE, + base::Bind(&GetChromeLaunchShortcuts), + base::Bind(&ResettableSettingsSnapshot::SetShortcutsReportFullFeedback, + base::Owned(snapshot.release()), + profile, + callback)); + return list.Pass(); +} + +void ResettableSettingsSnapshot::SetShortcutsReportFullFeedback( + Profile* profile, + const base::Callback<void(scoped_ptr<base::ListValue>)>& callback, + const std::vector<ShortcutCommand>& shortcuts) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + for (std::vector<ShortcutCommand>::const_iterator i = shortcuts.begin(); + i != shortcuts.end(); ++i) { + if (!i->second.empty()) + shortcuts_.push_back(*i); + } + shortcuts_determined_ = true; + + if (!callback.is_null()) + callback.Run(GetReadableFeedbackForSnapshot(profile, *this)); +} + std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot, int field_mask) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); base::DictionaryValue dict; if (field_mask & ResettableSettingsSnapshot::STARTUP_MODE) { @@ -147,7 +323,18 @@ std::string SerializeSettingsReport(const ResettableSettingsSnapshot& snapshot, dict.Set(kEnabledExtensions, list); } - COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 15, + if (field_mask & ResettableSettingsSnapshot::SHORTCUTS) { + base::ListValue* list = new base::ListValue; + const std::vector<ShortcutCommand>& shortcuts = snapshot.shortcuts(); + for (std::vector<ShortcutCommand>::const_iterator i = shortcuts.begin(); + i != shortcuts.end(); ++i) { + list->AppendString( + i->first.LossyDisplayName() + base::ASCIIToUTF16(" ") + i->second); + } + dict.Set(kShortcuts, list); + } + + COMPILE_ASSERT(ResettableSettingsSnapshot::ALL_FIELDS == 31, serialize_new_field_here); std::string json; @@ -179,91 +366,3 @@ void SendSettingsFeedback(const std::string& report, feedback_util::SendReport(feedback_data); } - -base::ListValue* GetReadableFeedback(Profile* profile) { - DCHECK(profile); - base::ListValue* list = new base::ListValue; - AddPair(list, l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_LOCALE), - g_browser_process->GetApplicationLocale()); - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_USER_AGENT), - content::GetUserAgent(GURL())); - chrome::VersionInfo version_info; - std::string version = version_info.Version(); - version += chrome::VersionInfo::GetVersionStringModifier(); - AddPair(list, - l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), - version); - - // Add snapshot data. - ResettableSettingsSnapshot snapshot(profile); - const std::vector<GURL>& urls = snapshot.startup_urls(); - std::string startup_urls; - for (std::vector<GURL>::const_iterator i = urls.begin(); - i != urls.end(); ++i) { - (startup_urls += i->host()) += ' '; - } - if (!startup_urls.empty()) { - startup_urls.erase(startup_urls.end() - 1); - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_URLS), - startup_urls); - } - - base::string16 startup_type; - switch (snapshot.startup_type()) { - case SessionStartupPref::DEFAULT: - startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_NEWTAB); - break; - case SessionStartupPref::LAST: - startup_type = l10n_util::GetStringUTF16( - IDS_OPTIONS_STARTUP_RESTORE_LAST_SESSION); - break; - case SessionStartupPref::URLS: - startup_type = l10n_util::GetStringUTF16(IDS_OPTIONS_STARTUP_SHOW_PAGES); - break; - default: - break; - } - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_STARTUP_TYPE), - startup_type); - - if (!snapshot.homepage().empty()) { - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE), - snapshot.homepage()); - } - - int is_ntp_message_id = snapshot.homepage_is_ntp() ? - IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_TRUE : - IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP_FALSE; - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_HOMEPAGE_IS_NTP), - l10n_util::GetStringUTF16(is_ntp_message_id)); - - TemplateURLService* service = - TemplateURLServiceFactory::GetForProfile(profile); - DCHECK(service); - TemplateURL* dse = service->GetDefaultSearchProvider(); - if (dse) { - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_DSE), - TemplateURLService::GenerateSearchURL(dse).host()); - } - - const ResettableSettingsSnapshot::ExtensionList& extensions = - snapshot.enabled_extensions(); - std::string extension_names; - for (ResettableSettingsSnapshot::ExtensionList::const_iterator i = - extensions.begin(); i != extensions.end(); ++i) { - (extension_names += i->second) += '\n'; - } - if (!extension_names.empty()) { - extension_names.erase(extension_names.end() - 1); - AddPair(list, - l10n_util::GetStringUTF16(IDS_RESET_PROFILE_SETTINGS_EXTENSIONS), - extension_names); - } - return list; -} diff --git a/chrome/browser/profile_resetter/resettable_settings_snapshot.h b/chrome/browser/profile_resetter/resettable_settings_snapshot.h index df58f08..3a1e686 100644 --- a/chrome/browser/profile_resetter/resettable_settings_snapshot.h +++ b/chrome/browser/profile_resetter/resettable_settings_snapshot.h @@ -5,8 +5,15 @@ #ifndef CHROME_BROWSER_PROFILE_RESETTER_RESETTABLE_SETTINGS_SNAPSHOT_H_ #define CHROME_BROWSER_PROFILE_RESETTER_RESETTABLE_SETTINGS_SNAPSHOT_H_ +#include <string> +#include <utility> +#include <vector> + #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "chrome/browser/prefs/session_startup_pref.h" +#include "chrome/browser/profile_resetter/profile_resetter.h" namespace base { class ListValue; @@ -25,11 +32,14 @@ class ResettableSettingsSnapshot { HOMEPAGE = 1 << 1, DSE_URL = 1 << 2, EXTENSIONS = 1 << 3, + SHORTCUTS = 1 << 4, - ALL_FIELDS = STARTUP_MODE | HOMEPAGE | DSE_URL | EXTENSIONS, + ALL_FIELDS = STARTUP_MODE | HOMEPAGE | DSE_URL | EXTENSIONS | SHORTCUTS, }; - explicit ResettableSettingsSnapshot(Profile* profile); + // If |async_request_shortcuts| is true, shortcuts are requested on FILE + // thread. Otherwise they aren't enumerated at all. + ResettableSettingsSnapshot(Profile* profile, bool async_request_shortcuts); ~ResettableSettingsSnapshot(); // Getters. @@ -47,6 +57,14 @@ class ResettableSettingsSnapshot { return enabled_extensions_; } + const std::vector<ShortcutCommand>& shortcuts() const { + return shortcuts_; + } + + bool shortcuts_determined() const { + return shortcuts_determined_; + } + // Substitutes |enabled_extensions_| with // |enabled_extensions_|\|snapshot.enabled_extensions_|. void Subtract(const ResettableSettingsSnapshot& snapshot); @@ -58,7 +76,21 @@ class ResettableSettingsSnapshot { // were different. int FindDifferentFields(const ResettableSettingsSnapshot& snapshot) const; + // Returns list of key/value pairs for all available reported information + // from the |profile| and some additional fields. + // Some data is collected asynchronously. |callback| is called when all + // information has been retrieved. + static scoped_ptr<base::ListValue> GetReadableFeedback( + Profile* profile, + const base::Callback<void(scoped_ptr<base::ListValue>)>& callback); + private: + // Fills the |shortcuts_| member and calls |callback|. + void SetShortcutsReportFullFeedback( + Profile* profile, + const base::Callback<void(scoped_ptr<base::ListValue>)>& callback, + const std::vector<ShortcutCommand>& shortcuts); + // Startup pages. URLs are always stored sorted. SessionStartupPref startup_; @@ -71,6 +103,15 @@ class ResettableSettingsSnapshot { // List of pairs [id, name] for enabled extensions. Always sorted. ExtensionList enabled_extensions_; + // Chrome shortcuts (e.g. icons on the Windows desktop, etc.) with non-empty + // arguments. + std::vector<ShortcutCommand> shortcuts_; + + // |shortcuts_| were retrieved. + bool shortcuts_determined_; + + base::WeakPtrFactory<ResettableSettingsSnapshot> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(ResettableSettingsSnapshot); }; @@ -91,8 +132,4 @@ void SendSettingsFeedback(const std::string& report, Profile* profile, SnapshotCaller caller); -// Returns list of key/value pairs for all reported information from the -// |profile| and some additional fields. -base::ListValue* GetReadableFeedback(Profile* profile); - #endif // CHROME_BROWSER_PROFILE_RESETTER_RESETTABLE_SETTINGS_SNAPSHOT_H_ diff --git a/chrome/browser/ui/views/profile_reset_bubble_view.cc b/chrome/browser/ui/views/profile_reset_bubble_view.cc index 1189356..4183a68 100644 --- a/chrome/browser/ui/views/profile_reset_bubble_view.cc +++ b/chrome/browser/ui/views/profile_reset_bubble_view.cc @@ -4,8 +4,6 @@ #include "chrome/browser/ui/views/profile_reset_bubble_view.h" -#include "base/memory/scoped_ptr.h" -#include "base/values.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/google/google_util.h" #include "chrome/browser/profile_resetter/profile_reset_global_error.h" @@ -196,6 +194,11 @@ void ProfileResetBubbleView::ResetAllChildren() { void ProfileResetBubbleView::Init() { set_margins(gfx::Insets(kMarginHeight, 0, 0, 0)); + // Start requesting the feedback data. + feedback_data_ = ResettableSettingsSnapshot::GetReadableFeedback( + profile_, + base::Bind(&ProfileResetBubbleView::UpdateFeedbackDetails, + weak_factory_.GetWeakPtr())); SetupLayoutManager(true); } @@ -348,29 +351,26 @@ void ProfileResetBubbleView::SetupLayoutManager(bool report_checked) { layout->AddView(controls_.report_settings_checkbox); layout->AddView(controls_.help_button); - if (show_help_pane_) { - scoped_ptr<base::ListValue> feedback(GetReadableFeedback(profile_)); - if (feedback.get()) { - // We need a single row to add the scroll view containing the feedback. - const int kReportDetailsColumnSetId = 5; - cs = layout->AddColumnSet(kReportDetailsColumnSetId); - cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, - GridLayout::USE_PREF, 0, 0); - - FeedbackView* feedback_view = new FeedbackView(); - feedback_view->SetupLayoutManager(*feedback.get()); - - views::ScrollView* scroll_view = new views::ScrollView(); - scroll_view->set_background(views::Background::CreateSolidBackground( - kLightGrayBackgroundColor)); - scroll_view->SetContents(feedback_view); - - layout->StartRow(1, kReportDetailsColumnSetId); - layout->AddView(scroll_view, 1, 1, GridLayout::FILL, - GridLayout::FILL, kAllColumnsWidth, - std::min(feedback_view->height() + kMarginHeight, - kMaxFeedbackViewHeight)); - } + if (show_help_pane_ && feedback_data_) { + // We need a single row to add the scroll view containing the feedback. + const int kReportDetailsColumnSetId = 5; + cs = layout->AddColumnSet(kReportDetailsColumnSetId); + cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, + GridLayout::USE_PREF, 0, 0); + + FeedbackView* feedback_view = new FeedbackView(); + feedback_view->SetupLayoutManager(*feedback_data_); + + views::ScrollView* scroll_view = new views::ScrollView(); + scroll_view->set_background(views::Background::CreateSolidBackground( + kLightGrayBackgroundColor)); + scroll_view->SetContents(feedback_view); + + layout->StartRow(1, kReportDetailsColumnSetId); + layout->AddView(scroll_view, 1, 1, GridLayout::FILL, + GridLayout::FILL, kAllColumnsWidth, + std::min(feedback_view->height() + kMarginHeight, + kMaxFeedbackViewHeight)); } Layout(); @@ -427,6 +427,13 @@ void ProfileResetBubbleView::CloseBubbleView() { StartFade(false); } +void ProfileResetBubbleView::UpdateFeedbackDetails( + scoped_ptr<base::ListValue> feedback_list) { + feedback_data_ = feedback_list.Pass(); + if (show_help_pane_) + SetupLayoutManager(controls_.report_settings_checkbox->checked()); +} + bool IsProfileResetBubbleSupported() { return true; } diff --git a/chrome/browser/ui/views/profile_reset_bubble_view.h b/chrome/browser/ui/views/profile_reset_bubble_view.h index 8924112..3657e44 100644 --- a/chrome/browser/ui/views/profile_reset_bubble_view.h +++ b/chrome/browser/ui/views/profile_reset_bubble_view.h @@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/values.h" #include "chrome/browser/ui/global_error/global_error_bubble_view_base.h" #include "ui/gfx/image/image_skia.h" #include "ui/views/bubble/bubble_delegate.h" @@ -74,6 +75,9 @@ class ProfileResetBubbleView : public views::BubbleDelegateView, // views::LinkListener method. virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; + // Sets the fully populated feedback data. + void UpdateFeedbackDetails(scoped_ptr<base::ListValue> feedback_list); + struct Controls { Controls() { Reset(); @@ -98,6 +102,9 @@ class ProfileResetBubbleView : public views::BubbleDelegateView, views::Checkbox* report_settings_checkbox; } controls_; + // Feedback shown to user. + scoped_ptr<base::ListValue> feedback_data_; + // A version of the help image that is brighter. gfx::ImageSkia brighter_help_image_; diff --git a/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc b/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc index 975891b..a245bb8 100644 --- a/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc +++ b/chrome/browser/ui/webui/options/reset_profile_settings_handler.cc @@ -114,7 +114,7 @@ void ResetProfileSettingsHandler::OnResetProfileSettingsDone() { web_ui()->CallJavascriptFunction("ResetProfileSettingsOverlay.doneResetting"); if (setting_snapshot_) { Profile* profile = Profile::FromWebUI(web_ui()); - ResettableSettingsSnapshot current_snapshot(profile); + ResettableSettingsSnapshot current_snapshot(profile, false); int difference = setting_snapshot_->FindDifferentFields(current_snapshot); if (difference) { setting_snapshot_->Subtract(current_snapshot); @@ -134,13 +134,10 @@ void ResetProfileSettingsHandler::OnResetProfileSettingsDone() { } void ResetProfileSettingsHandler::OnShowResetProfileDialog( - const base::ListValue*) { - base::DictionaryValue flashInfo; - flashInfo.Set("feedbackInfo", GetReadableFeedback( - Profile::FromWebUI(web_ui()))); - web_ui()->CallJavascriptFunction( - "ResetProfileSettingsOverlay.setFeedbackInfo", - flashInfo); + const base::ListValue* value) { + UpdateFeedbackUI(ResettableSettingsSnapshot::GetReadableFeedback( + Profile::FromWebUI(web_ui()), + base::Bind(&ResetProfileSettingsHandler::UpdateFeedbackUI, AsWeakPtr()))); if (automatic_profile_resetter_) automatic_profile_resetter_->NotifyDidOpenWebUIResetDialog(); @@ -186,7 +183,8 @@ void ResetProfileSettingsHandler::ResetProfile(bool send_settings) { default_settings.reset(new BrandcodedDefaultSettings); // Save current settings if required. setting_snapshot_.reset(send_settings ? - new ResettableSettingsSnapshot(Profile::FromWebUI(web_ui())) : NULL); + new ResettableSettingsSnapshot(Profile::FromWebUI(web_ui()), true) : + NULL); resetter_->Reset( ProfileResetter::ALL, default_settings.Pass(), @@ -196,4 +194,13 @@ void ResetProfileSettingsHandler::ResetProfile(bool send_settings) { UMA_HISTOGRAM_BOOLEAN("ProfileReset.SendFeedback", send_settings); } +void ResetProfileSettingsHandler::UpdateFeedbackUI( + scoped_ptr<base::ListValue> list) { + base::DictionaryValue feedback_info; + feedback_info.Set("feedbackInfo", list.release()); + web_ui()->CallJavascriptFunction( + "ResetProfileSettingsOverlay.setFeedbackInfo", + feedback_info); +} + } // namespace options diff --git a/chrome/browser/ui/webui/options/reset_profile_settings_handler.h b/chrome/browser/ui/webui/options/reset_profile_settings_handler.h index ab8766f..8914290 100644 --- a/chrome/browser/ui/webui/options/reset_profile_settings_handler.h +++ b/chrome/browser/ui/webui/options/reset_profile_settings_handler.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_OPTIONS_RESET_PROFILE_SETTINGS_HANDLER_H_ #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/ui/webui/options/options_ui.h" @@ -60,6 +61,9 @@ class ResetProfileSettingsHandler // gave his consent to upload broken settings to Google for analysis. void ResetProfile(bool send_settings); + // Sets new values for the feedback area. + void UpdateFeedbackUI(scoped_ptr<base::ListValue> list); + // Destroyed with the Profile, thus it should outlive us. This will be NULL if // the underlying profile is off-the-record (e.g. in Guest mode on Chrome OS). AutomaticProfileResetter* automatic_profile_resetter_; diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc index f5e3851..c6ae322 100644 --- a/chrome/installer/util/shell_util.cc +++ b/chrome/installer/util/shell_util.cc @@ -30,6 +30,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "base/win/registry.h" @@ -1292,6 +1293,37 @@ bool ShortcutOpRetarget(const base::FilePath& old_target, return result; } +bool ShortcutOpListOrRemoveUnknownArgs( + bool do_removal, + std::vector<std::pair<base::FilePath, base::string16> >* shortcuts, + const base::FilePath& shortcut_path) { + base::string16 args; + if (!base::win::ResolveShortcut(shortcut_path, NULL, &args)) + return false; + + CommandLine current_args(CommandLine::FromString(base::StringPrintf( + L"unused_program %ls", args.c_str()))); + const char* const kept_switches[] = { + switches::kApp, + switches::kAppId, + switches::kShowAppList, + switches::kProfileDirectory, + }; + CommandLine desired_args(CommandLine::NO_PROGRAM); + desired_args.CopySwitchesFrom(current_args, kept_switches, + arraysize(kept_switches)); + if (desired_args.argv().size() == current_args.argv().size()) + return true; + if (shortcuts) + shortcuts->push_back(std::make_pair(shortcut_path, args)); + if (!do_removal) + return true; + base::win::ShortcutProperties updated_properties; + updated_properties.set_arguments(desired_args.GetArgumentsString()); + return base::win::CreateOrUpdateShortcutLink( + shortcut_path, updated_properties, base::win::SHORTCUT_UPDATE_EXISTING); +} + // {|location|, |dist|, |level|} determine |shortcut_folder|. // For each shortcut in |shortcut_folder| that match |shortcut_filter|, apply // |shortcut_operation|. Returns true if all operations are successful. @@ -2137,6 +2169,24 @@ bool ShellUtil::RetargetShortcutsWithArgs( shortcut_operation, location, dist, level); } +// static +bool ShellUtil::ShortcutListMaybeRemoveUnknownArgs( + ShellUtil::ShortcutLocation location, + BrowserDistribution* dist, + ShellChange level, + const base::FilePath& chrome_exe, + bool do_removal, + std::vector<std::pair<base::FilePath, base::string16> >* shortcuts) { + if (!ShellUtil::ShortcutLocationIsSupported(location)) + return false; + DCHECK(dist); + FilterTargetEq shortcut_filter(chrome_exe, true); + ShortcutOperationCallback shortcut_operation( + base::Bind(&ShortcutOpListOrRemoveUnknownArgs, do_removal, shortcuts)); + return BatchShortcutAction(shortcut_filter.AsShortcutFilterCallback(), + shortcut_operation, location, dist, level); +} + bool ShellUtil::GetUserSpecificRegistrySuffix(base::string16* suffix) { // Use a thread-safe cache for the user's suffix. static base::LazyInstance<UserSpecificRegistrySuffix>::Leaky suffix_instance = diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h index 868a275..fccd39c 100644 --- a/chrome/installer/util/shell_util.h +++ b/chrome/installer/util/shell_util.h @@ -12,6 +12,7 @@ #include <windows.h> #include <map> +#include <utility> #include <vector> #include "base/basictypes.h" @@ -544,6 +545,17 @@ class ShellUtil { const base::FilePath& old_target_exe, const base::FilePath& new_target_exe); + // Appends Chrome shortcuts with non-whitelisted arguments to |shortcuts| if + // not NULL. If |do_removal|, also removes non-whitelisted arguments from + // those shortcuts. + static bool ShortcutListMaybeRemoveUnknownArgs( + ShellUtil::ShortcutLocation location, + BrowserDistribution* dist, + ShellChange level, + const base::FilePath& chrome_exe, + bool do_removal, + std::vector<std::pair<base::FilePath, base::string16> >* shortcuts); + // Sets |suffix| to the base 32 encoding of the md5 hash of this user's sid // preceded by a dot. // This is guaranteed to be unique on the machine and 27 characters long diff --git a/chrome/installer/util/shell_util_unittest.cc b/chrome/installer/util/shell_util_unittest.cc index 76ed7de5..20d23b5 100644 --- a/chrome/installer/util/shell_util_unittest.cc +++ b/chrome/installer/util/shell_util_unittest.cc @@ -590,6 +590,101 @@ TEST_F(ShellUtilShortcutTest, RetargetChromeShortcutsWithArgsIcon) { expected_properties3); } +TEST_F(ShellUtilShortcutTest, ClearShortcutArguments) { + // Shortcut 1: targets "chrome.exe"; no arguments. + test_properties_.set_shortcut_name(L"Chrome 1"); + test_properties_.set_arguments(L""); + ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_, + ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS)); + base::FilePath shortcut1_path = GetExpectedShortcutPath( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_); + ASSERT_TRUE(base::PathExists(shortcut1_path)); + ShellUtil::ShortcutProperties expected_properties1(test_properties_); + + // Shortcut 2: targets "chrome.exe"; has 1 whitelisted argument. + test_properties_.set_shortcut_name(L"Chrome 2"); + test_properties_.set_arguments(L"--profile-directory=\"Profile 2\""); + ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_, + ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS)); + base::FilePath shortcut2_path = GetExpectedShortcutPath( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_); + ASSERT_TRUE(base::PathExists(shortcut2_path)); + ShellUtil::ShortcutProperties expected_properties2(test_properties_); + + // Shortcut 3: targets "chrome.exe"; has an unknown argument. + test_properties_.set_shortcut_name(L"Chrome 3"); + test_properties_.set_arguments(L"foo.com"); + ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_, + ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS)); + base::FilePath shortcut3_path = GetExpectedShortcutPath( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_); + ASSERT_TRUE(base::PathExists(shortcut3_path)); + ShellUtil::ShortcutProperties expected_properties3(test_properties_); + + // Shortcut 4: targets "chrome.exe"; has both unknown and known arguments. + test_properties_.set_shortcut_name(L"Chrome 4"); + test_properties_.set_arguments(L"foo.com --show-app-list"); + ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_, + ShellUtil::SHELL_SHORTCUT_CREATE_ALWAYS)); + base::FilePath shortcut4_path = GetExpectedShortcutPath( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, test_properties_); + ASSERT_TRUE(base::PathExists(shortcut4_path)); + ShellUtil::ShortcutProperties expected_properties4(test_properties_); + + // List the shortcuts. + std::vector<std::pair<base::FilePath, base::string16> > shortcuts; + EXPECT_TRUE(ShellUtil::ShortcutListMaybeRemoveUnknownArgs( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, + dist_, + ShellUtil::CURRENT_USER, + chrome_exe_, + false, + &shortcuts)); + ASSERT_EQ(2u, shortcuts.size()); + std::pair<base::FilePath, base::string16> shortcut3 = + shortcuts[0].first == shortcut3_path ? shortcuts[0] : shortcuts[1]; + std::pair<base::FilePath, base::string16> shortcut4 = + shortcuts[0].first == shortcut4_path ? shortcuts[0] : shortcuts[1]; + EXPECT_EQ(shortcut3_path, shortcut3.first); + EXPECT_EQ(L"foo.com", shortcut3.second); + EXPECT_EQ(shortcut4_path, shortcut4.first); + EXPECT_EQ(L"foo.com --show-app-list", shortcut4.second); + + // Clear shortcuts. + shortcuts.clear(); + EXPECT_TRUE(ShellUtil::ShortcutListMaybeRemoveUnknownArgs( + ShellUtil::SHORTCUT_LOCATION_DESKTOP, + dist_, + ShellUtil::CURRENT_USER, + chrome_exe_, + true, + &shortcuts)); + ASSERT_EQ(2u, shortcuts.size()); + shortcut3 = shortcuts[0].first == shortcut3_path ? shortcuts[0] : + shortcuts[1]; + shortcut4 = shortcuts[0].first == shortcut4_path ? shortcuts[0] : + shortcuts[1]; + EXPECT_EQ(shortcut3_path, shortcut3.first); + EXPECT_EQ(L"foo.com", shortcut3.second); + EXPECT_EQ(shortcut4_path, shortcut4.first); + EXPECT_EQ(L"foo.com --show-app-list", shortcut4.second); + + ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, + expected_properties1); + ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, + expected_properties2); + expected_properties3.set_arguments(base::string16()); + ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, + expected_properties3); + expected_properties4.set_arguments(L"--show-app-list"); + ValidateChromeShortcut(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist_, + expected_properties4); +} + TEST_F(ShellUtilShortcutTest, CreateMultipleStartMenuShortcutsAndRemoveFolder) { ASSERT_TRUE(ShellUtil::CreateOrUpdateShortcut( ShellUtil::SHORTCUT_LOCATION_START_MENU_CHROME_DIR, |