summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extension_message_service.cc
blob: 87469f9166f32f2d2f8d10c192b95fba9bf366f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/extension_message_service.h"

#include "base/singleton.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension.h"
#include "chrome/browser/extensions/extension_view.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/resource_message_filter.h"
#include "chrome/common/render_messages.h"

// Since we have 2 ports for every channel, we just index channels by half the
// port ID.
#define GET_CHANNEL_ID(port_id) (port_id / 2)

// Port1 is always even, port2 is always odd.
#define IS_PORT1_ID(port_id) ((port_id & 1) == 0)

// Change even to odd and vice versa, to get the other side of a given channel.
#define GET_OPPOSITE_PORT_ID(source_port_id) (source_port_id ^ 1)

ExtensionMessageService* ExtensionMessageService::GetInstance() {
  return Singleton<ExtensionMessageService>::get();
}

ExtensionMessageService::ExtensionMessageService()
    : next_port_id_(0) {
}

void ExtensionMessageService::RegisterExtension(
    const std::string& extension_id, int render_process_id) {
  AutoLock lock(renderers_lock_);
  // TODO(mpcomplete): We need to ensure an extension always ends up in a single
  // process.  I think this means having an ExtensionProcessManager which holds
  // a BrowsingContext for each extension.
  //DCHECK(process_ids_.find(extension_id) == process_ids_.end());
  process_ids_[extension_id] = render_process_id;
}

int ExtensionMessageService::OpenChannelToExtension(
    const std::string& extension_id, ResourceMessageFilter* source) {
  DCHECK(MessageLoop::current() ==
         ChromeThread::GetMessageLoop(ChromeThread::IO));

  // Lookup the targeted extension process.
  ResourceMessageFilter* dest = NULL;
  {
    AutoLock lock(renderers_lock_);
    ProcessIDMap::iterator process_id = process_ids_.find(extension_id);
    if (process_id == process_ids_.end())
      return -1;

    RendererMap::iterator renderer = renderers_.find(process_id->second);
    if (renderer == renderers_.end())
      return -1;
    dest = renderer->second;
  }

  // Create a channel ID for both sides of the channel.
  int port1_id = next_port_id_++;
  int port2_id = next_port_id_++;
  DCHECK(IS_PORT1_ID(port1_id));
  DCHECK(GET_OPPOSITE_PORT_ID(port1_id) == port2_id);
  DCHECK(GET_OPPOSITE_PORT_ID(port2_id) == port1_id);
  DCHECK(GET_CHANNEL_ID(port1_id) == GET_CHANNEL_ID(port2_id));

  MessageChannel channel;
  channel.port1 = source;
  channel.port2 = dest;
  channels_[GET_CHANNEL_ID(port1_id)] = channel;

  // Send each process the id for the opposite port.
  dest->Send(new ViewMsg_ExtensionHandleConnect(port1_id));
  return port2_id;
}

void ExtensionMessageService::PostMessageFromRenderer(
    int port_id, const std::string& message, ResourceMessageFilter* source) {
  DCHECK(MessageLoop::current() ==
         ChromeThread::GetMessageLoop(ChromeThread::IO));

  // Look up the channel by port1's ID.
  MessageChannelMap::iterator iter =
      channels_.find(GET_CHANNEL_ID(port_id));
  if (iter == channels_.end())
    return;
  MessageChannel& channel = iter->second;

  // Figure out which port the ID corresponds to.
  ResourceMessageFilter* dest = NULL;
  if (IS_PORT1_ID(port_id)) {
    dest = channel.port1;
    DCHECK(source == channel.port2);
  } else {
    dest = channel.port2;
    DCHECK(source == channel.port1);
  }

  int source_port_id = GET_OPPOSITE_PORT_ID(port_id);
  dest->Send(new ViewMsg_ExtensionHandleMessage(message, source_port_id));
}

void ExtensionMessageService::RendererReady(ResourceMessageFilter* renderer) {
  AutoLock lock(renderers_lock_);
  DCHECK(renderers_.find(renderer->GetProcessId()) == renderers_.end());
  renderers_[renderer->GetProcessId()] = renderer;
}

void ExtensionMessageService::RendererShutdown(
    ResourceMessageFilter* renderer) {
  {
    AutoLock lock(renderers_lock_);
    DCHECK(renderers_.find(renderer->GetProcessId()) != renderers_.end());
    renderers_.erase(renderer->GetProcessId());
  }

  // Close any channels that share this filter.
  // TODO(mpcomplete): should we notify the other side of the port?
  for (MessageChannelMap::iterator it = channels_.begin();
       it != channels_.end(); ) {
    MessageChannelMap::iterator current = it++;
    if (current->second.port1 == renderer || current->second.port2 == renderer)
      channels_.erase(current);
  }
}