| 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 <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #include <memory> | 10 #include <memory> |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 #include "base/threading/thread.h" | 43 #include "base/threading/thread.h" |
| 44 #include "base/threading/thread_restrictions.h" | 44 #include "base/threading/thread_restrictions.h" |
| 45 #include "build/build_config.h" | 45 #include "build/build_config.h" |
| 46 #include "chrome/browser/shell_integration.h" | 46 #include "chrome/browser/shell_integration.h" |
| 47 #include "chrome/common/channel_info.h" | 47 #include "chrome/common/channel_info.h" |
| 48 #include "chrome/common/chrome_constants.h" | 48 #include "chrome/common/chrome_constants.h" |
| 49 #include "chrome/common/chrome_switches.h" | 49 #include "chrome/common/chrome_switches.h" |
| 50 #include "chrome/common/features.h" | 50 #include "chrome/common/features.h" |
| 51 #include "chrome/grit/chrome_unscaled_resources.h" | 51 #include "chrome/grit/chrome_unscaled_resources.h" |
| 52 #include "components/version_info/version_info.h" | 52 #include "components/version_info/version_info.h" |
| 53 #include "content/public/browser/browser_thread.h" | |
| 54 #include "ui/base/resource/resource_bundle.h" | 53 #include "ui/base/resource/resource_bundle.h" |
| 55 #include "ui/gfx/image/image_family.h" | 54 #include "ui/gfx/image/image_family.h" |
| 56 #include "url/gurl.h" | 55 #include "url/gurl.h" |
| 57 | 56 |
| 58 using content::BrowserThread; | |
| 59 | |
| 60 namespace shell_integration { | 57 namespace shell_integration { |
| 61 | 58 |
| 62 namespace { | 59 namespace { |
| 63 | 60 |
| 64 // Helper to launch xdg scripts. We don't want them to ask any questions on the | 61 // Helper to launch xdg scripts. We don't want them to ask any questions on the |
| 65 // terminal etc. The function returns true if the utility launches and exits | 62 // terminal etc. The function returns true if the utility launches and exits |
| 66 // cleanly, in which case |exit_code| returns the utility's exit code. | 63 // cleanly, in which case |exit_code| returns the utility's exit code. |
| 67 bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) { | 64 bool LaunchXdgUtility(const std::vector<std::string>& argv, int* exit_code) { |
| 68 // xdg-settings internally runs xdg-mime, which uses mv to move newly-created | 65 // xdg-settings internally runs xdg-mime, which uses mv to move newly-created |
| 69 // files on top of originals after making changes to them. In the event that | 66 // files on top of originals after making changes to them. In the event that |
| (...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 558 | 555 |
| 559 // Just return the name of the executable path for Chrome. | 556 // Just return the name of the executable path for Chrome. |
| 560 base::FilePath chrome_exe_path; | 557 base::FilePath chrome_exe_path; |
| 561 PathService::Get(base::FILE_EXE, &chrome_exe_path); | 558 PathService::Get(base::FILE_EXE, &chrome_exe_path); |
| 562 return chrome_exe_path; | 559 return chrome_exe_path; |
| 563 } | 560 } |
| 564 | 561 |
| 565 } // namespace | 562 } // namespace |
| 566 | 563 |
| 567 base::FilePath GetDataWriteLocation(base::Environment* env) { | 564 base::FilePath GetDataWriteLocation(base::Environment* env) { |
| 568 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 565 base::ThreadRestrictions::AssertIOAllowed(); |
| 569 | 566 |
| 570 return base::nix::GetXDGDirectory(env, "XDG_DATA_HOME", ".local/share"); | 567 return base::nix::GetXDGDirectory(env, "XDG_DATA_HOME", ".local/share"); |
| 571 } | 568 } |
| 572 | 569 |
| 573 std::vector<base::FilePath> GetDataSearchLocations(base::Environment* env) { | 570 std::vector<base::FilePath> GetDataSearchLocations(base::Environment* env) { |
| 574 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 571 base::ThreadRestrictions::AssertIOAllowed(); |
| 575 | 572 |
| 576 std::vector<base::FilePath> search_paths; | 573 std::vector<base::FilePath> search_paths; |
| 577 base::FilePath write_location = GetDataWriteLocation(env); | 574 base::FilePath write_location = GetDataWriteLocation(env); |
| 578 search_paths.push_back(write_location); | 575 search_paths.push_back(write_location); |
| 579 | 576 |
| 580 std::string xdg_data_dirs; | 577 std::string xdg_data_dirs; |
| 581 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && !xdg_data_dirs.empty()) { | 578 if (env->GetVar("XDG_DATA_DIRS", &xdg_data_dirs) && !xdg_data_dirs.empty()) { |
| 582 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); | 579 base::StringTokenizer tokenizer(xdg_data_dirs, ":"); |
| 583 while (tokenizer.GetNext()) { | 580 while (tokenizer.GetNext()) { |
| 584 base::FilePath data_dir(tokenizer.token()); | 581 base::FilePath data_dir(tokenizer.token()); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 675 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); | 672 PathService::Get(base::DIR_USER_DESKTOP, &desktop_path); |
| 676 return GetExistingShortcutLocations(env, profile_path, extension_id, | 673 return GetExistingShortcutLocations(env, profile_path, extension_id, |
| 677 desktop_path); | 674 desktop_path); |
| 678 } | 675 } |
| 679 | 676 |
| 680 web_app::ShortcutLocations GetExistingShortcutLocations( | 677 web_app::ShortcutLocations GetExistingShortcutLocations( |
| 681 base::Environment* env, | 678 base::Environment* env, |
| 682 const base::FilePath& profile_path, | 679 const base::FilePath& profile_path, |
| 683 const std::string& extension_id, | 680 const std::string& extension_id, |
| 684 const base::FilePath& desktop_path) { | 681 const base::FilePath& desktop_path) { |
| 685 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 682 base::ThreadRestrictions::AssertIOAllowed(); |
| 686 | 683 |
| 687 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 684 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 688 profile_path, extension_id); | 685 profile_path, extension_id); |
| 689 DCHECK(!shortcut_filename.empty()); | 686 DCHECK(!shortcut_filename.empty()); |
| 690 web_app::ShortcutLocations locations; | 687 web_app::ShortcutLocations locations; |
| 691 | 688 |
| 692 // Determine whether there is a shortcut on desktop. | 689 // Determine whether there is a shortcut on desktop. |
| 693 if (!desktop_path.empty()) { | 690 if (!desktop_path.empty()) { |
| 694 locations.on_desktop = | 691 locations.on_desktop = |
| 695 base::PathExists(desktop_path.Append(shortcut_filename)); | 692 base::PathExists(desktop_path.Append(shortcut_filename)); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 706 ? web_app::APP_MENU_LOCATION_HIDDEN | 703 ? web_app::APP_MENU_LOCATION_HIDDEN |
| 707 : web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS; | 704 : web_app::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS; |
| 708 } | 705 } |
| 709 | 706 |
| 710 return locations; | 707 return locations; |
| 711 } | 708 } |
| 712 | 709 |
| 713 bool GetExistingShortcutContents(base::Environment* env, | 710 bool GetExistingShortcutContents(base::Environment* env, |
| 714 const base::FilePath& desktop_filename, | 711 const base::FilePath& desktop_filename, |
| 715 std::string* output) { | 712 std::string* output) { |
| 716 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 713 base::ThreadRestrictions::AssertIOAllowed(); |
| 717 | 714 |
| 718 std::vector<base::FilePath> search_paths = GetDataSearchLocations(env); | 715 std::vector<base::FilePath> search_paths = GetDataSearchLocations(env); |
| 719 | 716 |
| 720 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); | 717 for (std::vector<base::FilePath>::const_iterator i = search_paths.begin(); |
| 721 i != search_paths.end(); ++i) { | 718 i != search_paths.end(); ++i) { |
| 722 base::FilePath path = i->Append("applications").Append(desktop_filename); | 719 base::FilePath path = i->Append("applications").Append(desktop_filename); |
| 723 VLOG(1) << "Looking for desktop file in " << path.value(); | 720 VLOG(1) << "Looking for desktop file in " << path.value(); |
| 724 if (base::PathExists(path)) { | 721 if (base::PathExists(path)) { |
| 725 VLOG(1) << "Found desktop file at " << path.value(); | 722 VLOG(1) << "Found desktop file at " << path.value(); |
| 726 return base::ReadFileToString(path, output); | 723 return base::ReadFileToString(path, output); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 767 base::i18n::ReplaceIllegalCharactersInPath(&filename, '_'); | 764 base::i18n::ReplaceIllegalCharactersInPath(&filename, '_'); |
| 768 // Spaces in filenames break xdg-desktop-menu | 765 // Spaces in filenames break xdg-desktop-menu |
| 769 // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605). | 766 // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605). |
| 770 base::ReplaceChars(filename, " ", "_", &filename); | 767 base::ReplaceChars(filename, " ", "_", &filename); |
| 771 return base::FilePath(filename.append(".desktop")); | 768 return base::FilePath(filename.append(".desktop")); |
| 772 } | 769 } |
| 773 | 770 |
| 774 std::vector<base::FilePath> GetExistingProfileShortcutFilenames( | 771 std::vector<base::FilePath> GetExistingProfileShortcutFilenames( |
| 775 const base::FilePath& profile_path, | 772 const base::FilePath& profile_path, |
| 776 const base::FilePath& directory) { | 773 const base::FilePath& directory) { |
| 777 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 774 base::ThreadRestrictions::AssertIOAllowed(); |
| 778 | 775 |
| 779 // Use a prefix, because xdg-desktop-menu requires it. | 776 // Use a prefix, because xdg-desktop-menu requires it. |
| 780 std::string prefix(chrome::kBrowserProcessExecutableName); | 777 std::string prefix(chrome::kBrowserProcessExecutableName); |
| 781 prefix.append("-"); | 778 prefix.append("-"); |
| 782 std::string suffix("-"); | 779 std::string suffix("-"); |
| 783 suffix.append(profile_path.BaseName().value()); | 780 suffix.append(profile_path.BaseName().value()); |
| 784 base::i18n::ReplaceIllegalCharactersInPath(&suffix, '_'); | 781 base::i18n::ReplaceIllegalCharactersInPath(&suffix, '_'); |
| 785 // Spaces in filenames break xdg-desktop-menu | 782 // Spaces in filenames break xdg-desktop-menu |
| 786 // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605). | 783 // (see https://bugs.freedesktop.org/show_bug.cgi?id=66605). |
| 787 base::ReplaceChars(suffix, " ", "_", &suffix); | 784 base::ReplaceChars(suffix, " ", "_", &suffix); |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 932 return output_buffer; | 929 return output_buffer; |
| 933 #else | 930 #else |
| 934 NOTIMPLEMENTED(); | 931 NOTIMPLEMENTED(); |
| 935 return std::string(); | 932 return std::string(); |
| 936 #endif | 933 #endif |
| 937 } | 934 } |
| 938 | 935 |
| 939 bool CreateDesktopShortcut( | 936 bool CreateDesktopShortcut( |
| 940 const web_app::ShortcutInfo& shortcut_info, | 937 const web_app::ShortcutInfo& shortcut_info, |
| 941 const web_app::ShortcutLocations& creation_locations) { | 938 const web_app::ShortcutLocations& creation_locations) { |
| 942 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 939 base::ThreadRestrictions::AssertIOAllowed(); |
| 943 | 940 |
| 944 base::FilePath shortcut_filename; | 941 base::FilePath shortcut_filename; |
| 945 if (!shortcut_info.extension_id.empty()) { | 942 if (!shortcut_info.extension_id.empty()) { |
| 946 shortcut_filename = GetExtensionShortcutFilename( | 943 shortcut_filename = GetExtensionShortcutFilename( |
| 947 shortcut_info.profile_path, shortcut_info.extension_id); | 944 shortcut_info.profile_path, shortcut_info.extension_id); |
| 948 // For extensions we do not want duplicate shortcuts. So, delete any that | 945 // For extensions we do not want duplicate shortcuts. So, delete any that |
| 949 // already exist and replace them. | 946 // already exist and replace them. |
| 950 if (creation_locations.on_desktop) | 947 if (creation_locations.on_desktop) |
| 951 DeleteShortcutOnDesktop(shortcut_filename); | 948 DeleteShortcutOnDesktop(shortcut_filename); |
| 952 | 949 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1025 shortcut_filename, contents, directory_filename, directory_contents) && | 1022 shortcut_filename, contents, directory_filename, directory_contents) && |
| 1026 success; | 1023 success; |
| 1027 | 1024 |
| 1028 return success; | 1025 return success; |
| 1029 } | 1026 } |
| 1030 | 1027 |
| 1031 #if BUILDFLAG(ENABLE_APP_LIST) | 1028 #if BUILDFLAG(ENABLE_APP_LIST) |
| 1032 bool CreateAppListDesktopShortcut( | 1029 bool CreateAppListDesktopShortcut( |
| 1033 const std::string& wm_class, | 1030 const std::string& wm_class, |
| 1034 const std::string& title) { | 1031 const std::string& title) { |
| 1035 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 1032 base::ThreadRestrictions::AssertIOAllowed(); |
| 1036 | 1033 |
| 1037 base::FilePath desktop_name(kAppListDesktopName); | 1034 base::FilePath desktop_name(kAppListDesktopName); |
| 1038 base::FilePath shortcut_filename = desktop_name.AddExtension("desktop"); | 1035 base::FilePath shortcut_filename = desktop_name.AddExtension("desktop"); |
| 1039 | 1036 |
| 1040 // We do not want duplicate shortcuts. Delete any that already exist and | 1037 // We do not want duplicate shortcuts. Delete any that already exist and |
| 1041 // replace them. | 1038 // replace them. |
| 1042 DeleteShortcutInApplicationsMenu(shortcut_filename, base::FilePath()); | 1039 DeleteShortcutInApplicationsMenu(shortcut_filename, base::FilePath()); |
| 1043 | 1040 |
| 1044 base::FilePath chrome_exe_path = GetChromeExePath(); | 1041 base::FilePath chrome_exe_path = GetChromeExePath(); |
| 1045 if (chrome_exe_path.empty()) { | 1042 if (chrome_exe_path.empty()) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1065 icon_name, | 1062 icon_name, |
| 1066 kAppListCategories, | 1063 kAppListCategories, |
| 1067 false); | 1064 false); |
| 1068 return CreateShortcutInApplicationsMenu( | 1065 return CreateShortcutInApplicationsMenu( |
| 1069 shortcut_filename, contents, base::FilePath(), ""); | 1066 shortcut_filename, contents, base::FilePath(), ""); |
| 1070 } | 1067 } |
| 1071 #endif | 1068 #endif |
| 1072 | 1069 |
| 1073 void DeleteDesktopShortcuts(const base::FilePath& profile_path, | 1070 void DeleteDesktopShortcuts(const base::FilePath& profile_path, |
| 1074 const std::string& extension_id) { | 1071 const std::string& extension_id) { |
| 1075 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 1072 base::ThreadRestrictions::AssertIOAllowed(); |
| 1076 | 1073 |
| 1077 base::FilePath shortcut_filename = GetExtensionShortcutFilename( | 1074 base::FilePath shortcut_filename = GetExtensionShortcutFilename( |
| 1078 profile_path, extension_id); | 1075 profile_path, extension_id); |
| 1079 DCHECK(!shortcut_filename.empty()); | 1076 DCHECK(!shortcut_filename.empty()); |
| 1080 | 1077 |
| 1081 DeleteShortcutOnDesktop(shortcut_filename); | 1078 DeleteShortcutOnDesktop(shortcut_filename); |
| 1082 // Delete shortcuts from |kDirectoryFilename|. | 1079 // Delete shortcuts from |kDirectoryFilename|. |
| 1083 // Note that it is possible that shortcuts were not created in the Chrome Apps | 1080 // Note that it is possible that shortcuts were not created in the Chrome Apps |
| 1084 // directory. It doesn't matter: this will still delete the shortcut even if | 1081 // directory. It doesn't matter: this will still delete the shortcut even if |
| 1085 // it isn't in the directory. | 1082 // it isn't in the directory. |
| 1086 DeleteShortcutInApplicationsMenu(shortcut_filename, | 1083 DeleteShortcutInApplicationsMenu(shortcut_filename, |
| 1087 base::FilePath(kDirectoryFilename)); | 1084 base::FilePath(kDirectoryFilename)); |
| 1088 } | 1085 } |
| 1089 | 1086 |
| 1090 void DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { | 1087 void DeleteAllDesktopShortcuts(const base::FilePath& profile_path) { |
| 1091 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 1088 base::ThreadRestrictions::AssertIOAllowed(); |
| 1092 | 1089 |
| 1093 std::unique_ptr<base::Environment> env(base::Environment::Create()); | 1090 std::unique_ptr<base::Environment> env(base::Environment::Create()); |
| 1094 | 1091 |
| 1095 // Delete shortcuts from Desktop. | 1092 // Delete shortcuts from Desktop. |
| 1096 base::FilePath desktop_path; | 1093 base::FilePath desktop_path; |
| 1097 if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) { | 1094 if (PathService::Get(base::DIR_USER_DESKTOP, &desktop_path)) { |
| 1098 std::vector<base::FilePath> shortcut_filenames_desktop = | 1095 std::vector<base::FilePath> shortcut_filenames_desktop = |
| 1099 GetExistingProfileShortcutFilenames(profile_path, desktop_path); | 1096 GetExistingProfileShortcutFilenames(profile_path, desktop_path); |
| 1100 for (const auto& shortcut : shortcut_filenames_desktop) { | 1097 for (const auto& shortcut : shortcut_filenames_desktop) { |
| 1101 DeleteShortcutOnDesktop(shortcut); | 1098 DeleteShortcutOnDesktop(shortcut); |
| 1102 } | 1099 } |
| 1103 } | 1100 } |
| 1104 | 1101 |
| 1105 // Delete shortcuts from |kDirectoryFilename|. | 1102 // Delete shortcuts from |kDirectoryFilename|. |
| 1106 base::FilePath applications_menu = GetDataWriteLocation(env.get()); | 1103 base::FilePath applications_menu = GetDataWriteLocation(env.get()); |
| 1107 applications_menu = applications_menu.AppendASCII("applications"); | 1104 applications_menu = applications_menu.AppendASCII("applications"); |
| 1108 std::vector<base::FilePath> shortcut_filenames_app_menu = | 1105 std::vector<base::FilePath> shortcut_filenames_app_menu = |
| 1109 GetExistingProfileShortcutFilenames(profile_path, applications_menu); | 1106 GetExistingProfileShortcutFilenames(profile_path, applications_menu); |
| 1110 for (const auto& menu : shortcut_filenames_app_menu) { | 1107 for (const auto& menu : shortcut_filenames_app_menu) { |
| 1111 DeleteShortcutInApplicationsMenu(menu, base::FilePath(kDirectoryFilename)); | 1108 DeleteShortcutInApplicationsMenu(menu, base::FilePath(kDirectoryFilename)); |
| 1112 } | 1109 } |
| 1113 } | 1110 } |
| 1114 | 1111 |
| 1115 } // namespace shell_integration_linux | 1112 } // namespace shell_integration_linux |
| OLD | NEW |