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 |