1 // Copyright 2013 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 "content/browser/message_port_service.h"
6
7 #include "content/browser/message_port_message_filter.h"
8 #include "content/common/message_port_messages.h"
9
10 namespace content {
11
12 struct MessagePortService::MessagePort {
13 // |filter| and |route_id| are what we need to send messages to the port.
14 // |filter| is just a weak pointer since we get notified when its process has
15 // gone away and remove it.
16 MessagePortMessageFilter* filter;
17 int route_id;
18 // A globally unique id for this message port.
19 int message_port_id;
20 // The globally unique id of the entangled message port.
21 int entangled_message_port_id;
22 // If true, all messages to this message port are queued and not delivered.
23 // This is needed so that when a message port is sent between processes all
24 // pending message get transferred. There are two possibilities for pending
25 // messages: either they are already received by the child process, or they're
26 // in-flight. This flag ensures that the latter type get flushed through the
27 // system.
28 // This flag should only be set to true in response to
29 // MessagePortHostMsg_QueueMessages.
30 bool queue_messages;
31 QueuedMessages queued_messages;
32 };
33
GetInstance()34 MessagePortService* MessagePortService::GetInstance() {
35 return Singleton<MessagePortService>::get();
36 }
37
MessagePortService()38 MessagePortService::MessagePortService()
39 : next_message_port_id_(0) {
40 }
41
~MessagePortService()42 MessagePortService::~MessagePortService() {
43 }
44
UpdateMessagePort(int message_port_id,MessagePortMessageFilter * filter,int routing_id)45 void MessagePortService::UpdateMessagePort(
46 int message_port_id,
47 MessagePortMessageFilter* filter,
48 int routing_id) {
49 if (!message_ports_.count(message_port_id)) {
50 NOTREACHED();
51 return;
52 }
53
54 MessagePort& port = message_ports_[message_port_id];
55 port.filter = filter;
56 port.route_id = routing_id;
57 }
58
OnMessagePortMessageFilterClosing(MessagePortMessageFilter * filter)59 void MessagePortService::OnMessagePortMessageFilterClosing(
60 MessagePortMessageFilter* filter) {
61 // Check if the (possibly) crashed process had any message ports.
62 for (MessagePorts::iterator iter = message_ports_.begin();
63 iter != message_ports_.end();) {
64 MessagePorts::iterator cur_item = iter++;
65 if (cur_item->second.filter == filter) {
66 Erase(cur_item->first);
67 }
68 }
69 }
70
Create(int route_id,MessagePortMessageFilter * filter,int * message_port_id)71 void MessagePortService::Create(int route_id,
72 MessagePortMessageFilter* filter,
73 int* message_port_id) {
74 *message_port_id = ++next_message_port_id_;
75
76 MessagePort port;
77 port.filter = filter;
78 port.route_id = route_id;
79 port.message_port_id = *message_port_id;
80 port.entangled_message_port_id = MSG_ROUTING_NONE;
81 port.queue_messages = false;
82 message_ports_[*message_port_id] = port;
83 }
84
Destroy(int message_port_id)85 void MessagePortService::Destroy(int message_port_id) {
86 if (!message_ports_.count(message_port_id)) {
87 NOTREACHED();
88 return;
89 }
90
91 DCHECK(message_ports_[message_port_id].queued_messages.empty());
92 Erase(message_port_id);
93 }
94
Entangle(int local_message_port_id,int remote_message_port_id)95 void MessagePortService::Entangle(int local_message_port_id,
96 int remote_message_port_id) {
97 if (!message_ports_.count(local_message_port_id) ||
98 !message_ports_.count(remote_message_port_id)) {
99 NOTREACHED();
100 return;
101 }
102
103 DCHECK(message_ports_[remote_message_port_id].entangled_message_port_id ==
104 MSG_ROUTING_NONE);
105 message_ports_[remote_message_port_id].entangled_message_port_id =
106 local_message_port_id;
107 }
108
PostMessage(int sender_message_port_id,const base::string16 & message,const std::vector<int> & sent_message_port_ids)109 void MessagePortService::PostMessage(
110 int sender_message_port_id,
111 const base::string16& message,
112 const std::vector<int>& sent_message_port_ids) {
113 if (!message_ports_.count(sender_message_port_id)) {
114 NOTREACHED();
115 return;
116 }
117
118 int entangled_message_port_id =
119 message_ports_[sender_message_port_id].entangled_message_port_id;
120 if (entangled_message_port_id == MSG_ROUTING_NONE)
121 return; // Process could have crashed.
122
123 if (!message_ports_.count(entangled_message_port_id)) {
124 NOTREACHED();
125 return;
126 }
127
128 PostMessageTo(entangled_message_port_id, message, sent_message_port_ids);
129 }
130
PostMessageTo(int message_port_id,const base::string16 & message,const std::vector<int> & sent_message_port_ids)131 void MessagePortService::PostMessageTo(
132 int message_port_id,
133 const base::string16& message,
134 const std::vector<int>& sent_message_port_ids) {
135 if (!message_ports_.count(message_port_id)) {
136 NOTREACHED();
137 return;
138 }
139 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
140 if (!message_ports_.count(sent_message_port_ids[i])) {
141 NOTREACHED();
142 return;
143 }
144 }
145
146 MessagePort& entangled_port = message_ports_[message_port_id];
147
148 std::vector<MessagePort*> sent_ports(sent_message_port_ids.size());
149 for (size_t i = 0; i < sent_message_port_ids.size(); ++i)
150 sent_ports[i] = &message_ports_[sent_message_port_ids[i]];
151
152 if (entangled_port.queue_messages) {
153 entangled_port.queued_messages.push_back(
154 std::make_pair(message, sent_message_port_ids));
155 return;
156 }
157
158 if (!entangled_port.filter) {
159 NOTREACHED();
160 return;
161 }
162
163 // If a message port was sent around, the new location will need a routing
164 // id. Instead of having the created port send us a sync message to get it,
165 // send along with the message.
166 std::vector<int> new_routing_ids(sent_message_port_ids.size());
167 for (size_t i = 0; i < sent_message_port_ids.size(); ++i) {
168 new_routing_ids[i] = entangled_port.filter->GetNextRoutingID();
169 sent_ports[i]->filter = entangled_port.filter;
170
171 // Update the entry for the sent port as it can be in a different process.
172 sent_ports[i]->route_id = new_routing_ids[i];
173 }
174
175 // Now send the message to the entangled port.
176 entangled_port.filter->Send(new MessagePortMsg_Message(
177 entangled_port.route_id, message, sent_message_port_ids,
178 new_routing_ids));
179 }
180
QueueMessages(int message_port_id)181 void MessagePortService::QueueMessages(int message_port_id) {
182 if (!message_ports_.count(message_port_id)) {
183 NOTREACHED();
184 return;
185 }
186
187 MessagePort& port = message_ports_[message_port_id];
188 if (port.filter) {
189 port.filter->Send(new MessagePortMsg_MessagesQueued(port.route_id));
190 port.queue_messages = true;
191 port.filter = NULL;
192 }
193 }
194
SendQueuedMessages(int message_port_id,const QueuedMessages & queued_messages)195 void MessagePortService::SendQueuedMessages(
196 int message_port_id,
197 const QueuedMessages& queued_messages) {
198 if (!message_ports_.count(message_port_id)) {
199 NOTREACHED();
200 return;
201 }
202
203 // Send the queued messages to the port again. This time they'll reach the
204 // new location.
205 MessagePort& port = message_ports_[message_port_id];
206 port.queue_messages = false;
207 port.queued_messages.insert(port.queued_messages.begin(),
208 queued_messages.begin(),
209 queued_messages.end());
210 SendQueuedMessagesIfPossible(message_port_id);
211 }
212
SendQueuedMessagesIfPossible(int message_port_id)213 void MessagePortService::SendQueuedMessagesIfPossible(int message_port_id) {
214 if (!message_ports_.count(message_port_id)) {
215 NOTREACHED();
216 return;
217 }
218
219 MessagePort& port = message_ports_[message_port_id];
220 if (port.queue_messages || !port.filter)
221 return;
222
223 for (QueuedMessages::iterator iter = port.queued_messages.begin();
224 iter != port.queued_messages.end(); ++iter) {
225 PostMessageTo(message_port_id, iter->first, iter->second);
226 }
227 port.queued_messages.clear();
228 }
229
Erase(int message_port_id)230 void MessagePortService::Erase(int message_port_id) {
231 MessagePorts::iterator erase_item = message_ports_.find(message_port_id);
232 DCHECK(erase_item != message_ports_.end());
233
234 int entangled_id = erase_item->second.entangled_message_port_id;
235 if (entangled_id != MSG_ROUTING_NONE) {
236 // Do the disentanglement (and be paranoid about the other side existing
237 // just in case something unusual happened during entanglement).
238 if (message_ports_.count(entangled_id)) {
239 message_ports_[entangled_id].entangled_message_port_id = MSG_ROUTING_NONE;
240 }
241 }
242 message_ports_.erase(erase_item);
243 }
244
245 } // namespace content
246