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
36struct 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
44struct ExtensionMessageService::MessageChannel {
45  ExtensionMessageService::MessagePort opener;
46  ExtensionMessageService::MessagePort receiver;
47};
48
49const char ExtensionMessageService::kDispatchOnConnect[] =
50    "Port.dispatchOnConnect";
51const char ExtensionMessageService::kDispatchOnDisconnect[] =
52    "Port.dispatchOnDisconnect";
53const char ExtensionMessageService::kDispatchOnMessage[] =
54    "Port.dispatchOnMessage";
55
56namespace {
57
58static base::AtomicSequenceNumber g_next_channel_id(base::LINKER_INITIALIZED);
59
60static 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
77static 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
87static 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
99void 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
117ExtensionMessageService::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
127ExtensionMessageService::~ExtensionMessageService() {
128  STLDeleteContainerPairSecondPointers(channels_.begin(), channels_.end());
129  channels_.clear();
130}
131
132void ExtensionMessageService::DestroyingProfile() {
133  profile_ = NULL;
134  if (!registrar_.IsEmpty())
135    registrar_.RemoveAll();
136}
137
138void 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
169void 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
208bool 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
248int 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
268int 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
293void 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
300void 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
313void 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}
327void 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
346void 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