| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/shell_integration_linux.h" | 5 #include "chrome/browser/shell_integration_linux.h" |
| 6 | 6 |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <glib.h> | 8 #include <glib.h> |
| 9 #include <stdlib.h> | 9 #include <stdlib.h> |
| 10 #include <sys/stat.h> | 10 #include <sys/stat.h> |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 | 166 |
| 167 return true; | 167 return true; |
| 168 } | 168 } |
| 169 | 169 |
| 170 void DeleteShortcutOnDesktop(const base::FilePath& shortcut_filename) { | 170 void DeleteShortcutOnDesktop(const base::FilePath& shortcut_filename) { |
| 171 base::FilePath desktop_path; | 171 base::FilePath desktop_path; |
| 172 if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) | 172 if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) |
| 173 file_util::Delete(desktop_path.Append(shortcut_filename), false); | 173 file_util::Delete(desktop_path.Append(shortcut_filename), false); |
| 174 } | 174 } |
| 175 | 175 |
| 176 // Creates a shortcut with |shortcut_filename| and |contents| in the system |
| 177 // applications menu. If |directory_filename| is non-empty, creates a sub-menu |
| 178 // with |directory_filename| and |directory_contents|, and stores the shortcut |
| 179 // under the sub-menu. |
| 176 bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, | 180 bool CreateShortcutInApplicationsMenu(const base::FilePath& shortcut_filename, |
| 177 const std::string& contents) { | 181 const std::string& contents, |
| 182 const base::FilePath& directory_filename, |
| 183 const std::string& directory_contents) { |
| 178 base::ScopedTempDir temp_dir; | 184 base::ScopedTempDir temp_dir; |
| 179 if (!temp_dir.CreateUniqueTempDir()) | 185 if (!temp_dir.CreateUniqueTempDir()) |
| 180 return false; | 186 return false; |
| 181 | 187 |
| 188 base::FilePath temp_directory_path; |
| 189 if (!directory_filename.empty()) { |
| 190 temp_directory_path = temp_dir.path().Append(directory_filename); |
| 191 |
| 192 int bytes_written = file_util::WriteFile(temp_directory_path, |
| 193 directory_contents.data(), |
| 194 directory_contents.length()); |
| 195 |
| 196 if (bytes_written != static_cast<int>(directory_contents.length())) |
| 197 return false; |
| 198 } |
| 199 |
| 182 base::FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); | 200 base::FilePath temp_file_path = temp_dir.path().Append(shortcut_filename); |
| 183 | 201 |
| 184 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), | 202 int bytes_written = file_util::WriteFile(temp_file_path, contents.data(), |
| 185 contents.length()); | 203 contents.length()); |
| 186 | 204 |
| 187 if (bytes_written != static_cast<int>(contents.length())) | 205 if (bytes_written != static_cast<int>(contents.length())) |
| 188 return false; | 206 return false; |
| 189 | 207 |
| 190 std::vector<std::string> argv; | 208 std::vector<std::string> argv; |
| 191 argv.push_back("xdg-desktop-menu"); | 209 argv.push_back("xdg-desktop-menu"); |
| 192 argv.push_back("install"); | 210 argv.push_back("install"); |
| 193 | 211 |
| 194 // Always install in user mode, even if someone runs the browser as root | 212 // Always install in user mode, even if someone runs the browser as root |
| 195 // (people do that). | 213 // (people do that). |
| 196 argv.push_back("--mode"); | 214 argv.push_back("--mode"); |
| 197 argv.push_back("user"); | 215 argv.push_back("user"); |
| 198 | 216 |
| 217 // If provided, install the shortcut file inside the given directory. |
| 218 if (!directory_filename.empty()) |
| 219 argv.push_back(temp_directory_path.value()); |
| 199 argv.push_back(temp_file_path.value()); | 220 argv.push_back(temp_file_path.value()); |
| 200 int exit_code; | 221 int exit_code; |
| 201 LaunchXdgUtility(argv, &exit_code); | 222 LaunchXdgUtility(argv, &exit_code); |
| 202 return exit_code == 0; | 223 return exit_code == 0; |
| 203 } | 224 } |
| 204 | 225 |
| 205 void DeleteShortcutInApplicationsMenu(const base::FilePath& shortcut_filename) { | 226 void DeleteShortcutInApplicationsMenu( |
| 227 const base::FilePath& shortcut_filename, |
| 228 const base::FilePath& directory_filename) { |
| 206 std::vector<std::string> argv; | 229 std::vector<std::string> argv; |
| 207 argv.push_back("xdg-desktop-menu"); | 230 argv.push_back("xdg-desktop-menu"); |
| 208 argv.push_back("uninstall"); | 231 argv.push_back("uninstall"); |
| 209 | 232 |
| 210 // Uninstall in user mode, to match the install. | 233 // Uninstall in user mode, to match the install. |
| 211 argv.push_back("--mode"); | 234 argv.push_back("--mode"); |
| 212 argv.push_back("user"); | 235 argv.push_back("user"); |
| 213 | 236 |
| 214 // The file does not need to exist anywhere - xdg-desktop-menu will uninstall | 237 // The file does not need to exist anywhere - xdg-desktop-menu will uninstall |
| 215 // items from the menu with a matching name. | 238 // items from the menu with a matching name. |
| 239 // If |directory_filename| is supplied, this will also remove the item from |
| 240 // the directory, and remove the directory if it is empty. |
| 241 if (!directory_filename.empty()) |
| 242 argv.push_back(directory_filename.value()); |
| 216 argv.push_back(shortcut_filename.value()); | 243 argv.push_back(shortcut_filename.value()); |
| 217 int exit_code; | 244 int exit_code; |
| 218 LaunchXdgUtility(argv, &exit_code); | 245 LaunchXdgUtility(argv, &exit_code); |
| 219 } | 246 } |
| 220 | 247 |
| 221 // Quote a string such that it appears as one verbatim argument for the Exec | 248 // Quote a string such that it appears as one verbatim argument for the Exec |
| 222 // key in a desktop file. | 249 // key in a desktop file. |
| 223 std::string QuoteArgForDesktopFileExec(const std::string& arg) { | 250 std::string QuoteArgForDesktopFileExec(const std::string& arg) { |
| 224 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html | 251 // http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html |
| 225 | 252 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 247 } | 274 } |
| 248 | 275 |
| 249 const char kDesktopEntry[] = "Desktop Entry"; | 276 const char kDesktopEntry[] = "Desktop Entry"; |
| 250 | 277 |
| 251 const char kXdgOpenShebang[] = "#!/usr/bin/env xdg-open"; | 278 const char kXdgOpenShebang[] = "#!/usr/bin/env xdg-open"; |
| 252 | 279 |
| 253 const char kXdgSettings[] = "xdg-settings"; | 280 const char kXdgSettings[] = "xdg-settings"; |
| 254 const char kXdgSettingsDefaultBrowser[] = "default-web-browser"; | 281 const char kXdgSettingsDefaultBrowser[] = "default-web-browser"; |
| 255 const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler"; | 282 const char kXdgSettingsDefaultSchemeHandler[] = "default-url-scheme-handler"; |
| 256 | 283 |
| 284 const char kDirectoryFilename[] = "chrome-apps.directory"; |
| 285 |
| 257 } // namespace | 286 } // namespace |
| 258 | 287 |
| 259 namespace { | 288 namespace { |
| 260 | 289 |
| 261 // Utility function to get the path to the version of a script shipped with | 290 // Utility function to get the path to the version of a script shipped with |
| 262 // Chrome. |script| gives the name of the script. |chrome_version| returns the | 291 // Chrome. |script| gives the name of the script. |chrome_version| returns the |
| 263 // path to the Chrome version of the script, and the return value of the | 292 // path to the Chrome version of the script, and the return value of the |
| 264 // function is true if the function is successful and the Chrome version is | 293 // function is true if the function is successful and the Chrome version is |
| 265 // not the script found on the PATH. | 294 // not the script found on the PATH. |
| 266 bool GetChromeVersionOfScript(const std::string& script, | 295 bool GetChromeVersionOfScript(const std::string& script, |
| (...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 687 } else { | 716 } else { |
| 688 output_buffer += data_dump; | 717 output_buffer += data_dump; |
| 689 } | 718 } |
| 690 g_free(data_dump); | 719 g_free(data_dump); |
| 691 } | 720 } |
| 692 | 721 |
| 693 g_key_file_free(key_file); | 722 g_key_file_free(key_file); |
| 694 return output_buffer; | 723 return output_buffer; |
| 695 } | 724 } |
| 696 | 725 |
| 726 std::string GetDirectoryFileContents(const string16& title, |
| 727 const std::string& icon_name) { |
| 728 // See http://standards.freedesktop.org/desktop-entry-spec/latest/ |
| 729 GKeyFile* key_file = g_key_file_new(); |
| 730 |
| 731 g_key_file_set_string(key_file, kDesktopEntry, "Version", "1.0"); |
| 732 g_key_file_set_string(key_file, kDesktopEntry, "Type", "Directory"); |
| 733 std::string final_title = UTF16ToUTF8(title); |
| 734 g_key_file_set_string(key_file, kDesktopEntry, "Name", final_title.c_str()); |
| 735 if (!icon_name.empty()) { |
| 736 g_key_file_set_string(key_file, kDesktopEntry, "Icon", icon_name.c_str()); |
| 737 } else { |
| 738 g_key_file_set_string(key_file, kDesktopEntry, "Icon", |
| 739 GetIconName().c_str()); |
| 740 } |
| 741 |
| 742 gsize length = 0; |
| 743 gchar* data_dump = g_key_file_to_data(key_file, &length, NULL); |
| 744 std::string output_buffer; |
| 745 if (data_dump) { |
| 746 // If strlen(data_dump[0]) == 0, this check will fail. |
| 747 if (data_dump[0] == '\n') { |
| 748 // Older versions of glib produce a leading newline. If this is the case, |
| 749 // remove it to avoid double-newline after the shebang. |
| 750 output_buffer += (data_dump + 1); |
| 751 } else { |
| 752 output_buffer += data_dump; |
| 753 } |
| 754 g_free(data_dump); |
| 755 } |
| 756 |
| 757 g_key_file_free(key_file); |
| 758 return output_buffer; |
| 759 } |
| 760 |
| 697 bool CreateDesktopShortcut( | 761 bool CreateDesktopShortcut( |
| 698 const ShellIntegration::ShortcutInfo& shortcut_info, | 762 const ShellIntegration::ShortcutInfo& shortcut_info, |
| 699 const ShellIntegration::ShortcutLocations& creation_locations) { | 763 const ShellIntegration::ShortcutLocations& creation_locations) { |
| 700 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 764 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 701 | 765 |
| 702 base::FilePath shortcut_filename; | 766 base::FilePath shortcut_filename; |
| 703 if (!shortcut_info.extension_id.empty()) { | 767 if (!shortcut_info.extension_id.empty()) { |
| 704 shortcut_filename = GetExtensionShortcutFilename( | 768 shortcut_filename = GetExtensionShortcutFilename( |
| 705 shortcut_info.profile_path, shortcut_info.extension_id); | 769 shortcut_info.profile_path, shortcut_info.extension_id); |
| 706 // For extensions we do not want duplicate shortcuts. So, delete any that | 770 // For extensions we do not want duplicate shortcuts. So, delete any that |
| 707 // already exist and replace them. | 771 // already exist and replace them. |
| 708 if (creation_locations.on_desktop) | 772 if (creation_locations.on_desktop) |
| 709 DeleteShortcutOnDesktop(shortcut_filename); | 773 DeleteShortcutOnDesktop(shortcut_filename); |
| 710 if (creation_locations.in_applications_menu || creation_locations.hidden) | 774 if (creation_locations.in_applications_menu || creation_locations.hidden) |
| 711 DeleteShortcutInApplicationsMenu(shortcut_filename); | 775 DeleteShortcutInApplicationsMenu(shortcut_filename, base::FilePath()); |
| 712 } else { | 776 } else { |
| 713 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); | 777 shortcut_filename = GetWebShortcutFilename(shortcut_info.url); |
| 714 } | 778 } |
| 715 if (shortcut_filename.empty()) | 779 if (shortcut_filename.empty()) |
| 716 return false; | 780 return false; |
| 717 | 781 |
| 718 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); | 782 std::string icon_name = CreateShortcutIcon(shortcut_info, shortcut_filename); |
| 719 | 783 |
| 720 std::string app_name = | 784 std::string app_name = |
| 721 web_app::GenerateApplicationNameFromInfo(shortcut_info); | 785 web_app::GenerateApplicationNameFromInfo(shortcut_info); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 739 shortcut_info.title, | 803 shortcut_info.title, |
| 740 icon_name, | 804 icon_name, |
| 741 shortcut_info.profile_path, | 805 shortcut_info.profile_path, |
| 742 false); | 806 false); |
| 743 success = CreateShortcutOnDesktop(shortcut_filename, contents); | 807 success = CreateShortcutOnDesktop(shortcut_filename, contents); |
| 744 } | 808 } |
| 745 | 809 |
| 746 // The 'in_applications_menu' and 'hidden' locations are actually the same | 810 // The 'in_applications_menu' and 'hidden' locations are actually the same |
| 747 // place ('applications'). | 811 // place ('applications'). |
| 748 if (creation_locations.in_applications_menu || creation_locations.hidden) { | 812 if (creation_locations.in_applications_menu || creation_locations.hidden) { |
| 813 base::FilePath directory_filename; |
| 814 std::string directory_contents; |
| 815 if (!creation_locations.applications_menu_subdir.empty()) { |
| 816 directory_filename = base::FilePath(kDirectoryFilename); |
| 817 directory_contents = ShellIntegrationLinux::GetDirectoryFileContents( |
| 818 creation_locations.applications_menu_subdir, ""); |
| 819 } |
| 749 // Set NoDisplay=true if hidden but not in_applications_menu. This will hide | 820 // Set NoDisplay=true if hidden but not in_applications_menu. This will hide |
| 750 // the application from user-facing menus. | 821 // the application from user-facing menus. |
| 751 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( | 822 std::string contents = ShellIntegrationLinux::GetDesktopFileContents( |
| 752 chrome_exe_path, | 823 chrome_exe_path, |
| 753 app_name, | 824 app_name, |
| 754 shortcut_info.url, | 825 shortcut_info.url, |
| 755 shortcut_info.extension_id, | 826 shortcut_info.extension_id, |
| 756 shortcut_info.extension_path, | 827 shortcut_info.extension_path, |
| 757 shortcut_info.title, | 828 shortcut_info.title, |
| 758 icon_name, | 829 icon_name, |
| 759 shortcut_info.profile_path, | 830 shortcut_info.profile_path, |
| 760 !creation_locations.in_applications_menu); | 831 !creation_locations.in_applications_menu); |
| 761 success = CreateShortcutInApplicationsMenu(shortcut_filename, contents) && | 832 success = CreateShortcutInApplicationsMenu( |
| 762 success; | 833 shortcut_filename, contents, directory_filename, directory_contents) && |
| 834 success; |
| 763 } | 835 } |
| 764 | 836 |
| 765 return success; | 837 return success; |
| 766 } | 838 } |
| 767 | 839 |
| 768 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 840 void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
| 769 const std::string& extension_id) { | 841 const std::string& extension_id) { |
| 770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 842 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 771 | 843 |
| 772 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 844 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 773 profile_path, extension_id); | 845 profile_path, extension_id); |
| 774 DCHECK(!shortcut_filename.empty()); | 846 DCHECK(!shortcut_filename.empty()); |
| 775 | 847 |
| 776 DeleteShortcutOnDesktop(shortcut_filename); | 848 DeleteShortcutOnDesktop(shortcut_filename); |
| 777 DeleteShortcutInApplicationsMenu(shortcut_filename); | 849 // Delete shortcuts from |kDirectoryFilename|. |
| 850 // Note that it is possible that shortcuts were not created in the Chrome Apps |
| 851 // directory (depending on the value of |applications_menu_subdir| when they |
| 852 // were created). It doesn't matter: this will still delete the shortcut even |
| 853 // if it isn't in the directory. |
| 854 DeleteShortcutInApplicationsMenu(shortcut_filename, |
| 855 base::FilePath(kDirectoryFilename)); |
| 778 } | 856 } |
| 779 | 857 |
| 780 } // namespace ShellIntegrationLinux | 858 } // namespace ShellIntegrationLinux |
| OLD | NEW |