diff options
-rw-r--r-- | chrome/app/generated_resources.grd | 3 | ||||
-rw-r--r-- | chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc | 16 | ||||
-rw-r--r-- | chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h | 1 | ||||
-rw-r--r-- | chrome/browser/shell_integration.h | 18 | ||||
-rw-r--r-- | chrome/browser/shell_integration_linux.cc | 124 | ||||
-rw-r--r-- | chrome/browser/shell_integration_unittest.cc | 9 |
6 files changed, 116 insertions, 55 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index f7b250b..96bfc63 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -1808,6 +1808,9 @@ each locale. --> <message name="IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX" desc="Label of the checkbox to create an application shortcut on the desktop."> Desktop </message> + <message name="IDS_CREATE_SHORTCUTS_MENU_CHKBOX" desc="Label of the checkbox to create an application shortcut in the system's applications menu."> + Applications menu + </message> <message name="IDS_CREATE_SHORTCUTS_COMMIT" desc="Title of the button to actually create the shortcuts."> Create </message> diff --git a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc index 6997651..91052e2 100644 --- a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc +++ b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.cc @@ -50,6 +50,12 @@ CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk( gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true); + // Menu checkbox. + menu_checkbox_ = gtk_check_button_new_with_label( + l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str()); + gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false); + g_signal_connect(dialog, "response", G_CALLBACK(HandleOnResponseDialog), this); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); @@ -59,8 +65,14 @@ CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk( void CreateApplicationShortcutsDialogGtk::OnDialogResponse(GtkWidget* widget, int response) { if (response == GTK_RESPONSE_ACCEPT) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_))) - ShellIntegration::CreateDesktopShortcut(url_, title_); + ShellIntegration::ShortcutInfo shortcut_info; + shortcut_info.url = url_; + shortcut_info.title = title_; + shortcut_info.create_on_desktop = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)); + shortcut_info.create_in_applications_menu = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_)); + ShellIntegration::CreateDesktopShortcut(shortcut_info); } delete this; diff --git a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h index 79b54306..f1b9081 100644 --- a/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h +++ b/chrome/browser/gtk/create_application_shortcuts_dialog_gtk.h @@ -33,6 +33,7 @@ class CreateApplicationShortcutsDialogGtk { // UI elements. GtkWidget* desktop_checkbox_; + GtkWidget* menu_checkbox_; // Target URL of the shortcut. GURL url_; diff --git a/chrome/browser/shell_integration.h b/chrome/browser/shell_integration.h index 9abfadd..2bc955c 100644 --- a/chrome/browser/shell_integration.h +++ b/chrome/browser/shell_integration.h @@ -10,9 +10,9 @@ #include "base/basictypes.h" #include "base/ref_counted.h" #include "base/string16.h" +#include "googleurl/src/gurl.h" class FilePath; -class GURL; class MessageLoop; class ShellIntegration { @@ -42,10 +42,18 @@ class ShellIntegration { const std::string& template_contents, const GURL& url, const string16& title); - // Creates a desktop shortcut for |url| with |title|. It is not guaranteed - // to exist immediately after returning from this function, because actual - // file operation is done on the file thread. - static void CreateDesktopShortcut(const GURL& url, const string16& title); + struct ShortcutInfo { + GURL url; + string16 title; + + bool create_on_desktop; + bool create_in_applications_menu; + }; + + // Creates a desktop shortcut. It is not guaranteed to exist immediately after + // returning from this function, because actual file operation is done on the + // file thread. + static void CreateDesktopShortcut(const ShortcutInfo& shortcut_info); #endif // defined(OS_LINUX) // The current default browser UI state diff --git a/chrome/browser/shell_integration_linux.cc b/chrome/browser/shell_integration_linux.cc index ea38c57..c7077d1 100644 --- a/chrome/browser/shell_integration_linux.cc +++ b/chrome/browser/shell_integration_linux.cc @@ -10,18 +10,22 @@ #include <sys/types.h> #include <unistd.h> +#include <string> #include <vector> +#include "base/command_line.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/process_util.h" +#include "base/scoped_temp_dir.h" #include "base/string_tokenizer.h" #include "base/string_util.h" #include "base/task.h" #include "base/thread.h" #include "chrome/browser/browser_process.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "googleurl/src/gurl.h" @@ -45,6 +49,33 @@ const char* GetDesktopName() { #endif } +// Helper to launch xdg scripts. We don't want them to ask any questions on the +// terminal etc. +bool LaunchXdgUtility(const std::vector<std::string>& argv) { + // xdg-settings internally runs xdg-mime, which uses mv to move newly-created + // files on top of originals after making changes to them. In the event that + // the original files are owned by another user (e.g. root, which can happen + // if they are updated within sudo), mv will prompt the user to confirm if + // standard input is a terminal (otherwise it just does it). So make sure it's + // not, to avoid locking everything up waiting for mv. + int devnull = open("/dev/null", O_RDONLY); + if (devnull < 0) + return false; + base::file_handle_mapping_vector no_stdin; + no_stdin.push_back(std::make_pair(devnull, STDIN_FILENO)); + + base::ProcessHandle handle; + if (!base::LaunchApp(argv, no_stdin, false, &handle)) { + close(devnull); + return false; + } + close(devnull); + + int success_code; + base::WaitForExitCode(handle, &success_code); + return success_code == EXIT_SUCCESS; +} + bool GetDesktopShortcutTemplate(std::string* output) { std::vector<std::string> search_paths; @@ -74,38 +105,61 @@ bool GetDesktopShortcutTemplate(std::string* output) { class CreateDesktopShortcutTask : public Task { public: - CreateDesktopShortcutTask(const GURL& url, const string16& title) - : url_(url), - title_(title) { + CreateDesktopShortcutTask(const ShellIntegration::ShortcutInfo& shortcut_info) + : shortcut_info_(shortcut_info) { } virtual void Run() { // TODO(phajdan.jr): Report errors from this function, possibly as infobars. - FilePath desktop_path; - if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) - return; - desktop_path = - desktop_path.Append(ShellIntegration::GetDesktopShortcutFilename(url_)); - - if (file_util::PathExists(desktop_path)) - return; - std::string template_contents; if (!GetDesktopShortcutTemplate(&template_contents)) return; std::string contents = ShellIntegration::GetDesktopFileContents( - template_contents, url_, title_); - int bytes_written = file_util::WriteFile(desktop_path, contents.data(), + template_contents, shortcut_info_.url, shortcut_info_.title); + + ScopedTempDir temp_dir; + if (!temp_dir.CreateUniqueTempDir()) + return; + + FilePath shortcut_filename = + ShellIntegration::GetDesktopShortcutFilename(shortcut_info_.url); + + FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); + + int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), contents.length()); - if (bytes_written != static_cast<int>(contents.length())) { - file_util::Delete(desktop_path, false); + + if (bytes_written != static_cast<int>(contents.length())) + return; + + if (shortcut_info_.create_on_desktop) { + FilePath desktop_path; + if (!PathService::Get(chrome::DIR_USER_DESKTOP, &desktop_path)) + return; + desktop_path = desktop_path.Append(shortcut_filename); + + if (!file_util::PathExists(desktop_path)) + file_util::CopyFile(temp_file_path, desktop_path); + } + + if (shortcut_info_.create_in_applications_menu) { + std::vector<std::string> argv; + argv.push_back("xdg-desktop-menu"); + argv.push_back("install"); + + // Always install in user mode, even if someone runs the browser as root + // (people do that). + argv.push_back("--mode"); + argv.push_back("user"); + + argv.push_back(temp_file_path.value()); + LaunchXdgUtility(argv); } } private: - const GURL url_; // URL of the web application. - const string16 title_; // Title displayed to the user. + const ShellIntegration::ShortcutInfo shortcut_info_; DISALLOW_COPY_AND_ASSIGN(CreateDesktopShortcutTask); }; @@ -123,29 +177,7 @@ bool ShellIntegration::SetAsDefaultBrowser() { argv.push_back("set"); argv.push_back("default-web-browser"); argv.push_back(GetDesktopName()); - - // xdg-settings internally runs xdg-mime, which uses mv to move newly-created - // files on top of originals after making changes to them. In the event that - // the original files are owned by another user (e.g. root, which can happen - // if they are updated within sudo), mv will prompt the user to confirm if - // standard input is a terminal (otherwise it just does it). So make sure it's - // not, to avoid locking everything up waiting for mv. - int devnull = open("/dev/null", O_RDONLY); - if (devnull < 0) - return false; - base::file_handle_mapping_vector no_stdin; - no_stdin.push_back(std::make_pair(devnull, STDIN_FILENO)); - - base::ProcessHandle handle; - if (!base::LaunchApp(argv, no_stdin, false, &handle)) { - close(devnull); - return false; - } - close(devnull); - - int success_code; - base::WaitForExitCode(handle, &success_code); - return success_code == EXIT_SUCCESS; + return LaunchXdgUtility(argv); } bool ShellIntegration::IsDefaultBrowser() { @@ -181,7 +213,9 @@ bool ShellIntegration::IsFirefoxDefaultBrowser() { } FilePath ShellIntegration::GetDesktopShortcutFilename(const GURL& url) { - std::wstring filename = UTF8ToWide(url.spec()) + L".desktop"; + // Use a prefix, because xdg-desktop-menu requires it. + std::wstring filename = std::wstring(chrome::kBrowserProcessExecutableName) + + L"-" + UTF8ToWide(url.spec()) + L".desktop"; file_util::ReplaceIllegalCharacters(&filename, '_'); // Return BaseName to be absolutely sure we're not vulnerable to a directory @@ -231,8 +265,8 @@ std::string ShellIntegration::GetDesktopFileContents( return output_buffer; } -void ShellIntegration::CreateDesktopShortcut(const GURL& url, - const string16& title) { +void ShellIntegration::CreateDesktopShortcut( + const ShortcutInfo& shortcut_info) { g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, - new CreateDesktopShortcutTask(url, title)); + new CreateDesktopShortcutTask(shortcut_info)); } diff --git a/chrome/browser/shell_integration_unittest.cc b/chrome/browser/shell_integration_unittest.cc index fe01fc1..6f0d71d 100644 --- a/chrome/browser/shell_integration_unittest.cc +++ b/chrome/browser/shell_integration_unittest.cc @@ -6,6 +6,7 @@ #include "base/file_path.h" #include "base/string_util.h" +#include "chrome/common/chrome_constants.h" #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,9 +28,11 @@ TEST(ShellIntegrationTest, GetDesktopShortcutFilename) { { FPL("http___.._.desktop"), "http://../../../../" }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); i++) { - EXPECT_EQ(test_cases[i].path, ShellIntegration::GetDesktopShortcutFilename( - GURL(test_cases[i].url)).value()) << " while testing " << - test_cases[i].url; + EXPECT_EQ(WideToASCII(chrome::kBrowserProcessExecutableName) + "-" + + test_cases[i].path, + ShellIntegration::GetDesktopShortcutFilename( + GURL(test_cases[i].url)).value()) << + " while testing " << test_cases[i].url; } } |