Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: chrome/browser/shell_integration_linux.cc

Issue 14383003: Application shortcuts in Linux are now installed into a "Chrome Apps" folder. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: GetDirectoryFileContents needs the same \n hack as GetDesktopFileContents. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/shell_integration_linux.h ('k') | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/browser/shell_integration_linux.h ('k') | chrome/browser/shell_integration_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698