• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
MessagePortExtensionMessageService::MessagePort39   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 
DispatchOnConnect(const ExtensionMessageService::MessagePort & port,int dest_port_id,const std::string & channel_name,const std::string & tab_json,const std::string & source_extension_id,const std::string & target_extension_id)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 
DispatchOnDisconnect(const ExtensionMessageService::MessagePort & port,int source_port_id,bool connection_error)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 
DispatchOnMessage(const ExtensionMessageService::MessagePort & port,const std::string & message,int source_port_id)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
AllocatePortIdPair(int * port1,int * port2)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 
ExtensionMessageService(Profile * profile)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 
~ExtensionMessageService()127 ExtensionMessageService::~ExtensionMessageService() {
128   STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
129   channels_.clear();
130 }
131 
DestroyingProfile()132 void ExtensionMessageService::DestroyingProfile() {
133   profile_ = NULL;
134   if (!registrar_.IsEmpty())
135     registrar_.RemoveAll();
136 }
137 
OpenChannelToExtension(int source_process_id,int source_routing_id,int receiver_port_id,const std::string & source_extension_id,const std::string & target_extension_id,const std::string & channel_name)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 
OpenChannelToTab(int source_process_id,int source_routing_id,int receiver_port_id,int tab_id,const std::string & extension_id,const std::string & channel_name)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 
OpenChannelImpl(IPC::Message::Sender * source,const std::string & tab_json,const MessagePort & receiver,int receiver_port_id,const std::string & source_extension_id,const std::string & target_extension_id,const std::string & channel_name)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 
OpenSpecialChannelToExtension(const std::string & extension_id,const std::string & channel_name,const std::string & tab_json,IPC::Message::Sender * source)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 
OpenSpecialChannelToTab(const std::string & extension_id,const std::string & channel_name,TabContents * target_tab_contents,IPC::Message::Sender * source)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 
CloseChannel(int port_id)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 
CloseChannelImpl(MessageChannelMap::iterator channel_iter,int closing_port_id,bool notify_other_port)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 
PostMessageFromRenderer(int source_port_id,const std::string & message)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 }
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
OnSenderClosed(IPC::Message::Sender * sender)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