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/file_util.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
19 #include "chrome/common/chrome_paths.h"
20 #include "content/public/browser/browser_thread.h"
24 #include "ui/views/win/hwnd_util.h"
27 namespace extensions
{
32 // Name of the command line switch used to pass handle of the native view to
33 // the native messaging host.
34 const char kParentWindowSwitchName
[] = "parent-window";
35 #endif // defined(OS_WIN)
37 // Default implementation on NativeProcessLauncher interface.
38 class NativeProcessLauncherImpl
: public NativeProcessLauncher
{
40 NativeProcessLauncherImpl(bool allow_user_level_hosts
,
41 intptr_t native_window
);
42 virtual ~NativeProcessLauncherImpl();
44 virtual void Launch(const GURL
& origin
,
45 const std::string
& native_host_name
,
46 LaunchedCallback callback
) const OVERRIDE
;
49 class Core
: public base::RefCountedThreadSafe
<Core
> {
51 Core(bool allow_user_level_hosts
, intptr_t native_window
);
52 void Launch(const GURL
& origin
,
53 const std::string
& native_host_name
,
54 LaunchedCallback callback
);
58 friend class base::RefCountedThreadSafe
<Core
>;
61 void DoLaunchOnThreadPool(const GURL
& origin
,
62 const std::string
& native_host_name
,
63 LaunchedCallback callback
);
64 void PostErrorResult(const LaunchedCallback
& callback
, LaunchResult error
);
65 void PostResult(const LaunchedCallback
& callback
,
66 base::ProcessHandle process_handle
,
68 base::File write_file
);
69 void CallCallbackOnIOThread(LaunchedCallback callback
,
71 base::ProcessHandle process_handle
,
73 base::File write_file
);
77 bool allow_user_level_hosts_
;
79 // Handle of the native window corresponding to the extension.
80 intptr_t window_handle_
;
82 DISALLOW_COPY_AND_ASSIGN(Core
);
85 scoped_refptr
<Core
> core_
;
87 DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl
);
90 NativeProcessLauncherImpl::Core::Core(bool allow_user_level_hosts
,
91 intptr_t window_handle
)
93 allow_user_level_hosts_(allow_user_level_hosts
),
94 window_handle_(window_handle
) {
97 NativeProcessLauncherImpl::Core::~Core() {
101 void NativeProcessLauncherImpl::Core::Detach() {
102 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
106 void NativeProcessLauncherImpl::Core::Launch(
108 const std::string
& native_host_name
,
109 LaunchedCallback callback
) {
110 content::BrowserThread::PostBlockingPoolTask(
111 FROM_HERE
, base::Bind(&Core::DoLaunchOnThreadPool
, this,
112 origin
, native_host_name
, callback
));
115 void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool(
117 const std::string
& native_host_name
,
118 LaunchedCallback callback
) {
119 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
121 if (!NativeMessagingHostManifest::IsValidName(native_host_name
)) {
122 PostErrorResult(callback
, RESULT_INVALID_NAME
);
126 std::string error_message
;
127 base::FilePath manifest_path
=
128 FindManifest(native_host_name
, allow_user_level_hosts_
, &error_message
);
130 if (manifest_path
.empty()) {
131 LOG(ERROR
) << "Can't find manifest for native messaging host "
133 PostErrorResult(callback
, RESULT_NOT_FOUND
);
137 scoped_ptr
<NativeMessagingHostManifest
> manifest
=
138 NativeMessagingHostManifest::Load(manifest_path
, &error_message
);
141 LOG(ERROR
) << "Failed to load manifest for native messaging host "
142 << native_host_name
<< ": " << error_message
;
143 PostErrorResult(callback
, RESULT_NOT_FOUND
);
147 if (manifest
->name() != native_host_name
) {
148 LOG(ERROR
) << "Failed to load manifest for native messaging host "
150 << ": Invalid name specified in the manifest.";
151 PostErrorResult(callback
, RESULT_NOT_FOUND
);
155 if (!manifest
->allowed_origins().MatchesSecurityOrigin(origin
)) {
156 // Not an allowed origin.
157 PostErrorResult(callback
, RESULT_FORBIDDEN
);
161 base::FilePath host_path
= manifest
->path();
162 if (!host_path
.IsAbsolute()) {
163 // On Windows host path is allowed to be relative to the location of the
164 // manifest file. On all other platforms the path must be absolute.
166 host_path
= manifest_path
.DirName().Append(host_path
);
167 #else // defined(OS_WIN)
168 LOG(ERROR
) << "Native messaging host path must be absolute for "
170 PostErrorResult(callback
, RESULT_NOT_FOUND
);
172 #endif // !defined(OS_WIN)
175 // In case when the manifest file is there, but the host binary doesn't exist
176 // report the NOT_FOUND error.
177 if (!base::PathExists(host_path
)) {
179 << "Found manifest, but not the binary for native messaging host "
180 << native_host_name
<< ". Host path specified in the manifest: "
181 << host_path
.AsUTF8Unsafe();
182 PostErrorResult(callback
, RESULT_NOT_FOUND
);
186 CommandLine
command_line(host_path
);
187 command_line
.AppendArg(origin
.spec());
189 // Pass handle of the native view window to the native messaging host. This
190 // way the host will be able to create properly focused UI windows.
192 command_line
.AppendSwitchASCII(kParentWindowSwitchName
,
193 base::Int64ToString(window_handle_
));
194 #endif // !defined(OS_WIN)
196 base::ProcessHandle process_handle
;
197 base::File read_file
;
198 base::File write_file
;
199 if (NativeProcessLauncher::LaunchNativeProcess(
200 command_line
, &process_handle
, &read_file
, &write_file
)) {
201 PostResult(callback
, process_handle
, read_file
.Pass(), write_file
.Pass());
203 PostErrorResult(callback
, RESULT_FAILED_TO_START
);
207 void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread(
208 LaunchedCallback callback
,
210 base::ProcessHandle process_handle
,
211 base::File read_file
,
212 base::File write_file
) {
213 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
217 callback
.Run(result
, process_handle
, read_file
.Pass(), write_file
.Pass());
220 void NativeProcessLauncherImpl::Core::PostErrorResult(
221 const LaunchedCallback
& callback
,
222 LaunchResult error
) {
223 content::BrowserThread::PostTask(
224 content::BrowserThread::IO
, FROM_HERE
,
225 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread
, this,
226 callback
, error
, base::kNullProcessHandle
,
227 Passed(base::File()), Passed(base::File())));
230 void NativeProcessLauncherImpl::Core::PostResult(
231 const LaunchedCallback
& callback
,
232 base::ProcessHandle process_handle
,
233 base::File read_file
,
234 base::File write_file
) {
235 content::BrowserThread::PostTask(
236 content::BrowserThread::IO
, FROM_HERE
,
237 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread
, this,
238 callback
, RESULT_SUCCESS
, process_handle
,
239 Passed(read_file
.Pass()), Passed(write_file
.Pass())));
242 NativeProcessLauncherImpl::NativeProcessLauncherImpl(
243 bool allow_user_level_hosts
,
244 intptr_t window_handle
)
245 : core_(new Core(allow_user_level_hosts
, window_handle
)) {
248 NativeProcessLauncherImpl::~NativeProcessLauncherImpl() {
252 void NativeProcessLauncherImpl::Launch(const GURL
& origin
,
253 const std::string
& native_host_name
,
254 LaunchedCallback callback
) const {
255 core_
->Launch(origin
, native_host_name
, callback
);
261 scoped_ptr
<NativeProcessLauncher
> NativeProcessLauncher::CreateDefault(
262 bool allow_user_level_hosts
,
263 gfx::NativeView native_view
) {
264 intptr_t window_handle
= 0;
266 window_handle
= reinterpret_cast<intptr_t>(
267 views::HWNDForNativeView(native_view
));
269 return scoped_ptr
<NativeProcessLauncher
>(
270 new NativeProcessLauncherImpl(allow_user_level_hosts
, window_handle
));
273 } // namespace extensions