1 // Copyright (c) 2011 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/extension_message_service.h" 6 7 #include "base/atomic_sequence_num.h" 8 #include "base/json/json_writer.h" 9 #include "base/stl_util-inl.h" 10 #include "base/values.h" 11 #include "chrome/browser/extensions/extension_process_manager.h" 12 #include "chrome/browser/extensions/extension_tabs_module.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/tab_contents/tab_util.h" 15 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 16 #include "chrome/common/extensions/extension.h" 17 #include "chrome/common/extensions/extension_messages.h" 18 #include "content/browser/child_process_security_policy.h" 19 #include "content/browser/renderer_host/render_process_host.h" 20 #include "content/browser/renderer_host/render_view_host.h" 21 #include "content/browser/tab_contents/tab_contents.h" 22 #include "content/common/notification_service.h" 23 24 // Since we have 2 ports for every channel, we just index channels by half the 25 // port ID. 26 #define GET_CHANNEL_ID(port_id) ((port_id) / 2) 27 #define GET_CHANNEL_OPENER_ID(channel_id) ((channel_id) * 2) 28 #define GET_CHANNEL_RECEIVERS_ID(channel_id) ((channel_id) * 2 + 1) 29 30 // Port1 is always even, port2 is always odd. 31 #define IS_OPENER_PORT_ID(port_id) (((port_id) & 1) == 0) 32 33 // Change even to odd and vice versa, to get the other side of a given channel. 34 #define GET_OPPOSITE_PORT_ID(source_port_id) ((source_port_id) ^ 1) 35 36 struct ExtensionMessageService::MessagePort { 37 IPC::Message::Sender* sender; 38 int routing_id; 39 explicit MessagePort(IPC::Message::Sender* sender = NULL, 40 int routing_id = MSG_ROUTING_CONTROL) 41 : sender(sender), routing_id(routing_id) {} 42 }; 43 44 struct ExtensionMessageService::MessageChannel { 45 ExtensionMessageService::MessagePort opener; 46 ExtensionMessageService::MessagePort receiver; 47 }; 48 49 const char ExtensionMessageService::kDispatchOnConnect[] = 50 "Port.dispatchOnConnect"; 51 const char ExtensionMessageService::kDispatchOnDisconnect[] = 52 "Port.dispatchOnDisconnect"; 53 const char ExtensionMessageService::kDispatchOnMessage[] = 54 "Port.dispatchOnMessage"; 55 56 namespace { 57 58 static base::AtomicSequenceNumber g_next_channel_id(base::LINKER_INITIALIZED); 59 60 static void DispatchOnConnect(const ExtensionMessageService::MessagePort& port, 61 int dest_port_id, 62 const std::string& channel_name, 63 const std::string& tab_json, 64 const std::string& source_extension_id, 65 const std::string& target_extension_id) { 66 ListValue args; 67 args.Set(0, Value::CreateIntegerValue(dest_port_id)); 68 args.Set(1, Value::CreateStringValue(channel_name)); 69 args.Set(2, Value::CreateStringValue(tab_json)); 70 args.Set(3, Value::CreateStringValue(source_extension_id)); 71 args.Set(4, Value::CreateStringValue(target_extension_id)); 72 CHECK(port.sender); 73 port.sender->Send(new ExtensionMsg_MessageInvoke(port.routing_id, 74 "", ExtensionMessageService::kDispatchOnConnect, args, GURL())); 75 } 76 77 static void DispatchOnDisconnect( 78 const ExtensionMessageService::MessagePort& port, int source_port_id, 79 bool connection_error) { 80 ListValue args; 81 args.Set(0, Value::CreateIntegerValue(source_port_id)); 82 args.Set(1, Value::CreateBooleanValue(connection_error)); 83 port.sender->Send(new ExtensionMsg_MessageInvoke(port.routing_id, 84 "", ExtensionMessageService::kDispatchOnDisconnect, args, GURL())); 85 } 86 87 static void DispatchOnMessage(const ExtensionMessageService::MessagePort& port, 88 const std::string& message, int source_port_id) { 89 ListValue args; 90 args.Set(0, Value::CreateStringValue(message)); 91 args.Set(1, Value::CreateIntegerValue(source_port_id)); 92 port.sender->Send(new ExtensionMsg_MessageInvoke(port.routing_id, 93 "", ExtensionMessageService::kDispatchOnMessage, args, GURL())); 94 } 95 96 } // namespace 97 98 // static 99 void ExtensionMessageService::AllocatePortIdPair(int* port1, int* port2) { 100 int channel_id = g_next_channel_id.GetNext(); 101 int port1_id = channel_id * 2; 102 int port2_id = channel_id * 2 + 1; 103 104 // Sanity checks to make sure our channel<->port converters are correct. 105 DCHECK(IS_OPENER_PORT_ID(port1_id)); 106 DCHECK(GET_OPPOSITE_PORT_ID(port1_id) == port2_id); 107 DCHECK(GET_OPPOSITE_PORT_ID(port2_id) == port1_id); 108 DCHECK(GET_CHANNEL_ID(port1_id) == GET_CHANNEL_ID(port2_id)); 109 DCHECK(GET_CHANNEL_ID(port1_id) == channel_id); 110 DCHECK(GET_CHANNEL_OPENER_ID(channel_id) == port1_id); 111 DCHECK(GET_CHANNEL_RECEIVERS_ID(channel_id) == port2_id); 112 113 *port1 = port1_id; 114 *port2 = port2_id; 115 } 116 117 ExtensionMessageService::ExtensionMessageService(Profile* profile) 118 : profile_(profile) { 119 registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED, 120 NotificationService::AllSources()); 121 registrar_.Add(this, NotificationType::RENDERER_PROCESS_CLOSED, 122 NotificationService::AllSources()); 123 registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED, 124 NotificationService::AllSources()); 125 } 126 127 ExtensionMessageService::~ExtensionMessageService() { 128 STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end()); 129 channels_.clear(); 130 } 131 132 void ExtensionMessageService::DestroyingProfile() { 133 profile_ = NULL; 134 if (!registrar_.IsEmpty()) 135 registrar_.RemoveAll(); 136 } 137 138 void ExtensionMessageService::OpenChannelToExtension( 139 int source_process_id, int source_routing_id, int receiver_port_id, 140 const std::string& source_extension_id, 141 const std::string& target_extension_id, 142 const std::string& channel_name) { 143 RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); 144 if (!source) 145 return; 146 147 // Note: we use the source's profile here. If the source is an incognito 148 // process, we will use the incognito EPM to find the right extension process, 149 // which depends on whether the extension uses spanning or split mode. 150 MessagePort receiver( 151 source->profile()->GetExtensionProcessManager()->GetExtensionProcess( 152 target_extension_id), 153 MSG_ROUTING_CONTROL); 154 TabContents* source_contents = tab_util::GetTabContentsByID( 155 source_process_id, source_routing_id); 156 157 // Include info about the opener's tab (if it was a tab). 158 std::string tab_json = "null"; 159 if (source_contents) { 160 scoped_ptr<DictionaryValue> tab_value( 161 ExtensionTabUtil::CreateTabValue(source_contents)); 162 base::JSONWriter::Write(tab_value.get(), false, &tab_json); 163 } 164 165 OpenChannelImpl(source, tab_json, receiver, receiver_port_id, 166 source_extension_id, target_extension_id, channel_name); 167 } 168 169 void ExtensionMessageService::OpenChannelToTab( 170 int source_process_id, int source_routing_id, int receiver_port_id, 171 int tab_id, const std::string& extension_id, 172 const std::string& channel_name) { 173 RenderProcessHost* source = RenderProcessHost::FromID(source_process_id); 174 if (!source) 175 return; 176 177 TabContentsWrapper* contents = NULL; 178 MessagePort receiver; 179 if (ExtensionTabUtil::GetTabById(tab_id, source->profile(), true, 180 NULL, NULL, &contents, NULL)) { 181 receiver.sender = contents->render_view_host(); 182 receiver.routing_id = contents->render_view_host()->routing_id(); 183 } 184 185 if (contents && contents->controller().needs_reload()) { 186 // The tab isn't loaded yet. Don't attempt to connect. Treat this as a 187 // disconnect. 188 DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), 189 GET_OPPOSITE_PORT_ID(receiver_port_id), true); 190 return; 191 } 192 193 TabContents* source_contents = tab_util::GetTabContentsByID( 194 source_process_id, source_routing_id); 195 196 // Include info about the opener's tab (if it was a tab). 197 std::string tab_json = "null"; 198 if (source_contents) { 199 scoped_ptr<DictionaryValue> tab_value( 200 ExtensionTabUtil::CreateTabValue(source_contents)); 201 base::JSONWriter::Write(tab_value.get(), false, &tab_json); 202 } 203 204 OpenChannelImpl(source, tab_json, receiver, receiver_port_id, 205 extension_id, extension_id, channel_name); 206 } 207 208 bool ExtensionMessageService::OpenChannelImpl( 209 IPC::Message::Sender* source, 210 const std::string& tab_json, 211 const MessagePort& receiver, int receiver_port_id, 212 const std::string& source_extension_id, 213 const std::string& target_extension_id, 214 const std::string& channel_name) { 215 if (!source) 216 return false; // Closed while in flight. 217 218 if (!receiver.sender) { 219 // Treat it as a disconnect. 220 DispatchOnDisconnect(MessagePort(source, MSG_ROUTING_CONTROL), 221 GET_OPPOSITE_PORT_ID(receiver_port_id), true); 222 return false; 223 } 224 225 // Add extra paranoid CHECKs, since we have crash reports of this being NULL. 226 // http://code.google.com/p/chromium/issues/detail?id=19067 227 CHECK(receiver.sender); 228 229 MessageChannel* channel(new MessageChannel); 230 channel->opener = MessagePort(source, MSG_ROUTING_CONTROL); 231 channel->receiver = receiver; 232 233 CHECK(receiver.sender); 234 235 CHECK(channels_.find(GET_CHANNEL_ID(receiver_port_id)) == channels_.end()); 236 channels_[GET_CHANNEL_ID(receiver_port_id)] = channel; 237 238 CHECK(receiver.sender); 239 240 // Send the connect event to the receiver. Give it the opener's port ID (the 241 // opener has the opposite port ID). 242 DispatchOnConnect(receiver, receiver_port_id, channel_name, tab_json, 243 source_extension_id, target_extension_id); 244 245 return true; 246 } 247 248 int ExtensionMessageService::OpenSpecialChannelToExtension( 249 const std::string& extension_id, const std::string& channel_name, 250 const std::string& tab_json, IPC::Message::Sender* source) { 251 DCHECK(profile_); 252 253 int port1_id = -1; 254 int port2_id = -1; 255 // Create a channel ID for both sides of the channel. 256 AllocatePortIdPair(&port1_id, &port2_id); 257 258 MessagePort receiver( 259 profile_->GetExtensionProcessManager()->GetExtensionProcess(extension_id), 260 MSG_ROUTING_CONTROL); 261 if (!OpenChannelImpl(source, tab_json, receiver, port2_id, 262 extension_id, extension_id, channel_name)) 263 return -1; 264 265 return port1_id; 266 } 267 268 int ExtensionMessageService::OpenSpecialChannelToTab( 269 const std::string& extension_id, const std::string& channel_name, 270 TabContents* target_tab_contents, IPC::Message::Sender* source) { 271 DCHECK(target_tab_contents); 272 273 if (target_tab_contents->controller().needs_reload()) { 274 // The tab isn't loaded yet. Don't attempt to connect. 275 return -1; 276 } 277 278 int port1_id = -1; 279 int port2_id = -1; 280 // Create a channel ID for both sides of the channel. 281 AllocatePortIdPair(&port1_id, &port2_id); 282 283 MessagePort receiver( 284 target_tab_contents->render_view_host(), 285 target_tab_contents->render_view_host()->routing_id()); 286 if (!OpenChannelImpl(source, "null", receiver, port2_id, 287 extension_id, extension_id, channel_name)) 288 return -1; 289 290 return port1_id; 291 } 292 293 void ExtensionMessageService::CloseChannel(int port_id) { 294 // Note: The channel might be gone already, if the other side closed first. 295 MessageChannelMap::iterator it = channels_.find(GET_CHANNEL_ID(port_id)); 296 if (it != channels_.end()) 297 CloseChannelImpl(it, port_id, true); 298 } 299 300 void ExtensionMessageService::CloseChannelImpl( 301 MessageChannelMap::iterator channel_iter, int closing_port_id, 302 bool notify_other_port) { 303 // Notify the other side. 304 const MessagePort& port = IS_OPENER_PORT_ID(closing_port_id) ? 305 channel_iter->second->receiver : channel_iter->second->opener; 306 307 if (notify_other_port) 308 DispatchOnDisconnect(port, GET_OPPOSITE_PORT_ID(closing_port_id), false); 309 delete channel_iter->second; 310 channels_.erase(channel_iter); 311 } 312 313 void ExtensionMessageService::PostMessageFromRenderer( 314 int source_port_id, const std::string& message) { 315 MessageChannelMap::iterator iter = 316 channels_.find(GET_CHANNEL_ID(source_port_id)); 317 if (iter == channels_.end()) 318 return; 319 320 // Figure out which port the ID corresponds to. 321 int dest_port_id = GET_OPPOSITE_PORT_ID(source_port_id); 322 const MessagePort& port = IS_OPENER_PORT_ID(dest_port_id) ? 323 iter->second->opener : iter->second->receiver; 324 325 DispatchOnMessage(port, message, dest_port_id); 326 } 327 void ExtensionMessageService::Observe(NotificationType type, 328 const NotificationSource& source, 329 const NotificationDetails& details) { 330 switch (type.value) { 331 case NotificationType::RENDERER_PROCESS_TERMINATED: 332 case NotificationType::RENDERER_PROCESS_CLOSED: { 333 RenderProcessHost* renderer = Source<RenderProcessHost>(source).ptr(); 334 OnSenderClosed(renderer); 335 break; 336 } 337 case NotificationType::RENDER_VIEW_HOST_DELETED: 338 OnSenderClosed(Source<RenderViewHost>(source).ptr()); 339 break; 340 default: 341 NOTREACHED(); 342 return; 343 } 344 } 345 346 void ExtensionMessageService::OnSenderClosed(IPC::Message::Sender* sender) { 347 // Close any channels that share this renderer. We notify the opposite 348 // port that his pair has closed. 349 for (MessageChannelMap::iterator it = channels_.begin(); 350 it != channels_.end(); ) { 351 MessageChannelMap::iterator current = it++; 352 // If both sides are the same renderer, and it is closing, there is no 353 // "other" port, so there's no need to notify it. 354 bool notify_other_port = 355 current->second->opener.sender != current->second->receiver.sender; 356 357 if (current->second->opener.sender == sender) { 358 CloseChannelImpl(current, GET_CHANNEL_OPENER_ID(current->first), 359 notify_other_port); 360 } else if (current->second->receiver.sender == sender) { 361 CloseChannelImpl(current, GET_CHANNEL_RECEIVERS_ID(current->first), 362 notify_other_port); 363 } 364 } 365 } 366