Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / messaging / native_message_process_host.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_message_process_host.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/process/kill.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h"
13 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
14 #include "chrome/common/chrome_version_info.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/common/constants.h"
17 #include "extensions/common/features/feature.h"
18 #include "net/base/file_stream.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_util.h"
22 #include "url/gurl.h"
23
24 namespace {
25
26 // Maximum message size in bytes for messages received from Native Messaging
27 // hosts. Message size is limited mainly to prevent Chrome from crashing when
28 // native application misbehaves (e.g. starts writing garbage to the pipe).
29 const size_t kMaximumMessageSize = 1024 * 1024;
30
31 // Message header contains 4-byte integer size of the message.
32 const size_t kMessageHeaderSize = 4;
33
34 // Size of the buffer to be allocated for each read.
35 const size_t kReadBufferSize = 4096;
36
37 }  // namespace
38
39 namespace extensions {
40
41 NativeMessageProcessHost::NativeMessageProcessHost(
42     const std::string& source_extension_id,
43     const std::string& native_host_name,
44     scoped_ptr<NativeProcessLauncher> launcher)
45     : client_(NULL),
46       source_extension_id_(source_extension_id),
47       native_host_name_(native_host_name),
48       launcher_(launcher.Pass()),
49       closed_(false),
50       process_handle_(base::kNullProcessHandle),
51 #if defined(OS_POSIX)
52       read_file_(-1),
53 #endif
54       read_pending_(false),
55       write_pending_(false),
56       weak_factory_(this) {
57   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
58
59   task_runner_ = content::BrowserThread::GetMessageLoopProxyForThread(
60       content::BrowserThread::IO);
61 }
62
63 NativeMessageProcessHost::~NativeMessageProcessHost() {
64   DCHECK(task_runner_->BelongsToCurrentThread());
65 }
66
67 // static
68 scoped_ptr<NativeMessageHost> NativeMessageHost::Create(
69     gfx::NativeView native_view,
70     const std::string& source_extension_id,
71     const std::string& native_host_name,
72     bool allow_user_level,
73     std::string* error_message) {
74   return NativeMessageProcessHost::CreateWithLauncher(
75       source_extension_id,
76       native_host_name,
77       NativeProcessLauncher::CreateDefault(allow_user_level, native_view));
78 }
79
80 // static
81 scoped_ptr<NativeMessageHost> NativeMessageProcessHost::CreateWithLauncher(
82     const std::string& source_extension_id,
83     const std::string& native_host_name,
84     scoped_ptr<NativeProcessLauncher> launcher) {
85   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
86
87   scoped_ptr<NativeMessageHost> process(
88       new NativeMessageProcessHost(source_extension_id,
89                                    native_host_name,
90                                    launcher.Pass()));
91
92   return process.Pass();
93 }
94
95 void NativeMessageProcessHost::LaunchHostProcess() {
96   DCHECK(task_runner_->BelongsToCurrentThread());
97
98   GURL origin(std::string(kExtensionScheme) + "://" + source_extension_id_);
99   launcher_->Launch(origin, native_host_name_,
100                     base::Bind(&NativeMessageProcessHost::OnHostProcessLaunched,
101                                weak_factory_.GetWeakPtr()));
102 }
103
104 void NativeMessageProcessHost::OnHostProcessLaunched(
105     NativeProcessLauncher::LaunchResult result,
106     base::ProcessHandle process_handle,
107     base::File read_file,
108     base::File write_file) {
109   DCHECK(task_runner_->BelongsToCurrentThread());
110
111   switch (result) {
112     case NativeProcessLauncher::RESULT_INVALID_NAME:
113       Close(kInvalidNameError);
114       return;
115     case NativeProcessLauncher::RESULT_NOT_FOUND:
116       Close(kNotFoundError);
117       return;
118     case NativeProcessLauncher::RESULT_FORBIDDEN:
119       Close(kForbiddenError);
120       return;
121     case NativeProcessLauncher::RESULT_FAILED_TO_START:
122       Close(kFailedToStartError);
123       return;
124     case NativeProcessLauncher::RESULT_SUCCESS:
125       break;
126   }
127
128   process_handle_ = process_handle;
129 #if defined(OS_POSIX)
130   // This object is not the owner of the file so it should not keep an fd.
131   read_file_ = read_file.GetPlatformFile();
132 #endif
133
134   scoped_refptr<base::TaskRunner> task_runner(
135       content::BrowserThread::GetBlockingPool()->
136           GetTaskRunnerWithShutdownBehavior(
137               base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
138
139   read_stream_.reset(new net::FileStream(read_file.Pass(), task_runner));
140   write_stream_.reset(new net::FileStream(write_file.Pass(), task_runner));
141
142   WaitRead();
143   DoWrite();
144 }
145
146 void NativeMessageProcessHost::OnMessage(const std::string& json) {
147   DCHECK(task_runner_->BelongsToCurrentThread());
148
149   if (closed_)
150     return;
151
152   // Allocate new buffer for the message.
153   scoped_refptr<net::IOBufferWithSize> buffer =
154       new net::IOBufferWithSize(json.size() + kMessageHeaderSize);
155
156   // Copy size and content of the message to the buffer.
157   COMPILE_ASSERT(sizeof(uint32) == kMessageHeaderSize, incorrect_header_size);
158   *reinterpret_cast<uint32*>(buffer->data()) = json.size();
159   memcpy(buffer->data() + kMessageHeaderSize, json.data(), json.size());
160
161   // Push new message to the write queue.
162   write_queue_.push(buffer);
163
164   // Send() may be called before the host process is started. In that case the
165   // message will be written when OnHostProcessLaunched() is called. If it's
166   // already started then write the message now.
167   if (write_stream_)
168     DoWrite();
169 }
170
171 void NativeMessageProcessHost::Start(Client* client) {
172   DCHECK(task_runner_->BelongsToCurrentThread());
173   DCHECK(!client_);
174   client_ = client;
175   // It's safe to use base::Unretained() here because NativeMessagePort always
176   // deletes us on the IO thread.
177   task_runner_->PostTask(
178       FROM_HERE,
179       base::Bind(&NativeMessageProcessHost::LaunchHostProcess,
180                  weak_factory_.GetWeakPtr()));
181 }
182
183 scoped_refptr<base::SingleThreadTaskRunner>
184 NativeMessageProcessHost::task_runner() const {
185   return task_runner_;
186 }
187
188 #if defined(OS_POSIX)
189 void NativeMessageProcessHost::OnFileCanReadWithoutBlocking(int fd) {
190   DCHECK_EQ(fd, read_file_);
191   DoRead();
192 }
193
194 void NativeMessageProcessHost::OnFileCanWriteWithoutBlocking(int fd) {
195   NOTREACHED();
196 }
197 #endif  // !defined(OS_POSIX)
198
199 void NativeMessageProcessHost::ReadNowForTesting() {
200   DoRead();
201 }
202
203 void NativeMessageProcessHost::WaitRead() {
204   if (closed_)
205     return;
206
207   DCHECK(!read_pending_);
208
209   // On POSIX FileStream::Read() uses blocking thread pool, so it's better to
210   // wait for the file to become readable before calling DoRead(). Otherwise it
211   // would always be consuming one thread in the thread pool. On Windows
212   // FileStream uses overlapped IO, so that optimization isn't necessary there.
213 #if defined(OS_POSIX)
214   base::MessageLoopForIO::current()->WatchFileDescriptor(
215     read_file_, false /* persistent */,
216     base::MessageLoopForIO::WATCH_READ, &read_watcher_, this);
217 #else  // defined(OS_POSIX)
218   DoRead();
219 #endif  // defined(!OS_POSIX)
220 }
221
222 void NativeMessageProcessHost::DoRead() {
223   DCHECK(task_runner_->BelongsToCurrentThread());
224
225   while (!closed_ && !read_pending_) {
226     read_buffer_ = new net::IOBuffer(kReadBufferSize);
227     int result =
228         read_stream_->Read(read_buffer_.get(), kReadBufferSize,
229                            base::Bind(&NativeMessageProcessHost::OnRead,
230                                       weak_factory_.GetWeakPtr()));
231     HandleReadResult(result);
232   }
233 }
234
235 void NativeMessageProcessHost::OnRead(int result) {
236   DCHECK(task_runner_->BelongsToCurrentThread());
237   DCHECK(read_pending_);
238   read_pending_ = false;
239
240   HandleReadResult(result);
241   WaitRead();
242 }
243
244 void NativeMessageProcessHost::HandleReadResult(int result) {
245   DCHECK(task_runner_->BelongsToCurrentThread());
246
247   if (closed_)
248     return;
249
250   if (result > 0) {
251     ProcessIncomingData(read_buffer_->data(), result);
252   } else if (result == net::ERR_IO_PENDING) {
253     read_pending_ = true;
254   } else if (result == 0 || result == net::ERR_CONNECTION_RESET) {
255     // On Windows we get net::ERR_CONNECTION_RESET for a broken pipe, while on
256     // Posix read() returns 0 in that case.
257     Close(kNativeHostExited);
258   } else {
259     LOG(ERROR) << "Error when reading from Native Messaging host: " << result;
260     Close(kHostInputOuputError);
261   }
262 }
263
264 void NativeMessageProcessHost::ProcessIncomingData(
265     const char* data, int data_size) {
266   DCHECK(task_runner_->BelongsToCurrentThread());
267
268   incoming_data_.append(data, data_size);
269
270   while (true) {
271     if (incoming_data_.size() < kMessageHeaderSize)
272       return;
273
274     size_t message_size =
275         *reinterpret_cast<const uint32*>(incoming_data_.data());
276
277     if (message_size > kMaximumMessageSize) {
278       LOG(ERROR) << "Native Messaging host tried sending a message that is "
279                  << message_size << " bytes long.";
280       Close(kHostInputOuputError);
281       return;
282     }
283
284     if (incoming_data_.size() < message_size + kMessageHeaderSize)
285       return;
286
287     client_->PostMessageFromNativeHost(
288         incoming_data_.substr(kMessageHeaderSize, message_size));
289
290     incoming_data_.erase(0, kMessageHeaderSize + message_size);
291   }
292 }
293
294 void NativeMessageProcessHost::DoWrite() {
295   DCHECK(task_runner_->BelongsToCurrentThread());
296
297   while (!write_pending_ && !closed_) {
298     if (!current_write_buffer_.get() ||
299         !current_write_buffer_->BytesRemaining()) {
300       if (write_queue_.empty())
301         return;
302       current_write_buffer_ = new net::DrainableIOBuffer(
303           write_queue_.front().get(), write_queue_.front()->size());
304       write_queue_.pop();
305     }
306
307     int result =
308         write_stream_->Write(current_write_buffer_.get(),
309                              current_write_buffer_->BytesRemaining(),
310                              base::Bind(&NativeMessageProcessHost::OnWritten,
311                                         weak_factory_.GetWeakPtr()));
312     HandleWriteResult(result);
313   }
314 }
315
316 void NativeMessageProcessHost::HandleWriteResult(int result) {
317   DCHECK(task_runner_->BelongsToCurrentThread());
318
319   if (result <= 0) {
320     if (result == net::ERR_IO_PENDING) {
321       write_pending_ = true;
322     } else {
323       LOG(ERROR) << "Error when writing to Native Messaging host: " << result;
324       Close(kHostInputOuputError);
325     }
326     return;
327   }
328
329   current_write_buffer_->DidConsume(result);
330 }
331
332 void NativeMessageProcessHost::OnWritten(int result) {
333   DCHECK(task_runner_->BelongsToCurrentThread());
334
335   DCHECK(write_pending_);
336   write_pending_ = false;
337
338   HandleWriteResult(result);
339   DoWrite();
340 }
341
342 void NativeMessageProcessHost::Close(const std::string& error_message) {
343   DCHECK(task_runner_->BelongsToCurrentThread());
344
345   if (!closed_) {
346     closed_ = true;
347     read_stream_.reset();
348     write_stream_.reset();
349     client_->CloseChannel(error_message);
350   }
351
352   if (process_handle_ != base::kNullProcessHandle) {
353     // Kill the host process if necessary to make sure we don't leave zombies.
354     // On OSX base::EnsureProcessTerminated() may block, so we have to post a
355     // task on the blocking pool.
356 #if defined(OS_MACOSX)
357     content::BrowserThread::PostBlockingPoolTask(
358         FROM_HERE, base::Bind(&base::EnsureProcessTerminated, process_handle_));
359 #else
360     base::EnsureProcessTerminated(process_handle_);
361 #endif
362     process_handle_ = base::kNullProcessHandle;
363   }
364 }
365
366 }  // namespace extensions