Index: chrome/browser/extensions/api/native_message/native_thread_delegate.cc |
diff --git a/chrome/browser/extensions/api/native_message/native_thread_delegate.cc b/chrome/browser/extensions/api/native_message/native_thread_delegate.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ad742e783a52f42b213c9c67b3c5292f36fc3782 |
--- /dev/null |
+++ b/chrome/browser/extensions/api/native_message/native_thread_delegate.cc |
@@ -0,0 +1,222 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/extensions/api/native_message/native_thread_delegate.h" |
+ |
+#include <unistd.h> |
+ |
+#include "base/bind.h" |
+#include "base/command_line.h" |
+#include "base/file_util.h" |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/logging.h" |
+#include "base/process_util.h" |
+#include "base/values.h" |
+#include "content/public/browser/browser_thread.h" |
+ |
+namespace { |
+ |
+// TODO(eriq): Figure out good values for these constants. |
+const int kExitTimeoutMS = 100; |
+const uint32_t kMaxMessageDataLength = 4096; |
+ |
+} // namespace |
+ |
+NativeThreadDelegate::NativeThreadDelegate(const DictionaryValue* data, |
+ scoped_ptr<FilePath> path, |
+ ExecutionFinishedCallback callback) |
+ : message_data_(data), |
+ path_(path.release()), |
+ callback_(callback) { |
+} |
+ |
+NativeThreadDelegate::~NativeThreadDelegate() { |
+} |
+ |
+void NativeThreadDelegate::Run() { |
+ base::FileHandleMappingVector fd_map; |
+ |
+ int read_pipe_fds[2]; |
+ if (HANDLE_EINTR(pipe(read_pipe_fds)) != 0) { |
+ Finish(false, std::string("Bad read pipe"), scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ file_util::ScopedFD read_pipe_read_fd(&read_pipe_fds[0]); |
+ file_util::ScopedFD read_pipe_write_fd(&read_pipe_fds[1]); |
Matt Perry
2012/07/23 23:47:03
A lot of this stuff is posix only, in which case t
|
+ fd_map.push_back(std::make_pair(*read_pipe_write_fd, STDOUT_FILENO)); |
+ |
+ int write_pipe_fds[2]; |
+ if (HANDLE_EINTR(pipe(write_pipe_fds)) != 0) { |
+ Finish(false, std::string("Bad write pipe"), |
+ scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ file_util::ScopedFD write_pipe_read_fd(&write_pipe_fds[0]); |
+ file_util::ScopedFD write_pipe_write_fd(&write_pipe_fds[1]); |
+ fd_map.push_back(std::make_pair(*write_pipe_read_fd, STDIN_FILENO)); |
+ |
+ CommandLine line(*path_); |
+ base::LaunchOptions options; |
+ options.fds_to_remap = &fd_map; |
+ if (!base::LaunchProcess(line, options, &handle_)) { |
+ Finish(false, std::string("Error launching process"), |
+ scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ |
+ // We will not be reading from the write pipe, nor writing from the read pipe. |
+ write_pipe_read_fd.reset(); |
+ read_pipe_write_fd.reset(); |
+ |
+ if (!WriteMessage(TYPE_SEND_MESSAGE_REQUEST, *write_pipe_write_fd)) { |
+ Finish(false, std::string("Error sending message to the native app"), |
+ scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ write_pipe_write_fd.reset(); |
+ |
+ MessageType response_type; |
+ DictionaryValue* response_data; |
+ if (!ReadMessage(&response_type, &response_data, *read_pipe_read_fd)) { |
+ Finish(false, std::string("Error reading"), scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ scoped_ptr<DictionaryValue> scoped_response_data(response_data); |
+ read_pipe_read_fd.reset(); |
+ |
+ if (response_type != TYPE_SEND_MESSAGE_RESPONSE) { |
+ Finish(false, std::string("Incorrect response type."), |
+ scoped_ptr<DictionaryValue>()); |
+ return; |
+ } |
+ |
+ Finish(true, std::string(), scoped_response_data.Pass()); |
+ exit(); |
+} |
+ |
+bool NativeThreadDelegate::WriteMessage(MessageType type, |
+ int fd) { |
+ std::string json_data; |
+ base::JSONWriter::Write(message_data_, &json_data); |
+ uint32_t json_length = json_data.length(); |
+ |
+ // Write the message type. |
+ if (HANDLE_EINTR(write(fd, &type, 1)) < 1) { |
Matt Perry
2012/07/23 23:47:03
base::Pickle might be a good cross-platform altern
|
+ LOG(ERROR) << "Error writing message type to the native client."; |
+ return false; |
+ } |
+ |
+ // Write the json data length. |
+ if (HANDLE_EINTR(write(fd, &json_length, 4)) < 1) { |
+ LOG(ERROR) << "Error writing json data length to the native client."; |
+ return false; |
+ } |
+ |
+ // Write the json data. |
+ if (HANDLE_EINTR(write(fd, json_data.data(), json_length)) < 1) { |
+ LOG(ERROR) << "Error writing json data to the native client."; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool NativeThreadDelegate::ReadMessage(MessageType* type, |
+ DictionaryValue** data, |
+ int fd) { |
+ char json_data[kMaxMessageDataLength]; |
+ |
+ // Get message type. |
+ unsigned char byte_type; |
+ if (HANDLE_EINTR(read(fd, &byte_type, 1)) < 1) { |
+ LOG(ERROR) << "Error reading the message type."; |
+ return false; |
+ } |
+ |
+ if (byte_type < 0 || byte_type >= NUM_MESSAGE_TYPES) { |
+ LOG(ERROR) << byte_type << " is not a valid message type."; |
+ return false; |
+ } |
+ *type = static_cast<MessageType>(byte_type); |
+ |
+ // Read the length of the json data. |
+ uint32_t data_length; |
+ if (HANDLE_EINTR(read(fd, &data_length, 4)) < 1) { |
+ LOG(ERROR) << "Error reading the json data length."; |
+ return false; |
+ } |
+ |
+ if (data_length > kMaxMessageDataLength) { |
+ LOG(ERROR) << data_length << " is too large for the length of a message. " |
+ << "Max message size is " << kMaxMessageDataLength; |
+ return false; |
+ } |
+ |
+ ssize_t bytes_read = HANDLE_EINTR(read(fd, &json_data, data_length)); |
+ if (bytes_read < 1) { |
+ LOG(ERROR) << "Error reading the json data."; |
+ return false; |
+ } else if (bytes_read != data_length) { |
+ LOG(ERROR) << "Advertised length from client differed from actual size. " |
+ << "Actual Size: " << bytes_read << ", " |
+ << "Advertised Size: " << data_length; |
+ return false; |
+ } |
+ json_data[bytes_read] = '\0'; |
+ |
+ std::string json_error_str; |
+ int json_error_code; |
+ Value* value = base::JSONReader::ReadAndReturnError(json_data, 0, |
+ &json_error_code, |
+ &json_error_str); |
+ if (!value) { |
+ LOG(ERROR) << "Error reading JSON data into string. " |
+ << "Code: " << json_error_code << ". " |
+ << "Message: " << json_error_str; |
+ return false; |
+ } |
+ |
+ if (!value->GetAsDictionary(data)) { |
+ LOG(ERROR) << "Error converting the message to a DictionaryValue."; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+void NativeThreadDelegate::Finish(bool success, |
+ const std::string& error, |
+ scoped_ptr<DictionaryValue> result) { |
+ scoped_ptr<std::string> scoped_error(new std::string(error)); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&NativeThreadDelegate::CallbackOnUIThread, |
+ base::Unretained(this), |
+ success, |
+ base::Passed(&scoped_error), |
+ base::Passed(&result))); |
+} |
+ |
+void NativeThreadDelegate::CallbackOnUIThread( |
+ bool success, scoped_ptr<std::string> error, |
+ scoped_ptr<DictionaryValue> result) { |
+ CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ callback_.Run(success, error.Pass(), result.Pass()); |
+} |
+ |
+void NativeThreadDelegate::exit() { |
+ int exit_code = 0; |
+ if (!base::WaitForExitCodeWithTimeout(handle_, &exit_code, kExitTimeoutMS)) { |
+ // Kill the process. |
+ if (!base::KillProcess(handle_, |
+ -1, /* error exit code */ |
+ false /* Don't wait for the process to exit */)) { |
+ // Really kill the process. |
+ base::EnsureProcessTerminated(handle_); |
+ } |
+ } |
+} |
+ |