Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1624)

Unified Diff: chrome/browser/extensions/api/native_message/native_thread_delegate.cc

Issue 10818013: Native Messaging! (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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_);
+ }
+ }
+}
+

Powered by Google App Engine
This is Rietveld 408576698