- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / messaging / native_process_launcher.cc
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.
4
5 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.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"
21 #include "url/gurl.h"
22
23 namespace extensions {
24
25 namespace {
26
27 #if defined(OS_WIN)
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)
32
33 base::FilePath GetHostManifestPathFromCommandLine(
34     const std::string& native_host_name) {
35   const std::string& value =
36       CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
37           switches::kNativeMessagingHosts);
38   if (value.empty())
39     return base::FilePath();
40
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)
47       continue;
48     if (key_and_value[0] == native_host_name)
49       return base::FilePath::FromUTF8Unsafe(key_and_value[1]);
50   }
51
52   return base::FilePath();
53 }
54
55
56 // Default implementation on NativeProcessLauncher interface.
57 class NativeProcessLauncherImpl : public NativeProcessLauncher {
58  public:
59   explicit NativeProcessLauncherImpl(gfx::NativeView native_view);
60   virtual ~NativeProcessLauncherImpl();
61
62   virtual void Launch(const GURL& origin,
63                       const std::string& native_host_name,
64                       LaunchedCallback callback) const OVERRIDE;
65
66  private:
67   class Core : public base::RefCountedThreadSafe<Core> {
68    public:
69     explicit Core(gfx::NativeView native_view);
70     void Launch(const GURL& origin,
71                 const std::string& native_host_name,
72                 LaunchedCallback callback);
73     void Detach();
74
75    private:
76     friend class base::RefCountedThreadSafe<Core>;
77     virtual ~Core();
78
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,
88                                 LaunchResult result,
89                                 base::ProcessHandle process_handle,
90                                 base::PlatformFile read_file,
91                                 base::PlatformFile write_file);
92
93     bool detached_;
94
95     // Handle of the native view corrsponding to the extension.
96     gfx::NativeView native_view_;
97
98     DISALLOW_COPY_AND_ASSIGN(Core);
99   };
100
101   scoped_refptr<Core> core_;
102
103   DISALLOW_COPY_AND_ASSIGN(NativeProcessLauncherImpl);
104 };
105
106 NativeProcessLauncherImpl::Core::Core(gfx::NativeView native_view)
107     : detached_(false),
108       native_view_(native_view) {
109 }
110
111 NativeProcessLauncherImpl::Core::~Core() {
112   DCHECK(detached_);
113 }
114
115 void NativeProcessLauncherImpl::Core::Detach() {
116   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
117   detached_ = true;
118 }
119
120 void NativeProcessLauncherImpl::Core::Launch(
121     const GURL& origin,
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));
127 }
128
129 void NativeProcessLauncherImpl::Core::DoLaunchOnThreadPool(
130     const GURL& origin,
131     const std::string& native_host_name,
132     LaunchedCallback callback) {
133   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
134
135   if (!NativeMessagingHostManifest::IsValidName(native_host_name)) {
136     PostErrorResult(callback, RESULT_INVALID_NAME);
137     return;
138   }
139
140   std::string error_message;
141   scoped_ptr<NativeMessagingHostManifest> manifest;
142
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);
148
149   if (manifest_path.empty()) {
150     LOG(ERROR) << "Can't find manifest for native messaging host "
151                << native_host_name;
152     PostErrorResult(callback, RESULT_NOT_FOUND);
153     return;
154   }
155
156   manifest = NativeMessagingHostManifest::Load(manifest_path, &error_message);
157
158   if (!manifest) {
159     LOG(ERROR) << "Failed to load manifest for native messaging host "
160                << native_host_name << ": " << error_message;
161     PostErrorResult(callback, RESULT_NOT_FOUND);
162     return;
163   }
164
165   if (manifest->name() != native_host_name) {
166     LOG(ERROR) << "Failed to load manifest for native messaging host "
167                << native_host_name
168                << ": Invalid name specified in the manifest.";
169     PostErrorResult(callback, RESULT_NOT_FOUND);
170     return;
171   }
172
173   if (!manifest->allowed_origins().MatchesSecurityOrigin(origin)) {
174     // Not an allowed origin.
175     PostErrorResult(callback, RESULT_FORBIDDEN);
176     return;
177   }
178
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.
183 #if defined(OS_WIN)
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 "
187                << native_host_name;
188     PostErrorResult(callback, RESULT_NOT_FOUND);
189     return;
190 #endif  // !defined(OS_WIN)
191   }
192
193   CommandLine command_line(host_path);
194   command_line.AppendArg(origin.spec());
195
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.
198 #if defined(OS_WIN)
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)
203
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);
210   } else {
211     PostErrorResult(callback, RESULT_FAILED_TO_START);
212   }
213 }
214
215 void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread(
216     LaunchedCallback callback,
217     LaunchResult result,
218     base::ProcessHandle process_handle,
219     base::PlatformFile read_file,
220     base::PlatformFile write_file) {
221   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
222   if (detached_) {
223     if (read_file != base::kInvalidPlatformFileValue)
224       base::ClosePlatformFile(read_file);
225     if (write_file != base::kInvalidPlatformFileValue)
226       base::ClosePlatformFile(write_file);
227     return;
228   }
229
230   callback.Run(result, process_handle, read_file, write_file);
231 }
232
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));
242 }
243
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,
253                  write_file));
254 }
255
256 NativeProcessLauncherImpl::NativeProcessLauncherImpl(
257     gfx::NativeView native_view)
258   : core_(new Core(native_view)) {
259 }
260
261 NativeProcessLauncherImpl::~NativeProcessLauncherImpl() {
262   core_->Detach();
263 }
264
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);
269 }
270
271 }  // namespace
272
273 // static
274 scoped_ptr<NativeProcessLauncher> NativeProcessLauncher::CreateDefault(
275     gfx::NativeView native_view) {
276   return scoped_ptr<NativeProcessLauncher>(
277       new NativeProcessLauncherImpl(native_view));
278 }
279
280 }  // namespace extensions