summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvasilii@chromium.org <vasilii@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-14 14:54:09 +0000
committervasilii@chromium.org <vasilii@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-14 14:54:09 +0000
commit73ba82b5e91eaca97fbd517817e2fe2c87bc97dc (patch)
tree543c5da9c7066f91a35e7400b72e29bcf24dbc49
parent867a32486223a69c28f0614ee9c834db43ea5c81 (diff)
downloadchromium_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
-rw-r--r--chrome/browser/extensions/extension_service_unittest.h4
-rw-r--r--chrome/browser/profile_resetter/automatic_profile_resetter_delegate.cc4
-rw-r--r--chrome/browser/profile_resetter/profile_resetter.cc78
-rw-r--r--chrome/browser/profile_resetter/profile_resetter.h22
-rw-r--r--chrome/browser/profile_resetter/profile_resetter_unittest.cc281
-rw-r--r--chrome/browser/profile_resetter/resettable_settings_snapshot.cc287
-rw-r--r--chrome/browser/profile_resetter/resettable_settings_snapshot.h49
-rw-r--r--chrome/browser/ui/views/profile_reset_bubble_view.cc57
-rw-r--r--chrome/browser/ui/views/profile_reset_bubble_view.h7
-rw-r--r--chrome/browser/ui/webui/options/reset_profile_settings_handler.cc25
-rw-r--r--chrome/browser/ui/webui/options/reset_profile_settings_handler.h4
-rw-r--r--chrome/installer/util/shell_util.cc50
-rw-r--r--chrome/installer/util/shell_util.h12
-rw-r--r--chrome/installer/util/shell_util_unittest.cc95
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,