| 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/platform_util.h" | 5 #include "chrome/browser/platform_util.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" |
| 8 #include "base/files/file_util.h" | 9 #include "base/files/file_util.h" |
| 9 #include "base/process/kill.h" | 10 #include "base/process/kill.h" |
| 10 #include "base/process/launch.h" | 11 #include "base/process/launch.h" |
| 12 #include "base/strings/string_util.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "base/version.h" |
| 12 #include "chrome/browser/platform_util_internal.h" | 15 #include "chrome/browser/platform_util_internal.h" |
| 13 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 14 #include "url/gurl.h" | 17 #include "url/gurl.h" |
| 15 | 18 |
| 16 using content::BrowserThread; | 19 using content::BrowserThread; |
| 17 | 20 |
| 18 namespace platform_util { | 21 namespace platform_util { |
| 19 | 22 |
| 20 namespace { | 23 namespace { |
| 21 | 24 |
| 22 void XDGUtil(const std::string& util, | 25 const char kNautilusKey[] = "nautilus.desktop"; |
| 23 const base::FilePath& working_directory, | 26 const char kNautilusCmd[] = "nautilus"; |
| 24 const std::string& arg) { | 27 const char kSupportedNautilusVersion[] = "3.0.2"; |
| 28 |
| 29 void RunCommand(const std::string& command, |
| 30 const base::FilePath& working_directory, |
| 31 const std::string& arg) { |
| 25 std::vector<std::string> argv; | 32 std::vector<std::string> argv; |
| 26 argv.push_back(util); | 33 argv.push_back(command); |
| 27 argv.push_back(arg); | 34 argv.push_back(arg); |
| 28 | 35 |
| 29 base::LaunchOptions options; | 36 base::LaunchOptions options; |
| 30 options.current_directory = working_directory; | 37 options.current_directory = working_directory; |
| 31 options.allow_new_privs = true; | 38 options.allow_new_privs = true; |
| 32 // xdg-open can fall back on mailcap which eventually might plumb through | 39 // xdg-open can fall back on mailcap which eventually might plumb through |
| 33 // to a command that needs a terminal. Set the environment variable telling | 40 // to a command that needs a terminal. Set the environment variable telling |
| 34 // it that we definitely don't have a terminal available and that it should | 41 // it that we definitely don't have a terminal available and that it should |
| 35 // bring up a new terminal if necessary. See "man mailcap". | 42 // bring up a new terminal if necessary. See "man mailcap". |
| 36 options.environ["MM_NOTTTY"] = "1"; | 43 options.environ["MM_NOTTTY"] = "1"; |
| 37 | 44 |
| 38 // In Google Chrome, we do not let GNOME's bug-buddy intercept our crashes. | 45 // In Google Chrome, we do not let GNOME's bug-buddy intercept our crashes. |
| 39 // However, we do not want this environment variable to propagate to external | 46 // However, we do not want this environment variable to propagate to external |
| 40 // applications. See http://crbug.com/24120 | 47 // applications. See http://crbug.com/24120 |
| 41 char* disable_gnome_bug_buddy = getenv("GNOME_DISABLE_CRASH_DIALOG"); | 48 char* disable_gnome_bug_buddy = getenv("GNOME_DISABLE_CRASH_DIALOG"); |
| 42 if (disable_gnome_bug_buddy && | 49 if (disable_gnome_bug_buddy && |
| 43 disable_gnome_bug_buddy == std::string("SET_BY_GOOGLE_CHROME")) | 50 disable_gnome_bug_buddy == std::string("SET_BY_GOOGLE_CHROME")) |
| 44 options.environ["GNOME_DISABLE_CRASH_DIALOG"] = std::string(); | 51 options.environ["GNOME_DISABLE_CRASH_DIALOG"] = std::string(); |
| 45 | 52 |
| 46 base::Process process = base::LaunchProcess(argv, options); | 53 base::Process process = base::LaunchProcess(argv, options); |
| 47 if (process.IsValid()) | 54 if (process.IsValid()) |
| 48 base::EnsureProcessGetsReaped(process.Pid()); | 55 base::EnsureProcessGetsReaped(process.Pid()); |
| 49 } | 56 } |
| 50 | 57 |
| 51 void XDGOpen(const base::FilePath& working_directory, const std::string& path) { | 58 void XDGOpen(const base::FilePath& working_directory, const std::string& path) { |
| 52 XDGUtil("xdg-open", working_directory, path); | 59 RunCommand("xdg-open", working_directory, path); |
| 53 } | 60 } |
| 54 | 61 |
| 55 void XDGEmail(const std::string& email) { | 62 void XDGEmail(const std::string& email) { |
| 56 XDGUtil("xdg-email", base::FilePath(), email); | 63 RunCommand("xdg-email", base::FilePath(), email); |
| 64 } |
| 65 |
| 66 void ShowFileInNautilus(const base::FilePath& working_directory, |
| 67 const std::string& path) { |
| 68 RunCommand(kNautilusCmd, working_directory, path); |
| 69 } |
| 70 |
| 71 std::string GetNautilusVersion() { |
| 72 std::string output; |
| 73 std::string found_version; |
| 74 |
| 75 base::CommandLine nautilus_cl((base::FilePath(kNautilusCmd))); |
| 76 nautilus_cl.AppendArg("--version"); |
| 77 |
| 78 if (base::GetAppOutputAndError(nautilus_cl, &output)) { |
| 79 // It is assumed that "nautilus --version" returns something like |
| 80 // "GNOME nautilus 3.14.2". First, find the position of the first char of |
| 81 // "nautilus " and skip the whole string to get the position of |
| 82 // version in the |output| string. |
| 83 size_t nautilus_position = output.find("nautilus "); |
| 84 size_t version_position = nautilus_position + strlen("nautilus "); |
| 85 if (nautilus_position != std::string::npos) { |
| 86 found_version = output.substr(version_position); |
| 87 base::TrimWhitespaceASCII(found_version, |
| 88 base::TRIM_TRAILING, |
| 89 &found_version); |
| 90 } |
| 91 } |
| 92 return found_version; |
| 93 } |
| 94 |
| 95 bool CheckNautilusIsDefault() { |
| 96 std::string file_browser; |
| 97 |
| 98 base::CommandLine xdg_mime(base::FilePath("xdg-mime")); |
| 99 xdg_mime.AppendArg("query"); |
| 100 xdg_mime.AppendArg("default"); |
| 101 xdg_mime.AppendArg("inode/directory"); |
| 102 |
| 103 bool success = base::GetAppOutputAndError(xdg_mime, &file_browser); |
| 104 base::TrimWhitespaceASCII(file_browser, |
| 105 base::TRIM_TRAILING, |
| 106 &file_browser); |
| 107 |
| 108 if (!success || file_browser != kNautilusKey) |
| 109 return false; |
| 110 |
| 111 const base::Version supported_version(kSupportedNautilusVersion); |
| 112 DCHECK(supported_version.IsValid()); |
| 113 const base::Version current_version(GetNautilusVersion()); |
| 114 return current_version.IsValid() && current_version >= supported_version; |
| 115 } |
| 116 |
| 117 void ShowItem(Profile* profile, |
| 118 const base::FilePath& full_path, |
| 119 bool use_nautilus_file_browser) { |
| 120 if (use_nautilus_file_browser) { |
| 121 OpenItem(profile, full_path, SHOW_ITEM_IN_FOLDER, OpenOperationCallback()); |
| 122 } else { |
| 123 // TODO(estade): It would be nice to be able to select the file in other |
| 124 // file managers, but that probably requires extending xdg-open. |
| 125 // For now just show the folder for non-Nautilus users. |
| 126 OpenItem(profile, full_path.DirName(), OPEN_FOLDER, |
| 127 OpenOperationCallback()); |
| 128 } |
| 57 } | 129 } |
| 58 | 130 |
| 59 } // namespace | 131 } // namespace |
| 60 | 132 |
| 61 namespace internal { | 133 namespace internal { |
| 62 | 134 |
| 63 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) { | 135 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) { |
| 64 switch (type) { | 136 switch (type) { |
| 65 case OPEN_FILE: | 137 case OPEN_FILE: |
| 66 XDGOpen(path.DirName(), path.value()); | 138 XDGOpen(path.DirName(), path.value()); |
| 67 break; | 139 break; |
| 68 case OPEN_FOLDER: | 140 case OPEN_FOLDER: |
| 69 // The utility process checks the working directory prior to the | 141 // The utility process checks the working directory prior to the |
| 70 // invocation of xdg-open by changing the current directory into it. This | 142 // invocation of xdg-open by changing the current directory into it. This |
| 71 // operation only succeeds if |path| is a directory. Opening "." from | 143 // operation only succeeds if |path| is a directory. Opening "." from |
| 72 // there ensures that the target of the operation is a directory. Note | 144 // there ensures that the target of the operation is a directory. Note |
| 73 // that there remains a TOCTOU race where the directory could be unlinked | 145 // that there remains a TOCTOU race where the directory could be unlinked |
| 74 // between the time the utility process changes into the directory and the | 146 // between the time the utility process changes into the directory and the |
| 75 // time the application invoked by xdg-open inspects the path by name. | 147 // time the application invoked by xdg-open inspects the path by name. |
| 76 XDGOpen(path, "."); | 148 XDGOpen(path, "."); |
| 77 break; | 149 break; |
| 150 case SHOW_ITEM_IN_FOLDER: |
| 151 ShowFileInNautilus(path.DirName(), path.value()); |
| 152 break; |
| 78 } | 153 } |
| 79 } | 154 } |
| 155 |
| 80 } // namespace internal | 156 } // namespace internal |
| 81 | 157 |
| 82 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { | 158 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { |
| 83 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 159 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 84 // TODO(estade): It would be nice to be able to select the file in the file | 160 base::PostTaskAndReplyWithResult(content::BrowserThread::GetBlockingPool(), |
| 85 // manager, but that probably requires extending xdg-open. For now just show | 161 FROM_HERE, |
| 86 // the folder. | 162 base::Bind(&CheckNautilusIsDefault), |
| 87 OpenItem(profile, full_path.DirName(), OPEN_FOLDER, OpenOperationCallback()); | 163 base::Bind(&ShowItem, profile, full_path)); |
| 88 } | 164 } |
| 89 | 165 |
| 90 void OpenExternal(Profile* profile, const GURL& url) { | 166 void OpenExternal(Profile* profile, const GURL& url) { |
| 91 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 167 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 92 if (url.SchemeIs("mailto")) | 168 if (url.SchemeIs("mailto")) |
| 93 XDGEmail(url.spec()); | 169 XDGEmail(url.spec()); |
| 94 else | 170 else |
| 95 XDGOpen(base::FilePath(), url.spec()); | 171 XDGOpen(base::FilePath(), url.spec()); |
| 96 } | 172 } |
| 97 | 173 |
| 98 } // namespace platform_util | 174 } // namespace platform_util |
| OLD | NEW |