| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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/automation/extension_port_container.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/json/json_reader.h" | |
| 9 #include "base/json/json_writer.h" | |
| 10 #include "base/values.h" | |
| 11 #include "chrome/browser/automation/automation_provider.h" | |
| 12 #include "chrome/browser/automation/extension_automation_constants.h" | |
| 13 #include "chrome/browser/extensions/extension_message_service.h" | |
| 14 #include "chrome/browser/profiles/profile.h" | |
| 15 #include "chrome/common/automation_messages.h" | |
| 16 #include "chrome/common/extensions/extension_messages.h" | |
| 17 #include "content/browser/renderer_host/render_process_host.h" | |
| 18 #include "content/browser/renderer_host/render_view_host.h" | |
| 19 | |
| 20 // TODO(siggi): Find a more structured way to read and write JSON messages. | |
| 21 | |
| 22 namespace ext = extension_automation_constants; | |
| 23 | |
| 24 ExtensionPortContainer::ExtensionPortContainer(AutomationProvider* automation, | |
| 25 int tab_handle) : | |
| 26 automation_(automation), service_(NULL), port_id_(-1), | |
| 27 tab_handle_(tab_handle) { | |
| 28 service_ = automation_->profile()->GetExtensionMessageService(); | |
| 29 DCHECK(service_); | |
| 30 } | |
| 31 | |
| 32 ExtensionPortContainer::~ExtensionPortContainer() { | |
| 33 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); | |
| 34 | |
| 35 if (port_id_ != -1) | |
| 36 service_->CloseChannel(port_id_); | |
| 37 } | |
| 38 | |
| 39 bool ExtensionPortContainer::PostResponseToExternalPort( | |
| 40 const std::string& message) { | |
| 41 return automation_->Send( | |
| 42 new AutomationMsg_ForwardMessageToExternalHost( | |
| 43 tab_handle_, message, ext::kAutomationOrigin, | |
| 44 ext::kAutomationPortResponseTarget)); | |
| 45 } | |
| 46 | |
| 47 bool ExtensionPortContainer::PostMessageToExternalPort( | |
| 48 const std::string& message) { | |
| 49 return automation_->Send( | |
| 50 new AutomationMsg_ForwardMessageToExternalHost( | |
| 51 tab_handle_, message, | |
| 52 ext::kAutomationOrigin, | |
| 53 ext::kAutomationPortRequestTarget)); | |
| 54 } | |
| 55 | |
| 56 void ExtensionPortContainer::PostMessageFromExternalPort( | |
| 57 const std::string &message) { | |
| 58 service_->PostMessageFromRenderer(port_id_, message); | |
| 59 } | |
| 60 | |
| 61 bool ExtensionPortContainer::Connect(const std::string &extension_id, | |
| 62 int process_id, | |
| 63 int routing_id, | |
| 64 int connection_id, | |
| 65 const std::string& channel_name, | |
| 66 const std::string& tab_json) { | |
| 67 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); | |
| 68 | |
| 69 port_id_ = service_->OpenSpecialChannelToExtension( | |
| 70 extension_id, channel_name, tab_json, this); | |
| 71 if (port_id_ == -1) { | |
| 72 // In this case a disconnect message has been dispatched. | |
| 73 return false; | |
| 74 } | |
| 75 | |
| 76 SendConnectionResponse(connection_id, port_id_); | |
| 77 return true; | |
| 78 } | |
| 79 | |
| 80 void ExtensionPortContainer::SendConnectionResponse(int connection_id, | |
| 81 int port_id) { | |
| 82 // Compose the reply message. | |
| 83 scoped_ptr<DictionaryValue> msg_dict(new DictionaryValue()); | |
| 84 msg_dict->SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_OPENED); | |
| 85 msg_dict->SetInteger(ext::kAutomationConnectionIdKey, connection_id); | |
| 86 msg_dict->SetInteger(ext::kAutomationPortIdKey, port_id); | |
| 87 | |
| 88 std::string msg_json; | |
| 89 base::JSONWriter::Write(msg_dict.get(), false, &msg_json); | |
| 90 | |
| 91 PostResponseToExternalPort(msg_json); | |
| 92 } | |
| 93 | |
| 94 bool ExtensionPortContainer::Send(IPC::Message *message) { | |
| 95 DCHECK_EQ(MessageLoop::current()->type(), MessageLoop::TYPE_UI); | |
| 96 | |
| 97 IPC_BEGIN_MESSAGE_MAP(ExtensionPortContainer, *message) | |
| 98 IPC_MESSAGE_HANDLER(ExtensionMsg_MessageInvoke, OnExtensionMessageInvoke) | |
| 99 IPC_MESSAGE_UNHANDLED_ERROR() | |
| 100 IPC_END_MESSAGE_MAP() | |
| 101 | |
| 102 delete message; | |
| 103 return true; | |
| 104 } | |
| 105 | |
| 106 void ExtensionPortContainer::OnExtensionMessageInvoke( | |
| 107 const std::string& extension_id, | |
| 108 const std::string& function_name, | |
| 109 const ListValue& args, | |
| 110 const GURL& event_url) { | |
| 111 if (function_name == ExtensionMessageService::kDispatchOnMessage) { | |
| 112 DCHECK_EQ(args.GetSize(), 2u); | |
| 113 | |
| 114 std::string message; | |
| 115 int source_port_id; | |
| 116 if (args.GetString(0, &message) && args.GetInteger(1, &source_port_id)) | |
| 117 OnExtensionHandleMessage(message, source_port_id); | |
| 118 } else if (function_name == ExtensionMessageService::kDispatchOnDisconnect) { | |
| 119 DCHECK_EQ(args.GetSize(), 2u); | |
| 120 int port_id; | |
| 121 if (args.GetInteger(0, &port_id)) | |
| 122 OnExtensionPortDisconnected(port_id); | |
| 123 } else if (function_name == ExtensionMessageService::kDispatchOnConnect) { | |
| 124 // Do nothing. | |
| 125 // TODO(siggi): implement | |
| 126 } else { | |
| 127 NOTREACHED() << function_name << " shouldn't be called."; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void ExtensionPortContainer::OnExtensionHandleMessage( | |
| 132 const std::string& message, int source_port_id) { | |
| 133 // Compose the reply message and fire it away. | |
| 134 DictionaryValue msg_dict; | |
| 135 msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::POST_MESSAGE); | |
| 136 msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_); | |
| 137 msg_dict.SetString(ext::kAutomationMessageDataKey, message); | |
| 138 | |
| 139 std::string msg_json; | |
| 140 base::JSONWriter::Write(&msg_dict, false, &msg_json); | |
| 141 | |
| 142 PostMessageToExternalPort(msg_json); | |
| 143 } | |
| 144 | |
| 145 void ExtensionPortContainer::OnExtensionPortDisconnected(int source_port_id) { | |
| 146 // Compose the disconnect message and fire it away. | |
| 147 DictionaryValue msg_dict; | |
| 148 msg_dict.SetInteger(ext::kAutomationRequestIdKey, ext::CHANNEL_CLOSED); | |
| 149 msg_dict.SetInteger(ext::kAutomationPortIdKey, port_id_); | |
| 150 | |
| 151 std::string msg_json; | |
| 152 base::JSONWriter::Write(&msg_dict, false, &msg_json); | |
| 153 | |
| 154 PostMessageToExternalPort(msg_json); | |
| 155 } | |
| 156 | |
| 157 bool ExtensionPortContainer::InterceptMessageFromExternalHost( | |
| 158 const std::string& message, const std::string& origin, | |
| 159 const std::string& target, AutomationProvider* automation, | |
| 160 RenderViewHost *view_host, int tab_handle) { | |
| 161 if (target != ext::kAutomationPortRequestTarget) | |
| 162 return false; | |
| 163 | |
| 164 if (origin != ext::kAutomationOrigin) { | |
| 165 // TODO(siggi): Should we block the message on wrong origin? | |
| 166 LOG(WARNING) << "Wrong origin on automation port message " << origin; | |
| 167 } | |
| 168 | |
| 169 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); | |
| 170 DCHECK(message_value->IsType(Value::TYPE_DICTIONARY)); | |
| 171 if (!message_value->IsType(Value::TYPE_DICTIONARY)) | |
| 172 return true; | |
| 173 | |
| 174 DictionaryValue* message_dict = | |
| 175 reinterpret_cast<DictionaryValue*>(message_value.get()); | |
| 176 | |
| 177 int command = -1; | |
| 178 bool got_value = message_dict->GetInteger(ext::kAutomationRequestIdKey, | |
| 179 &command); | |
| 180 DCHECK(got_value); | |
| 181 if (!got_value) | |
| 182 return true; | |
| 183 | |
| 184 if (command == ext::OPEN_CHANNEL) { | |
| 185 // Extract the "extension_id" and "connection_id" parameters. | |
| 186 std::string extension_id; | |
| 187 got_value = message_dict->GetString(ext::kAutomationExtensionIdKey, | |
| 188 &extension_id); | |
| 189 DCHECK(got_value); | |
| 190 if (!got_value) | |
| 191 return true; | |
| 192 | |
| 193 int connection_id; | |
| 194 got_value = message_dict->GetInteger(ext::kAutomationConnectionIdKey, | |
| 195 &connection_id); | |
| 196 DCHECK(got_value); | |
| 197 if (!got_value) | |
| 198 return true; | |
| 199 | |
| 200 std::string channel_name; | |
| 201 // Channel name is optional. | |
| 202 message_dict->GetString(ext::kAutomationChannelNameKey, &channel_name); | |
| 203 | |
| 204 // Tab information is optional, try to retrieve it | |
| 205 // and re-flatten it to a string. | |
| 206 std::string tab_json("null"); | |
| 207 DictionaryValue* tab = NULL; | |
| 208 if (message_dict->GetDictionary(ext::kAutomationTabJsonKey, &tab)) | |
| 209 base::JSONWriter::Write(tab, false, &tab_json); | |
| 210 | |
| 211 int routing_id = view_host->routing_id(); | |
| 212 // Create the extension port and connect it. | |
| 213 scoped_ptr<ExtensionPortContainer> port( | |
| 214 new ExtensionPortContainer(automation, tab_handle)); | |
| 215 | |
| 216 int process_id = view_host->process()->id(); | |
| 217 if (port->Connect(extension_id, process_id, routing_id, connection_id, | |
| 218 channel_name, tab_json)) { | |
| 219 // We have a successful connection. | |
| 220 automation->AddPortContainer(port.release()); | |
| 221 } | |
| 222 } else if (command == ext::POST_MESSAGE) { | |
| 223 int port_id = -1; | |
| 224 got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id); | |
| 225 DCHECK(got_value); | |
| 226 if (!got_value) | |
| 227 return true; | |
| 228 | |
| 229 std::string data; | |
| 230 got_value = message_dict->GetString(ext::kAutomationMessageDataKey, &data); | |
| 231 DCHECK(got_value); | |
| 232 if (!got_value) | |
| 233 return true; | |
| 234 | |
| 235 ExtensionPortContainer* port = automation->GetPortContainer(port_id); | |
| 236 DCHECK(port); | |
| 237 if (port) | |
| 238 port->PostMessageFromExternalPort(data); | |
| 239 } else if (command == ext::CHANNEL_CLOSED) { | |
| 240 int port_id = -1; | |
| 241 got_value = message_dict->GetInteger(ext::kAutomationPortIdKey, &port_id); | |
| 242 DCHECK(got_value); | |
| 243 if (!got_value) | |
| 244 return true; | |
| 245 | |
| 246 ExtensionPortContainer* port = automation->GetPortContainer(port_id); | |
| 247 DCHECK(port); | |
| 248 if (port) { | |
| 249 // This will delete the port and notify the other end of the disconnect. | |
| 250 automation->RemovePortContainer(port); | |
| 251 } | |
| 252 } else { | |
| 253 // We don't expect other messages here. | |
| 254 NOTREACHED(); | |
| 255 } | |
| 256 | |
| 257 return true; | |
| 258 } | |
| OLD | NEW |