diff options
author | mgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 10:09:29 +0000 |
---|---|---|
committer | mgiuca@chromium.org <mgiuca@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-05 10:09:29 +0000 |
commit | 111f0287058cd71709f4123d3c0095d0a4b066bc (patch) | |
tree | c42f8460bbc08d5f9158f4b8b577ca249e159447 | |
parent | bcc2f47984fd65d35dee5a5f659756d1216e89f1 (diff) | |
download | chromium_src-111f0287058cd71709f4123d3c0095d0a4b066bc.zip chromium_src-111f0287058cd71709f4123d3c0095d0a4b066bc.tar.gz chromium_src-111f0287058cd71709f4123d3c0095d0a4b066bc.tar.bz2 |
Linux: Deleting a profile deletes all app shortcuts associated with it.
This will search all locations where app shortcuts reside and delete any whose
filename matches the given profile.
BUG=236353
TEST=Create profile, create Desktop and app menu shortcuts, delete profile.
Check that Desktop and app menu shortcuts have been deleted for that profile,
and remain for any other profile.
Review URL: https://chromiumcodereview.appspot.com/21723002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215588 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/shell_integration_linux.cc | 125 | ||||
-rw-r--r-- | chrome/browser/shell_integration_linux.h | 23 | ||||
-rw-r--r-- | chrome/browser/shell_integration_unittest.cc | 126 | ||||
-rw-r--r-- | chrome/browser/web_applications/web_app_linux.cc | 2 |
4 files changed, 249 insertions, 27 deletions
diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc index ffefa81..b19fd2e 100644 --- a/chrome/browser/shell_integration_linux.cc +++ b/chrome/browser/shell_integration_linux.cc @@ -18,6 +18,7 @@ #include "base/command_line.h" #include "base/environment.h" #include "base/file_util.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/i18n/file_util_icu.h" @@ -505,6 +506,45 @@ bool ShellIntegration::IsFirefoxDefaultBrowser() { namespace ShellIntegrationLinux { +bool GetDataWriteLocation(base::Environment* env, base::FilePath* search_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + std::string xdg_data_home; + std::string home; + if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && !xdg_data_home.empty()) { + *search_path = base::FilePath(xdg_data_home); + return true; + } else if (env->GetVar("HOME", &home) && !home.empty()) { + *search_path = base::FilePath(home).Append(".local").Append("share"); + return true; + } + return false; +} + +std::vector<base::FilePath> GetDataSearchLocations(base::Environment* env) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + std::vector<base::FilePath> search_paths; + + base::FilePath write_location; + if (GetDataWriteLocation(env, &write_location)) + search_paths.push_back(write_location); + + std::string xdg_data_dirs; + if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && !xdg_data_dirs.empty()) { + base::StringTokenizer tokenizer(xdg_data_dirs, ":"); + while (tokenizer.GetNext()) { + base::FilePath data_dir(tokenizer.token()); + search_paths.push_back(data_dir); + } + } else { + search_paths.push_back(base::FilePath("/usr/local/share")); + search_paths.push_back(base::FilePath("/usr/share")); + } + + return search_paths; +} + std::string GetDesktopName(base::Environment* env) { #if defined(GOOGLE_CHROME_BUILD) return "google-chrome.desktop"; @@ -575,32 +615,7 @@ bool GetExistingShortcutContents(base::Environment* env, std::string* output) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); - std::vector<base::FilePath> search_paths; - - // Search paths as specified in the XDG Base Directory Specification. - // http://standards.freedesktop.org/basedir-spec/latest/ - std::string xdg_data_home; - std::string home; - if (env->GetVar("XDG_DATA_HOME", &xdg_data_home) && - !xdg_data_home.empty()) { - search_paths.push_back(base::FilePath(xdg_data_home)); - } else if (env->GetVar("HOME", &home) && !home.empty()) { - search_paths.push_back(base::FilePath(home).Append(".local").Append( - "share")); - } - - std::string xdg_data_dirs; - if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && - !xdg_data_dirs.empty()) { - base::StringTokenizer tokenizer(xdg_data_dirs, ":"); - while (tokenizer.GetNext()) { - base::FilePath data_dir(tokenizer.token()); - search_paths.push_back(data_dir); - } - } else { - search_paths.push_back(base::FilePath("/usr/local/share")); - search_paths.push_back(base::FilePath("/usr/share")); - } + std::vector<base::FilePath> search_paths = GetDataSearchLocations(env); for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); i != search_paths.end(); ++i) { @@ -656,6 +671,32 @@ base::FilePath GetExtensionShortcutFilename(const base::FilePath& profile_path, return base::FilePath(filename.append(".desktop")); } +std::vector<base::FilePath> GetExistingProfileShortcutFilenames( + const base::FilePath& profile_path, + const base::FilePath& directory) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Use a prefix, because xdg-desktop-menu requires it. + std::string prefix(chrome::kBrowserProcessExecutableName); + prefix.append("-"); + std::string suffix("-"); + suffix.append(profile_path.BaseName().value()); + file_util::ReplaceIllegalCharactersInPath(&suffix, '_'); + // Spaces in filenames break xdg-desktop-menu + // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605). + ReplaceChars(suffix, " ", "_", &suffix); + std::string glob = prefix + "*" + suffix + ".desktop"; + + base::FileEnumerator files(directory, false, base::FileEnumerator::FILES, + glob); + base::FilePath shortcut_file = files.Next(); + std::vector<base::FilePath> shortcut_paths; + while (!shortcut_file.empty()) { + shortcut_paths.push_back(shortcut_file.BaseName()); + shortcut_file = files.Next(); + } + return shortcut_paths; +} + std::string GetDesktopFileContents( const base::FilePath& chrome_exe_path, const std::string& app_name, @@ -875,4 +916,36 @@ void DeleteDesktopShortcuts(const base::FilePath& profile_path, base::FilePath(kDirectoryFilename)); } +void DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + scoped_ptr<base::Environment> env(base::Environment::Create()); + + // Delete shortcuts from Desktop. + base::FilePath desktop_path; + if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) { + std::vector<base::FilePath> shortcut_filenames_desktop = + GetExistingProfileShortcutFilenames(profile_path, desktop_path); + for (std::vector<base::FilePath>::const_iterator it = + shortcut_filenames_desktop.begin(); + it != shortcut_filenames_desktop.end(); ++it) { + DeleteShortcutOnDesktop(*it); + } + } + + // Delete shortcuts from |kDirectoryFilename|. + base::FilePath applications_menu; + if (GetDataWriteLocation(env.get(), &applications_menu)) { + applications_menu = applications_menu.AppendASCII("applications"); + std::vector<base::FilePath> shortcut_filenames_app_menu = + GetExistingProfileShortcutFilenames(profile_path, applications_menu); + for (std::vector<base::FilePath>::const_iterator it = + shortcut_filenames_app_menu.begin(); + it != shortcut_filenames_app_menu.end(); ++it) { + DeleteShortcutInApplicationsMenu(*it, + base::FilePath(kDirectoryFilename)); + } + } +} + } // namespace ShellIntegrationLinux diff --git a/chrome/browser/shell_integration_linux.h b/chrome/browser/shell_integration_linux.h index 2618cf8..48414b8 100644 --- a/chrome/browser/shell_integration_linux.h +++ b/chrome/browser/shell_integration_linux.h @@ -18,6 +18,19 @@ class Environment; namespace ShellIntegrationLinux { +// Get the path to write user-specific application data files to, as specified +// in the XDG Base Directory Specification: +// http://standards.freedesktop.org/basedir-spec/latest/ +// Returns true on success, or false if no such path could be found. +// Called on the FILE thread. +bool GetDataWriteLocation(base::Environment* env, base::FilePath* search_path); + +// Get the list of paths to search for application data files, in order of +// preference, as specified in the XDG Base Directory Specification: +// http://standards.freedesktop.org/basedir-spec/latest/ +// Called on the FILE thread. +std::vector<base::FilePath> GetDataSearchLocations(base::Environment* env); + // Returns filename of the desktop shortcut used to launch the browser. std::string GetDesktopName(base::Environment* env); @@ -59,6 +72,12 @@ base::FilePath GetWebShortcutFilename(const GURL& url); base::FilePath GetExtensionShortcutFilename(const base::FilePath& profile_path, const std::string& extension_id); +// Returns a list of filenames for all existing .desktop files corresponding to +// on |profile_path| in a given |directory|. +std::vector<base::FilePath> GetExistingProfileShortcutFilenames( + const base::FilePath& profile_path, + const base::FilePath& directory); + // Returns contents for .desktop file based on |url| and |title|. If // |no_display| is true, the shortcut will not be visible to the user in menus. std::string GetDesktopFileContents(const base::FilePath& chrome_exe_path, @@ -89,6 +108,10 @@ bool CreateDesktopShortcut( void DeleteDesktopShortcuts(const base::FilePath& profile_path, const std::string& extension_id); +// Delete any desktop shortcuts on desktop or in the application menu that have +// for the profile in |profile_path|. +void DeleteAllDesktopShortcuts(const base::FilePath& profile_path); + } // namespace ShellIntegrationLinux #endif // CHROME_BROWSER_SHELL_INTEGRATION_LINUX_H_ diff --git a/chrome/browser/shell_integration_unittest.cc b/chrome/browser/shell_integration_unittest.cc index 17d5636..82a9b6a 100644 --- a/chrome/browser/shell_integration_unittest.cc +++ b/chrome/browser/shell_integration_unittest.cc @@ -4,6 +4,7 @@ #include "chrome/browser/shell_integration.h" +#include <algorithm> #include <cstdlib> #include <map> @@ -19,6 +20,7 @@ #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_constants.h" #include "content/public/test/test_browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -30,6 +32,7 @@ #define FPL FILE_PATH_LITERAL using content::BrowserThread; +using ::testing::ElementsAre; #if defined(OS_POSIX) && !defined(OS_MACOSX) namespace { @@ -71,6 +74,90 @@ class MockEnvironment : public base::Environment { } // namespace +TEST(ShellIntegrationTest, GetDataWriteLocation) { + base::MessageLoop message_loop; + content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop); + + // Test that it returns $XDG_DATA_HOME. + { + MockEnvironment env; + env.Set("HOME", "/home/user"); + env.Set("XDG_DATA_HOME", "/user/path"); + base::FilePath path; + ASSERT_TRUE(ShellIntegrationLinux::GetDataWriteLocation(&env, &path)); + EXPECT_EQ(base::FilePath("/user/path"), path); + } + + // Test that $XDG_DATA_HOME falls back to $HOME/.local/share. + { + MockEnvironment env; + env.Set("HOME", "/home/user"); + base::FilePath path; + ASSERT_TRUE(ShellIntegrationLinux::GetDataWriteLocation(&env, &path)); + EXPECT_EQ(base::FilePath("/home/user/.local/share"), path); + } + + // Test that if neither $XDG_DATA_HOME nor $HOME are specified, it fails. + { + MockEnvironment env; + base::FilePath path; + ASSERT_FALSE(ShellIntegrationLinux::GetDataWriteLocation(&env, &path)); + } +} + +TEST(ShellIntegrationTest, GetDataSearchLocations) { + base::MessageLoop message_loop; + content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop); + + // Test that it returns $XDG_DATA_HOME + $XDG_DATA_DIRS. + { + MockEnvironment env; + env.Set("HOME", "/home/user"); + env.Set("XDG_DATA_HOME", "/user/path"); + env.Set("XDG_DATA_DIRS", "/system/path/1:/system/path/2"); + EXPECT_THAT( + ShellIntegrationLinux::GetDataSearchLocations(&env), + ElementsAre(base::FilePath("/user/path"), + base::FilePath("/system/path/1"), + base::FilePath("/system/path/2"))); + } + + // Test that $XDG_DATA_HOME falls back to $HOME/.local/share. + { + MockEnvironment env; + env.Set("HOME", "/home/user"); + env.Set("XDG_DATA_DIRS", "/system/path/1:/system/path/2"); + EXPECT_THAT( + ShellIntegrationLinux::GetDataSearchLocations(&env), + ElementsAre(base::FilePath("/home/user/.local/share"), + base::FilePath("/system/path/1"), + base::FilePath("/system/path/2"))); + } + + // Test that if neither $XDG_DATA_HOME nor $HOME are specified, it still + // succeeds. + { + MockEnvironment env; + env.Set("XDG_DATA_DIRS", "/system/path/1:/system/path/2"); + EXPECT_THAT( + ShellIntegrationLinux::GetDataSearchLocations(&env), + ElementsAre(base::FilePath("/system/path/1"), + base::FilePath("/system/path/2"))); + } + + // Test that $XDG_DATA_DIRS falls back to the two default paths. + { + MockEnvironment env; + env.Set("HOME", "/home/user"); + env.Set("XDG_DATA_HOME", "/user/path"); + EXPECT_THAT( + ShellIntegrationLinux::GetDataSearchLocations(&env), + ElementsAre(base::FilePath("/user/path"), + base::FilePath("/usr/local/share"), + base::FilePath("/usr/share"))); + } +} + TEST(ShellIntegrationTest, GetExistingShortcutLocations) { base::FilePath kProfilePath("Profile 1"); const char kExtensionId[] = "test_extension"; @@ -288,6 +375,45 @@ TEST(ShellIntegrationTest, GetExistingShortcutContents) { } } +TEST(ShellIntegrationTest, GetExtensionShortcutFilename) { + base::FilePath kProfilePath("a/b/c/Profile Name?"); + const char kExtensionId[] = "extensionid"; + EXPECT_EQ(base::FilePath("chrome-extensionid-Profile_Name_.desktop"), + ShellIntegrationLinux::GetExtensionShortcutFilename( + kProfilePath, kExtensionId)); +} + +TEST(ShellIntegrationTest, GetExistingProfileShortcutFilenames) { + base::FilePath kProfilePath("a/b/c/Profile Name?"); + const char kApp1Filename[] = "chrome-extension1-Profile_Name_.desktop"; + const char kApp2Filename[] = "chrome-extension2-Profile_Name_.desktop"; + const char kUnrelatedAppFilename[] = "chrome-extension-Other_Profile.desktop"; + + base::MessageLoop message_loop; + content::TestBrowserThread file_thread(BrowserThread::FILE, &message_loop); + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + ASSERT_EQ(0, + file_util::WriteFile( + temp_dir.path().AppendASCII(kApp1Filename), "", 0)); + ASSERT_EQ(0, + file_util::WriteFile( + temp_dir.path().AppendASCII(kApp2Filename), "", 0)); + // This file should not be returned in the results. + ASSERT_EQ(0, + file_util::WriteFile( + temp_dir.path().AppendASCII(kUnrelatedAppFilename), "", 0)); + std::vector<base::FilePath> paths = + ShellIntegrationLinux::GetExistingProfileShortcutFilenames( + kProfilePath, temp_dir.path()); + // Path order is arbitrary. Sort the output for consistency. + std::sort(paths.begin(), paths.end()); + EXPECT_THAT(paths, + ElementsAre(base::FilePath(kApp1Filename), + base::FilePath(kApp2Filename))); +} + TEST(ShellIntegrationTest, GetWebShortcutFilename) { const struct { const base::FilePath::CharType* path; diff --git a/chrome/browser/web_applications/web_app_linux.cc b/chrome/browser/web_applications/web_app_linux.cc index 9b298fc..4371597 100644 --- a/chrome/browser/web_applications/web_app_linux.cc +++ b/chrome/browser/web_applications/web_app_linux.cc @@ -56,7 +56,7 @@ void UpdatePlatformShortcuts( } void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) { - // TODO(mgiuca): Implement this on Linux. + ShellIntegrationLinux::DeleteAllDesktopShortcuts(profile_path); } } // namespace internals |