diff options
Diffstat (limited to 'chrome/browser/automation/extension_port_container.cc')
-rw-r--r-- | chrome/browser/automation/extension_port_container.cc | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc new file mode 100644 index 0000000..e93f42c --- /dev/null +++ b/chrome/browser/automation/extension_port_container.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2010 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/automation/extension_port_container.h" + +#include "base/logging.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/values.h" +#include "chrome/browser/automation/automation_provider.h" +#include "chrome/browser/automation/extension_automation_constants.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/render_messages.h" +#include "chrome/test/automation/automation_messages.h" + +// TODO(siggi): Find a more structured way to read and write JSON messages. + +namespace ext = extension_automation_constants; + +ExtensionPortContainer::ExtensionPortContainer(AutomationProvider* automation, + int tab_handle) : + automation_(automation), service_(NULL), port_id_(-1), + tab_handle_(tab_handle) { + service_ = automation_->profile()->GetExtensionMessageService(); + DCHECK(service_); +} + +ExtensionPortContainer::~ExtensionPortContainer() { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + if (port_id_ != -1) + service_->CloseChannel(port_id_); + + NotificationService::current()->Notify( + NotificationType::EXTENSION_PORT_DELETED_DEBUG, + Source<IPC::Message::Sender>(this), + NotificationService::NoDetails()); +} + +bool ExtensionPortContainer::PostResponseToExternalPort( + const std::string& message) { + return automation_->Send( + new AutomationMsg_ForwardMessageToExternalHost( + 0, tab_handle_, message, ext::kAutomationOrigin, + ext::kAutomationPortResponseTarget)); +} + +bool ExtensionPortContainer::PostMessageToExternalPort( + const std::string& message) { + return automation_->Send( + new AutomationMsg_ForwardMessageToExternalHost( + 0, tab_handle_, message, + ext::kAutomationOrigin, + ext::kAutomationPortRequestTarget)); +} + +void ExtensionPortContainer::PostMessageFromExternalPort( + const std::string &message) { + service_->PostMessageFromRenderer(port_id_, message); +} + +bool ExtensionPortContainer::Connect(const std::string &extension_id, + int process_id, + int routing_id, + int connection_id, + const std::string& channel_name, + const std::string& tab_json) { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + port_id_ = service_->OpenSpecialChannelToExtension( + extension_id, channel_name, tab_json, this); + if (port_id_ == -1) { + // In this case a disconnect message has been dispatched. + return false; + } + + SendConnectionResponse(connection_id, port_id_); + return true; +} + +void ExtensionPortContainer::SendConnectionResponse(int connection_id, + int port_id) { + // Compose the reply message. + scoped_ptr<DictionaryValue> msg_dict(new DictionaryValue()); + msg_dict->SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_OPENED); + msg_dict->SetInteger(ext::kAutomationConnectionIdKey, connection_id); + msg_dict->SetInteger(ext::kAutomationPortIdKey, port_id); + + std::string msg_json; + base::JSONWriter::Write(msg_dict.get(), false, &msg_json); + + PostResponseToExternalPort(msg_json); +} + +bool ExtensionPortContainer::Send(IPC::Message *message) { + DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); + + IPC_BEGIN_MESSAGE_MAP(ExtensionPortContainer, *message) + IPC_MESSAGE_HANDLER(ViewMsg_ExtensionMessageInvoke, + OnExtensionMessageInvoke) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() + + delete message; + return true; +} + +void ExtensionPortContainer::OnExtensionMessageInvoke( + const std::string& function_name, + const ListValue& args, + bool requires_incognito_access, + const GURL& event_url) { + if (function_name == ExtensionMessageService::kDispatchOnMessage) { + DCHECK_EQ(args.GetSize(), 2u); + + std::string message; + int source_port_id; + if (args.GetString(0, &message) && args.GetInteger(1, &source_port_id)) + OnExtensionHandleMessage(message, source_port_id); + } else if (function_name == ExtensionMessageService::kDispatchOnDisconnect) { + DCHECK_EQ(args.GetSize(), 1u); + int port_id; + if (args.GetInteger(0, &port_id)) + OnExtensionPortDisconnected(port_id); + } else if (function_name == ExtensionMessageService::kDispatchOnConnect) { + // Do nothing. + // TODO(siggi): implement + } else { + NOTREACHED() << function_name << " shouldn't be called."; + } +} + +void ExtensionPortContainer::OnExtensionHandleMessage( + const std::string& message, int source_port_id) { + // Compose the reply message and fire it away. + DictionaryValue msg_dict; + msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::POST_MESSAGE); + msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_); + msg_dict.SetString(ext::kAutomationMessageDataKey, message); + + std::string msg_json; + base::JSONWriter::Write(&msg_dict, false, &msg_json); + + PostMessageToExternalPort(msg_json); +} + +void ExtensionPortContainer::OnExtensionPortDisconnected(int source_port_id) { + // Compose the disconnect message and fire it away. + DictionaryValue msg_dict; + msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_CLOSED); + msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_); + + std::string msg_json; + base::JSONWriter::Write(&msg_dict, false, &msg_json); + + PostMessageToExternalPort(msg_json); +} + +bool ExtensionPortContainer::InterceptMessageFromExternalHost( + const std::string& message, const std::string& origin, + const std::string& target, AutomationProvider* automation, + RenderViewHost *view_host, int tab_handle) { + if (target != ext::kAutomationPortRequestTarget) + return false; + + if (origin != ext::kAutomationOrigin) { + // TODO(siggi): Should we block the message on wrong origin? + LOG(WARNING) << "Wrong origin on automation port message " << origin; + } + + scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); + DCHECK(message_value->IsType(Value::TYPE_DICTIONARY)); + if (!message_value->IsType(Value::TYPE_DICTIONARY)) + return true; + + DictionaryValue* message_dict = + reinterpret_cast<DictionaryValue*>(message_value.get()); + + int command = -1; + bool got_value = message_dict->GetInteger(ext::kAutomationRequestIdKey, + &command); + DCHECK(got_value); + if (!got_value) + return true; + + if (command == ext::OPEN_CHANNEL) { + // Extract the "extension_id" and "connection_id" parameters. + std::string extension_id; + got_value = message_dict->GetString(ext::kAutomationExtensionIdKey, + &extension_id); + DCHECK(got_value); + if (!got_value) + return true; + + int connection_id; + got_value = message_dict->GetInteger(ext::kAutomationConnectionIdKey, + &connection_id); + DCHECK(got_value); + if (!got_value) + return true; + + std::string channel_name; + // Channel name is optional. + message_dict->GetString(ext::kAutomationChannelNameKey, &channel_name); + + // Tab information is optional, try to retrieve it + // and re-flatten it to a string. + std::string tab_json("null"); + DictionaryValue* tab = NULL; + if (message_dict->GetDictionary(ext::kAutomationTabJsonKey, &tab)) + base::JSONWriter::Write(tab, false, &tab_json); + + int routing_id = view_host->routing_id(); + // Create the extension port and connect it. + scoped_ptr<ExtensionPortContainer> port( + new ExtensionPortContainer(automation, tab_handle)); + + int process_id = view_host->process()->id(); + if (port->Connect(extension_id, process_id, routing_id, connection_id, + channel_name, tab_json)) { + // We have a successful connection. + automation->AddPortContainer(port.release()); + } + } else if (command == ext::POST_MESSAGE) { + int port_id = -1; + got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id); + DCHECK(got_value); + if (!got_value) + return true; + + std::string data; + got_value = message_dict->GetString(ext::kAutomationMessageDataKey, &data); + DCHECK(got_value); + if (!got_value) + return true; + + ExtensionPortContainer* port = automation->GetPortContainer(port_id); + DCHECK(port); + if (port) + port->PostMessageFromExternalPort(data); + } else if (command == ext::CHANNEL_CLOSED) { + int port_id = -1; + got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id); + DCHECK(got_value); + if (!got_value) + return true; + + ExtensionPortContainer* port = automation->GetPortContainer(port_id); + DCHECK(port); + if (port) { + // This will delete the port and notify the other end of the disconnect. + automation->RemovePortContainer(port); + } + } else { + // We don't expect other messages here. + NOTREACHED(); + } + + return true; +} |