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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
7 #include "base/basictypes.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/path_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/threading/sequenced_worker_pool.h"
17 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "content/public/browser/browser_thread.h"
23 namespace extensions {
28 // Name of the command line switch used to pass handle of the native view to
29 // the native messaging host.
30 const char kParentWindowSwitchName[] = "parent-window";
31 #endif // defined(OS_WIN)
33 base::FilePath GetHostManifestPathFromCommandLine(
34 const std::string& native_host_name) {
35 const std::string& value =
36 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
37 switches::kNativeMessagingHosts);
39 return base::FilePath();
41 std::vector<std::string> hosts;
42 base::SplitString(value, ',', &hosts);
43 for (size_t i = 0; i < hosts.size(); ++i) {
44 std::vector<std::string> key_and_value;
45 base::SplitString(hosts[i], '=', &key_and_value);
46 if (key_and_value.size() != 2)
48 if (key_and_value[0] == native_host_name)
49 return base::FilePath::FromUTF8Unsafe(key_and_value[1]);
52 return base::FilePath();
56 // Default implementation on NativeProcessLauncher interface.
57 class NativeProcessLauncherImpl : public NativeProcessLauncher {
59 explicit NativeProcessLauncherImpl(gfx::NativeView native_view);
60 virtual ~NativeProcessLauncherImpl();
62 virtual void Launch(const GURL& origin,
63 const std::string& native_host_name,
64 LaunchedCallback callback) const OVERRIDE;
67 class Core : public base::RefCountedThreadSafe<Core> {
69 explicit Core(gfx::NativeView native_view);
70 void Launch(const GURL& origin,
71 const std::string& native_host_name,
72 LaunchedCallback callback);
76 friend class base::RefCountedThreadSafe<Core>;
79 void DoLaunchOnThreadPool(const GURL& origin,
80 const std::string& native_host_name,
81 LaunchedCallback callback);
82 void PostErrorResult(const LaunchedCallback& callback, LaunchResult error);
83 void PostResult(const LaunchedCallback& callback,
84 base::ProcessHandle process_handle,
85 base::PlatformFile read_file,
86 base::PlatformFile write_file);
87 void CallCallbackOnIOThread(LaunchedCallback callback,
89 base::ProcessHandle process_handle,
90 base::PlatformFile read_file,
91 base::PlatformFile write_file);
95 // Handle of the native view corrsponding to the extension.
96 gfx::NativeView native_view_;
98 DISALLOW_COPY_AND_ASSIGN(Core);
101 scoped_refptr<Core> core_;
103 DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl);
106 NativeProcessLauncherImpl::Core::Core(gfx::NativeView native_view)
108 native_view_(native_view) {
111 NativeProcessLauncherImpl::Core::~Core() {
115 void NativeProcessLauncherImpl::Core::Detach() {
116 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
120 void NativeProcessLauncherImpl::Core::Launch(
122 const std::string& native_host_name,
123 LaunchedCallback callback) {
124 content::BrowserThread::PostBlockingPoolTask(
125 FROM_HERE, base::Bind(&Core::DoLaunchOnThreadPool, this,
126 origin, native_host_name, callback));
129 void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool(
131 const std::string& native_host_name,
132 LaunchedCallback callback) {
133 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
135 if (!NativeMessagingHostManifest::IsValidName(native_host_name)) {
136 PostErrorResult(callback, RESULT_INVALID_NAME);
140 std::string error_message;
141 scoped_ptr<NativeMessagingHostManifest> manifest;
143 // First check if the manifest location is specified in the command line.
144 base::FilePath manifest_path =
145 GetHostManifestPathFromCommandLine(native_host_name);
146 if (manifest_path.empty())
147 manifest_path = FindManifest(native_host_name, &error_message);
149 if (manifest_path.empty()) {
150 LOG(ERROR) << "Can't find manifest for native messaging host "
152 PostErrorResult(callback, RESULT_NOT_FOUND);
156 manifest = NativeMessagingHostManifest::Load(manifest_path, &error_message);
159 LOG(ERROR) << "Failed to load manifest for native messaging host "
160 << native_host_name << ": " << error_message;
161 PostErrorResult(callback, RESULT_NOT_FOUND);
165 if (manifest->name() != native_host_name) {
166 LOG(ERROR) << "Failed to load manifest for native messaging host "
168 << ": Invalid name specified in the manifest.";
169 PostErrorResult(callback, RESULT_NOT_FOUND);
173 if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) {
174 // Not an allowed origin.
175 PostErrorResult(callback, RESULT_FORBIDDEN);
179 base::FilePath host_path = manifest->path();
180 if (!host_path.IsAbsolute()) {
181 // On Windows host path is allowed to be relative to the location of the
182 // manifest file. On all other platforms the path must be absolute.
184 host_path = manifest_path.DirName().Append(host_path);
185 #else // defined(OS_WIN)
186 LOG(ERROR) << "Native messaging host path must be absolute for "
188 PostErrorResult(callback, RESULT_NOT_FOUND);
190 #endif // !defined(OS_WIN)
193 CommandLine command_line(host_path);
194 command_line.AppendArg(origin.spec());
196 // Pass handle of the native view window to the native messaging host. This
197 // way the host will be able to create properly focused UI windows.
199 int64 window_handle = reinterpret_cast<intptr_t>(native_view_);
200 command_line.AppendSwitchASCII(kParentWindowSwitchName,
201 base::Int64ToString(window_handle));
202 #endif // !defined(OS_WIN)
204 base::ProcessHandle process_handle;
205 base::PlatformFile read_file;
206 base::PlatformFile write_file;
207 if (NativeProcessLauncher::LaunchNativeProcess(
208 command_line, &process_handle, &read_file, &write_file)) {
209 PostResult(callback, process_handle, read_file, write_file);
211 PostErrorResult(callback, RESULT_FAILED_TO_START);
215 void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread(
216 LaunchedCallback callback,
218 base::ProcessHandle process_handle,
219 base::PlatformFile read_file,
220 base::PlatformFile write_file) {
221 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
223 if (read_file != base::kInvalidPlatformFileValue)
224 base::ClosePlatformFile(read_file);
225 if (write_file != base::kInvalidPlatformFileValue)
226 base::ClosePlatformFile(write_file);
230 callback.Run(result, process_handle, read_file, write_file);
233 void NativeProcessLauncherImpl::Core::PostErrorResult(
234 const LaunchedCallback& callback,
235 LaunchResult error) {
236 content::BrowserThread::PostTask(
237 content::BrowserThread::IO, FROM_HERE,
238 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this,
239 callback, error, base::kNullProcessHandle,
240 base::kInvalidPlatformFileValue,
241 base::kInvalidPlatformFileValue));
244 void NativeProcessLauncherImpl::Core::PostResult(
245 const LaunchedCallback& callback,
246 base::ProcessHandle process_handle,
247 base::PlatformFile read_file,
248 base::PlatformFile write_file) {
249 content::BrowserThread::PostTask(
250 content::BrowserThread::IO, FROM_HERE,
251 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this,
252 callback, RESULT_SUCCESS, process_handle, read_file,
256 NativeProcessLauncherImpl::NativeProcessLauncherImpl(
257 gfx::NativeView native_view)
258 : core_(new Core(native_view)) {
261 NativeProcessLauncherImpl::~NativeProcessLauncherImpl() {
265 void NativeProcessLauncherImpl::Launch(const GURL& origin,
266 const std::string& native_host_name,
267 LaunchedCallback callback) const {
268 core_->Launch(origin, native_host_name, callback);
274 scoped_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault(
275 gfx::NativeView native_view) {
276 return scoped_ptr<NativeProcessLauncher>(
277 new NativeProcessLauncherImpl(native_view));
280 } // namespace extensions