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