Chromium Code Reviews| 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", skip the whole string and add 1 to get the position of | |
| 82 // version in the |output| string. | |
| 83 const std::size_t nautilus_position = output.find(kNautilusCmd); | |
| 84 if (nautilus_position != std::string::npos) { | |
| 85 const std::size_t version_position = | |
| 86 nautilus_position + strlen(kNautilusCmd) + 1; | |
| 87 found_version = output.substr(version_position); | |
| 88 } | |
| 89 if (!found_version.empty() && | |
|
Lei Zhang
2016/04/22 07:09:57
If you are going to trim, there's no need to check
| |
| 90 found_version[found_version.length() - 1] == '\n') { | |
| 91 base::TrimWhitespaceASCII(found_version, | |
| 92 base::TRIM_TRAILING, | |
| 93 &found_version); | |
| 94 } | |
| 95 } | |
| 96 return found_version; | |
| 97 } | |
| 98 | |
| 99 bool CheckNautilusIsDefault() { | |
| 100 std::string file_browser; | |
| 101 | |
| 102 base::CommandLine xdg_mime(base::FilePath("xdg-mime")); | |
| 103 xdg_mime.AppendArg("query"); | |
| 104 xdg_mime.AppendArg("default"); | |
| 105 xdg_mime.AppendArg("inode/directory"); | |
| 106 | |
| 107 if (!base::GetAppOutputAndError(xdg_mime, &file_browser) || | |
| 108 file_browser.find(kNautilusKey) == std::string::npos) { | |
| 109 return false; | |
| 110 } | |
| 111 | |
| 112 const base::Version supported_version(kSupportedNautilusVersion); | |
| 113 DCHECK(supported_version.IsValid()); | |
| 114 const base::Version current_version(GetNautilusVersion()); | |
| 115 return current_version.IsValid() && | |
| 116 current_version >= supported_version; | |
| 117 } | |
| 118 | |
| 119 void ShowItem(Profile* profile, | |
| 120 const base::FilePath& full_path, | |
| 121 bool use_nautilus_file_browser) { | |
| 122 if (use_nautilus_file_browser) { | |
| 123 OpenItem(profile, full_path, SHOW_ITEM_IN_FOLDER, OpenOperationCallback()); | |
| 124 } else { | |
| 125 // TODO(estade): It would be nice to be able to select the file in other | |
| 126 // file managers, but that probably requires extending xdg-open. | |
| 127 // For now just show the folder for non-Nautilus users. | |
| 128 OpenItem(profile, full_path.DirName(), OPEN_FOLDER, | |
| 129 OpenOperationCallback()); | |
| 130 } | |
| 57 } | 131 } |
| 58 | 132 |
| 59 } // namespace | 133 } // namespace |
| 60 | 134 |
| 61 namespace internal { | 135 namespace internal { |
| 62 | 136 |
| 63 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) { | 137 void PlatformOpenVerifiedItem(const base::FilePath& path, OpenItemType type) { |
| 64 switch (type) { | 138 switch (type) { |
| 65 case OPEN_FILE: | 139 case OPEN_FILE: |
| 66 XDGOpen(path.DirName(), path.value()); | 140 XDGOpen(path.DirName(), path.value()); |
| 67 break; | 141 break; |
| 68 case OPEN_FOLDER: | 142 case OPEN_FOLDER: |
| 69 // The utility process checks the working directory prior to the | 143 // The utility process checks the working directory prior to the |
| 70 // invocation of xdg-open by changing the current directory into it. This | 144 // invocation of xdg-open by changing the current directory into it. This |
| 71 // operation only succeeds if |path| is a directory. Opening "." from | 145 // operation only succeeds if |path| is a directory. Opening "." from |
| 72 // there ensures that the target of the operation is a directory. Note | 146 // 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 | 147 // 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 | 148 // 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. | 149 // time the application invoked by xdg-open inspects the path by name. |
| 76 XDGOpen(path, "."); | 150 XDGOpen(path, "."); |
| 77 break; | 151 break; |
| 152 case SHOW_ITEM_IN_FOLDER: | |
| 153 ShowFileInNautilus(path.DirName(), path.value()); | |
| 154 break; | |
| 78 } | 155 } |
| 79 } | 156 } |
| 157 | |
| 80 } // namespace internal | 158 } // namespace internal |
| 81 | 159 |
| 82 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { | 160 void ShowItemInFolder(Profile* profile, const base::FilePath& full_path) { |
| 83 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 161 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 84 // TODO(estade): It would be nice to be able to select the file in the file | 162 base::PostTaskAndReplyWithResult(content::BrowserThread::GetBlockingPool(), |
| 85 // manager, but that probably requires extending xdg-open. For now just show | 163 FROM_HERE, |
| 86 // the folder. | 164 base::Bind(&CheckNautilusIsDefault), |
| 87 OpenItem(profile, full_path.DirName(), OPEN_FOLDER, OpenOperationCallback()); | 165 base::Bind(&ShowItem, profile, full_path)); |
| 88 } | 166 } |
| 89 | 167 |
| 90 void OpenExternal(Profile* profile, const GURL& url) { | 168 void OpenExternal(Profile* profile, const GURL& url) { |
| 91 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 169 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 92 if (url.SchemeIs("mailto")) | 170 if (url.SchemeIs("mailto")) |
| 93 XDGEmail(url.spec()); | 171 XDGEmail(url.spec()); |
| 94 else | 172 else |
| 95 XDGOpen(base::FilePath(), url.spec()); | 173 XDGOpen(base::FilePath(), url.spec()); |
| 96 } | 174 } |
| 97 | 175 |
| 98 } // namespace platform_util | 176 } // namespace platform_util |
| OLD | NEW |