Home | History | Annotate | Download | only in extensions
      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