Commit 0b7df36d authored by benwells@chromium.org's avatar benwells@chromium.org

Remove app shortcuts when app is uninstalled on Linux.

To support this, shortcut creation on Linux for extensions has been modified so that the filename encodes the extension ID and the profile. Also, when creating shortcuts any existing shortcuts are removed first.

Web page shortcuts are not affected.

BUG=130456
TEST=Test uninstalling apps removes their shortcuts; test uninstalling
apps is not broken in any way; test shortcuts for web apps
are not broken in any way.


Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=146065

Review URL: https://chromiumcodereview.appspot.com/10698114

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146094 0039d316-1c4b-4281-b951-d872f2087c98
parent fd9a6331
...@@ -36,6 +36,8 @@ AppShortcutManager::AppShortcutManager(Profile* profile) ...@@ -36,6 +36,8 @@ AppShortcutManager::AppShortcutManager(Profile* profile)
tracker_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { tracker_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
content::Source<Profile>(profile_)); content::Source<Profile>(profile_));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
content::Source<Profile>(profile_));
} }
void AppShortcutManager::OnImageLoaded(const gfx::Image& image, void AppShortcutManager::OnImageLoaded(const gfx::Image& image,
...@@ -61,15 +63,28 @@ void AppShortcutManager::OnImageLoaded(const gfx::Image& image, ...@@ -61,15 +63,28 @@ void AppShortcutManager::OnImageLoaded(const gfx::Image& image,
void AppShortcutManager::Observe(int type, void AppShortcutManager::Observe(int type,
const content::NotificationSource& source, const content::NotificationSource& source,
const content::NotificationDetails& details) { const content::NotificationDetails& details) {
DCHECK(type == chrome::NOTIFICATION_EXTENSION_INSTALLED); #if !defined(OS_MACOSX)
#if !defined(OS_MACOSX) switch (type) {
const Extension* extension = content::Details<const Extension>( case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
details).ptr(); const Extension* extension = content::Details<const Extension>(
if (!disable_shortcut_creation_for_tests && details).ptr();
extension->is_platform_app() && if (!disable_shortcut_creation_for_tests &&
extension->location() != Extension::LOAD) extension->is_platform_app() &&
InstallApplicationShortcuts(extension); extension->location() != Extension::LOAD) {
#endif InstallApplicationShortcuts(extension);
}
break;
}
case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
std::string extension_id = *content::Details<std::string>(details).ptr();
if (!disable_shortcut_creation_for_tests)
web_app::DeleteAllShortcuts(profile_->GetPath(), extension_id);
break;
}
default:
NOTREACHED();
}
#endif
} }
// static // static
......
...@@ -154,6 +154,12 @@ bool CreateShortcutOnDesktop(const FilePath& shortcut_filename, ...@@ -154,6 +154,12 @@ bool CreateShortcutOnDesktop(const FilePath& shortcut_filename,
return true; return true;
} }
void DeleteShortcutOnDesktop(const FilePath& shortcut_filename) {
FilePath desktop_path;
if (PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path))
file_util::Delete(desktop_path.Append(shortcut_filename), false);
}
bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename,
const std::string& contents) { const std::string& contents) {
ScopedTempDir temp_dir; ScopedTempDir temp_dir;
...@@ -183,6 +189,22 @@ bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename, ...@@ -183,6 +189,22 @@ bool CreateShortcutInApplicationsMenu(const FilePath& shortcut_filename,
return exit_code == 0; return exit_code == 0;
} }
void DeleteShortcutInApplicationsMenu(const FilePath& shortcut_filename) {
std::vector<std::string> argv;
argv.push_back("xdg-desktop-menu");
argv.push_back("uninstall");
// Uninstall in user mode, to match the install.
argv.push_back("--mode");
argv.push_back("user");
// The file does not need to exist anywhere - xdg-desktop-menu will uninstall
// items from the menu with a matching name.
argv.push_back(shortcut_filename.value());
int exit_code;
LaunchXdgUtility(argv, &exit_code);
}
// Quote a string such that it appears as one verbatim argument for the Exec // Quote a string such that it appears as one verbatim argument for the Exec
// key in a desktop file. // key in a desktop file.
std::string QuoteArgForDesktopFileExec(const std::string& arg) { std::string QuoteArgForDesktopFileExec(const std::string& arg) {
...@@ -455,7 +477,7 @@ bool GetDesktopShortcutTemplate(base::Environment* env, ...@@ -455,7 +477,7 @@ bool GetDesktopShortcutTemplate(base::Environment* env,
return false; return false;
} }
FilePath GetDesktopShortcutFilename(const GURL& url) { FilePath GetWebShortcutFilename(const GURL& url) {
// Use a prefix, because xdg-desktop-menu requires it. // Use a prefix, because xdg-desktop-menu requires it.
std::string filename = std::string filename =
std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec(); std::string(chrome::kBrowserProcessExecutableName) + "-" + url.spec();
...@@ -479,6 +501,20 @@ FilePath GetDesktopShortcutFilename(const GURL& url) { ...@@ -479,6 +501,20 @@ FilePath GetDesktopShortcutFilename(const GURL& url) {
return FilePath(); return FilePath();
} }
FilePath GetExtensionShortcutFilename(const FilePath& profile_path,
const std::string& extension_id) {
DCHECK(!extension_id.empty());
// Use a prefix, because xdg-desktop-menu requires it.
std::string filename(chrome::kBrowserProcessExecutableName);
filename.append("-")
.append(extension_id)
.append("-")
.append(profile_path.BaseName().value());
file_util::ReplaceIllegalCharactersInPath(&filename, '_');
return FilePath(filename.append(".desktop"));
}
std::string GetDesktopFileContents( std::string GetDesktopFileContents(
const std::string& template_contents, const std::string& template_contents,
const std::string& app_name, const std::string& app_name,
...@@ -597,8 +633,19 @@ bool CreateDesktopShortcut( ...@@ -597,8 +633,19 @@ bool CreateDesktopShortcut(
const std::string& shortcut_template) { const std::string& shortcut_template) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
FilePath shortcut_filename = FilePath shortcut_filename;
ShellIntegrationLinux::GetDesktopShortcutFilename(shortcut_info.url); if (!shortcut_info.extension_id.empty()) {
shortcut_filename = GetExtensionShortcutFilename(
shortcut_info.profile_path, shortcut_info.extension_id);
// For extensions we do not want duplicate shortcuts. So, delete any that
// already exist and replace them.
if (shortcut_info.create_on_desktop)
DeleteShortcutOnDesktop(shortcut_filename);
if (shortcut_info.create_in_applications_menu)
DeleteShortcutInApplicationsMenu(shortcut_filename);
} else {
shortcut_filename = GetWebShortcutFilename(shortcut_info.url);
}
if (shortcut_filename.empty()) if (shortcut_filename.empty())
return false; return false;
...@@ -629,4 +676,16 @@ bool CreateDesktopShortcut( ...@@ -629,4 +676,16 @@ bool CreateDesktopShortcut(
return success; return success;
} }
void DeleteDesktopShortcuts(const FilePath& profile_path,
const std::string& extension_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
FilePath shortcut_filename = GetExtensionShortcutFilename(
profile_path, extension_id);
DCHECK(!shortcut_filename.empty());
DeleteShortcutOnDesktop(shortcut_filename);
DeleteShortcutInApplicationsMenu(shortcut_filename);
}
} // namespace ShellIntegrationLinux } // namespace ShellIntegrationLinux
...@@ -26,7 +26,12 @@ bool GetDesktopShortcutTemplate(base::Environment* env, ...@@ -26,7 +26,12 @@ bool GetDesktopShortcutTemplate(base::Environment* env,
std::string* output); std::string* output);
// Returns filename for .desktop file based on |url|, sanitized for security. // Returns filename for .desktop file based on |url|, sanitized for security.
FilePath GetDesktopShortcutFilename(const GURL& url); FilePath GetWebShortcutFilename(const GURL& url);
// Returns filename for .desktop file based on |profile_path| and
// |extension_id|, sanitized for security.
FilePath GetExtensionShortcutFilename(const FilePath& profile_path,
const std::string& extension_id);
// Returns contents for .desktop file based on |template_contents|, |url| // Returns contents for .desktop file based on |template_contents|, |url|
// and |title|. The |template_contents| should be contents of .desktop file // and |title|. The |template_contents| should be contents of .desktop file
...@@ -41,9 +46,20 @@ std::string GetDesktopFileContents(const std::string& template_contents, ...@@ -41,9 +46,20 @@ std::string GetDesktopFileContents(const std::string& template_contents,
const std::string& icon_name, const std::string& icon_name,
const FilePath& profile_path); const FilePath& profile_path);
// Create shortcuts on the desktop or in the application menu (as specified by
// |shortcut_info|), for the web page or extension in |shortcut_info|. Use the
// shortcut template contained in |shortcut_template|.
// For extensions, duplicate shortcuts are avoided, so if a requested shortcut
// already exists it is deleted first.
bool CreateDesktopShortcut(const ShellIntegration::ShortcutInfo& shortcut_info, bool CreateDesktopShortcut(const ShellIntegration::ShortcutInfo& shortcut_info,
const std::string& shortcut_template); const std::string& shortcut_template);
// Delete any desktop shortcuts on desktop or in the application menu that have
// been added for the extension with |extension_id| in |profile_path|.
void DeleteDesktopShortcuts(const FilePath& profile_path,
const std::string& extension_id);
} // namespace ShellIntegrationLinux } // namespace ShellIntegrationLinux
#endif // CHROME_BROWSER_SHELL_INTEGRATION_LINUX_H_ #endif // CHROME_BROWSER_SHELL_INTEGRATION_LINUX_H_
...@@ -139,7 +139,7 @@ TEST(ShellIntegrationTest, GetDesktopShortcutTemplate) { ...@@ -139,7 +139,7 @@ TEST(ShellIntegrationTest, GetDesktopShortcutTemplate) {
} }
} }
TEST(ShellIntegrationTest, GetDesktopShortcutFilename) { TEST(ShellIntegrationTest, GetWebShortcutFilename) {
const struct { const struct {
const FilePath::CharType* path; const FilePath::CharType* path;
const char* url; const char* url;
...@@ -156,7 +156,7 @@ TEST(ShellIntegrationTest, GetDesktopShortcutFilename) { ...@@ -156,7 +156,7 @@ TEST(ShellIntegrationTest, GetDesktopShortcutFilename) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) {
EXPECT_EQ(std::string(chrome::kBrowserProcessExecutableName) + "-" + EXPECT_EQ(std::string(chrome::kBrowserProcessExecutableName) + "-" +
test_cases[i].path, test_cases[i].path,
ShellIntegrationLinux::GetDesktopShortcutFilename( ShellIntegrationLinux::GetWebShortcutFilename(
GURL(test_cases[i].url)).value()) << GURL(test_cases[i].url)).value()) <<
" while testing " << test_cases[i].url; " while testing " << test_cases[i].url;
} }
......
...@@ -127,8 +127,18 @@ void CreateShortcut( ...@@ -127,8 +127,18 @@ void CreateShortcut(
BrowserThread::FILE, BrowserThread::FILE,
FROM_HERE, FROM_HERE,
base::Bind(base::IgnoreResult(&CreateShortcutOnFileThread), base::Bind(base::IgnoreResult(&CreateShortcutOnFileThread),
profile_path, profile_path, shortcut_info));
shortcut_info)); }
void DeleteAllShortcuts(const FilePath& profile_path,
const std::string& extension_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&internals::DeletePlatformShortcuts, profile_path,
extension_id));
} }
bool CreateShortcutOnFileThread( bool CreateShortcutOnFileThread(
......
...@@ -54,6 +54,11 @@ void CreateShortcut( ...@@ -54,6 +54,11 @@ void CreateShortcut(
const FilePath& profile_path, const FilePath& profile_path,
const ShellIntegration::ShortcutInfo& shortcut_info); const ShellIntegration::ShortcutInfo& shortcut_info);
// Delete all the shortcuts that have been created for the extension with
// |extension_id| in the profile with |profile_path|.
void DeleteAllShortcuts(const FilePath& profile_path,
const std::string& extension_id);
// Creates a shortcut. Must be called on the file thread. This is used to // Creates a shortcut. Must be called on the file thread. This is used to
// implement CreateShortcut() above, and can also be used directly from the // implement CreateShortcut() above, and can also be used directly from the
// file thread. |profile_path| is the path of the creating profile. // file thread. |profile_path| is the path of the creating profile.
...@@ -97,6 +102,12 @@ bool CreatePlatformShortcut( ...@@ -97,6 +102,12 @@ bool CreatePlatformShortcut(
const FilePath& profile_path, const FilePath& profile_path,
const ShellIntegration::ShortcutInfo& shortcut_info); const ShellIntegration::ShortcutInfo& shortcut_info);
// Delete all the shortcuts we have added for this extension. This is the
// platform specific implementation of the DeleteAllShortcuts function, and
// is executed on the FILE thread..
void DeletePlatformShortcuts(const FilePath& profile_path,
const std::string& extension_id);
// Sanitizes |name| and returns a version of it that is safe to use as an // Sanitizes |name| and returns a version of it that is safe to use as an
// on-disk file name . // on-disk file name .
FilePath GetSanitizedFileName(const string16& name); FilePath GetSanitizedFileName(const string16& name);
......
...@@ -29,5 +29,10 @@ bool CreatePlatformShortcut( ...@@ -29,5 +29,10 @@ bool CreatePlatformShortcut(
shortcut_info, shortcut_template); shortcut_info, shortcut_template);
} }
void DeletePlatformShortcuts(const FilePath& profile_path,
const std::string& extension_id) {
ShellIntegrationLinux::DeleteDesktopShortcuts(profile_path, extension_id);
}
} // namespace internals } // namespace internals
} // namespace web_app } // namespace web_app
...@@ -262,5 +262,11 @@ bool CreatePlatformShortcut( ...@@ -262,5 +262,11 @@ bool CreatePlatformShortcut(
return shortcut_creator.CreateShortcut(); return shortcut_creator.CreateShortcut();
} }
void DeletePlatformShortcuts(const FilePath& profile_path,
const std::string& extension_id) {
// TODO(benwells): Implement this when shortcuts / weblings are enabled on
// mac.
}
} // namespace internals } // namespace internals
} // namespace web_app } // namespace web_app
...@@ -255,6 +255,11 @@ bool CreatePlatformShortcut( ...@@ -255,6 +255,11 @@ bool CreatePlatformShortcut(
return success; return success;
} }
void DeletePlatformShortcuts(const FilePath& profile_path,
const std::string& extension_id) {
// TODO(benwells): Implement this.
}
} // namespace internals } // namespace internals
} // namespace web_app } // namespace web_app
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment