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 "chrome/common/chrome_switches.h"
21 #include "content/public/browser/browser_thread.h"
25 #include "ui/views/win/hwnd_util.h"
28 namespace extensions {
33 // Name of the command line switch used to pass handle of the native view to
34 // the native messaging host.
35 const char kParentWindowSwitchName[] = "parent-window";
36 #endif // defined(OS_WIN)
38 // Default implementation on NativeProcessLauncher interface.
39 class NativeProcessLauncherImpl : public NativeProcessLauncher {
41 NativeProcessLauncherImpl(bool allow_user_level_hosts,
42 intptr_t native_window);
43 virtual ~NativeProcessLauncherImpl();
45 virtual void Launch(const GURL& origin,
46 const std::string& native_host_name,
47 LaunchedCallback callback) const OVERRIDE;
50 class Core : public base::RefCountedThreadSafe<Core> {
52 Core(bool allow_user_level_hosts, intptr_t native_window);
53 void Launch(const GURL& origin,
54 const std::string& native_host_name,
55 LaunchedCallback callback);
59 friend class base::RefCountedThreadSafe<Core>;
62 void DoLaunchOnThreadPool(const GURL& origin,
63 const std::string& native_host_name,
64 LaunchedCallback callback);
65 void PostErrorResult(const LaunchedCallback& callback, LaunchResult error);
66 void PostResult(const LaunchedCallback& callback,
67 base::ProcessHandle process_handle,
69 base::File write_file);
70 void CallCallbackOnIOThread(LaunchedCallback callback,
72 base::ProcessHandle process_handle,
74 base::File write_file);
78 bool allow_user_level_hosts_;
80 // Handle of the native window corresponding to the extension.
81 intptr_t window_handle_;
83 DISALLOW_COPY_AND_ASSIGN(Core);
86 scoped_refptr<Core> core_;
88 DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl);
91 NativeProcessLauncherImpl::Core::Core(bool allow_user_level_hosts,
92 intptr_t window_handle)
94 allow_user_level_hosts_(allow_user_level_hosts),
95 window_handle_(window_handle) {
98 NativeProcessLauncherImpl::Core::~Core() {
102 void NativeProcessLauncherImpl::Core::Detach() {
103 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
107 void NativeProcessLauncherImpl::Core::Launch(
109 const std::string& native_host_name,
110 LaunchedCallback callback) {
111 content::BrowserThread::PostBlockingPoolTask(
112 FROM_HERE, base::Bind(&Core::DoLaunchOnThreadPool, this,
113 origin, native_host_name, callback));
116 void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool(
118 const std::string& native_host_name,
119 LaunchedCallback callback) {
120 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
122 if (!NativeMessagingHostManifest::IsValidName(native_host_name)) {
123 PostErrorResult(callback, RESULT_INVALID_NAME);
127 std::string error_message;
128 base::FilePath manifest_path =
129 FindManifest(native_host_name, allow_user_level_hosts_, &error_message);
131 if (manifest_path.empty()) {
132 LOG(ERROR) << "Can't find manifest for native messaging host "
134 PostErrorResult(callback, RESULT_NOT_FOUND);
138 scoped_ptr<NativeMessagingHostManifest> manifest =
139 NativeMessagingHostManifest::Load(manifest_path, &error_message);
142 LOG(ERROR) << "Failed to load manifest for native messaging host "
143 << native_host_name << ": " << error_message;
144 PostErrorResult(callback, RESULT_NOT_FOUND);
148 if (manifest->name() != native_host_name) {
149 LOG(ERROR) << "Failed to load manifest for native messaging host "
151 << ": Invalid name specified in the manifest.";
152 PostErrorResult(callback, RESULT_NOT_FOUND);
156 if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) {
157 // Not an allowed origin.
158 PostErrorResult(callback, RESULT_FORBIDDEN);
162 base::FilePath host_path = manifest->path();
163 if (!host_path.IsAbsolute()) {
164 // On Windows host path is allowed to be relative to the location of the
165 // manifest file. On all other platforms the path must be absolute.
167 host_path = manifest_path.DirName().Append(host_path);
168 #else // defined(OS_WIN)
169 LOG(ERROR) << "Native messaging host path must be absolute for "
171 PostErrorResult(callback, RESULT_NOT_FOUND);
173 #endif // !defined(OS_WIN)
176 // In case when the manifest file is there, but the host binary doesn't exist
177 // report the NOT_FOUND error.
178 if (!base::PathExists(host_path)) {
180 << "Found manifest, but not the binary for native messaging host "
181 << native_host_name << ". Host path specified in the manifest: "
182 << host_path.AsUTF8Unsafe();
183 PostErrorResult(callback, RESULT_NOT_FOUND);
187 CommandLine command_line(host_path);
188 command_line.AppendArg(origin.spec());
190 // Pass handle of the native view window to the native messaging host. This
191 // way the host will be able to create properly focused UI windows.
193 command_line.AppendSwitchASCII(kParentWindowSwitchName,
194 base::Int64ToString(window_handle_));
195 #endif // !defined(OS_WIN)
197 base::ProcessHandle process_handle;
198 base::File read_file;
199 base::File write_file;
200 if (NativeProcessLauncher::LaunchNativeProcess(
201 command_line, &process_handle, &read_file, &write_file)) {
202 PostResult(callback, process_handle, read_file.Pass(), write_file.Pass());
204 PostErrorResult(callback, RESULT_FAILED_TO_START);
208 void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread(
209 LaunchedCallback callback,
211 base::ProcessHandle process_handle,
212 base::File read_file,
213 base::File write_file) {
214 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
218 callback.Run(result, process_handle, read_file.Pass(), write_file.Pass());
221 void NativeProcessLauncherImpl::Core::PostErrorResult(
222 const LaunchedCallback& callback,
223 LaunchResult error) {
224 content::BrowserThread::PostTask(
225 content::BrowserThread::IO, FROM_HERE,
226 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this,
227 callback, error, base::kNullProcessHandle,
228 Passed(base::File()), Passed(base::File())));
231 void NativeProcessLauncherImpl::Core::PostResult(
232 const LaunchedCallback& callback,
233 base::ProcessHandle process_handle,
234 base::File read_file,
235 base::File write_file) {
236 content::BrowserThread::PostTask(
237 content::BrowserThread::IO, FROM_HERE,
238 base::Bind(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this,
239 callback, RESULT_SUCCESS, process_handle,
240 Passed(read_file.Pass()), Passed(write_file.Pass())));
243 NativeProcessLauncherImpl::NativeProcessLauncherImpl(
244 bool allow_user_level_hosts,
245 intptr_t window_handle)
246 : core_(new Core(allow_user_level_hosts, window_handle)) {
249 NativeProcessLauncherImpl::~NativeProcessLauncherImpl() {
253 void NativeProcessLauncherImpl::Launch(const GURL& origin,
254 const std::string& native_host_name,
255 LaunchedCallback callback) const {
256 core_->Launch(origin, native_host_name, callback);
262 scoped_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault(
263 bool allow_user_level_hosts,
264 gfx::NativeView native_view) {
265 intptr_t window_handle = 0;
267 window_handle = reinterpret_cast<intptr_t>(
268 views::HWNDForNativeView(native_view));
270 return scoped_ptr<NativeProcessLauncher>(
271 new NativeProcessLauncherImpl(allow_user_level_hosts, window_handle));
274 } // namespace extensions