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.
5 #include "content/browser/message_port_service.h"
7 #include "content/browser/message_port_message_filter.h"
8 #include "content/common/message_port_messages.h"
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
;
18 // A globally unique id for this message port.
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
28 // This flag should only be set to true in response to
29 // MessagePortHostMsg_QueueMessages.
30 bool queue_for_inflight_messages
;
31 // If true, all messages to this message port are queued and not delivered.
32 // This is needed so that when a message port is sent to a new process all
33 // messages are held in the browser process until the destination process is
34 // ready to receive messages. This flag is set true when a message port is
35 // transferred to a different process but there isn't immediately a
36 // MessagePortMessageFilter available for that new process. Once the
37 // destination process is ready to receive messages it sends
38 // MessagePortHostMsg_ReleaseMessages to set this flag to false.
39 bool hold_messages_for_destination
;
40 // Returns true if messages should be queued for either reason.
41 bool queue_messages() const {
42 return queue_for_inflight_messages
|| hold_messages_for_destination
;
44 // If true, the message port should be destroyed but was currently still
45 // waiting for a SendQueuedMessages message from a renderer. As soon as that
46 // message is received the port will actually be destroyed.
47 bool should_be_destroyed
;
48 QueuedMessages queued_messages
;
51 MessagePortService
* MessagePortService::GetInstance() {
52 return Singleton
<MessagePortService
>::get();
55 MessagePortService::MessagePortService()
56 : next_message_port_id_(0) {
59 MessagePortService::~MessagePortService() {
62 void MessagePortService::UpdateMessagePort(
64 MessagePortMessageFilter
* filter
,
66 if (!message_ports_
.count(message_port_id
)) {
71 MessagePort
& port
= message_ports_
[message_port_id
];
73 port
.route_id
= routing_id
;
76 void MessagePortService::OnMessagePortMessageFilterClosing(
77 MessagePortMessageFilter
* filter
) {
78 // Check if the (possibly) crashed process had any message ports.
79 for (MessagePorts::iterator iter
= message_ports_
.begin();
80 iter
!= message_ports_
.end();) {
81 MessagePorts::iterator cur_item
= iter
++;
82 if (cur_item
->second
.filter
== filter
) {
83 Erase(cur_item
->first
);
88 void MessagePortService::Create(int route_id
,
89 MessagePortMessageFilter
* filter
,
90 int* message_port_id
) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
92 *message_port_id
= ++next_message_port_id_
;
96 port
.route_id
= route_id
;
97 port
.message_port_id
= *message_port_id
;
98 port
.entangled_message_port_id
= MSG_ROUTING_NONE
;
99 port
.queue_for_inflight_messages
= false;
100 port
.hold_messages_for_destination
= false;
101 port
.should_be_destroyed
= false;
102 message_ports_
[*message_port_id
] = port
;
105 void MessagePortService::Destroy(int message_port_id
) {
106 if (!message_ports_
.count(message_port_id
)) {
111 DCHECK(message_ports_
[message_port_id
].queued_messages
.empty());
113 Erase(message_port_id
);
116 void MessagePortService::Entangle(int local_message_port_id
,
117 int remote_message_port_id
) {
118 if (!message_ports_
.count(local_message_port_id
) ||
119 !message_ports_
.count(remote_message_port_id
)) {
124 DCHECK(message_ports_
[remote_message_port_id
].entangled_message_port_id
==
126 message_ports_
[remote_message_port_id
].entangled_message_port_id
=
127 local_message_port_id
;
130 void MessagePortService::PostMessage(
131 int sender_message_port_id
,
132 const base::string16
& message
,
133 const std::vector
<int>& sent_message_port_ids
) {
134 if (!message_ports_
.count(sender_message_port_id
)) {
139 int entangled_message_port_id
=
140 message_ports_
[sender_message_port_id
].entangled_message_port_id
;
141 if (entangled_message_port_id
== MSG_ROUTING_NONE
)
142 return; // Process could have crashed.
144 if (!message_ports_
.count(entangled_message_port_id
)) {
149 PostMessageTo(entangled_message_port_id
, message
, sent_message_port_ids
);
152 void MessagePortService::PostMessageTo(
154 const base::string16
& message
,
155 const std::vector
<int>& sent_message_port_ids
) {
156 if (!message_ports_
.count(message_port_id
)) {
160 for (size_t i
= 0; i
< sent_message_port_ids
.size(); ++i
) {
161 if (!message_ports_
.count(sent_message_port_ids
[i
])) {
167 MessagePort
& entangled_port
= message_ports_
[message_port_id
];
169 std::vector
<MessagePort
*> sent_ports(sent_message_port_ids
.size());
170 for (size_t i
= 0; i
< sent_message_port_ids
.size(); ++i
)
171 sent_ports
[i
] = &message_ports_
[sent_message_port_ids
[i
]];
173 if (entangled_port
.queue_messages()) {
174 // If the target port is currently holding messages because the destination
175 // renderer isn't available yet, all message ports being sent should also be
176 // put in this state.
177 if (entangled_port
.hold_messages_for_destination
) {
178 for (int sent_message_port_id
: sent_message_port_ids
)
179 HoldMessages(sent_message_port_id
);
181 entangled_port
.queued_messages
.push_back(
182 std::make_pair(message
, sent_message_port_ids
));
186 if (!entangled_port
.filter
) {
191 // If a message port was sent around, the new location will need a routing
192 // id. Instead of having the created port send us a sync message to get it,
193 // send along with the message.
194 std::vector
<int> new_routing_ids(sent_message_port_ids
.size());
195 for (size_t i
= 0; i
< sent_message_port_ids
.size(); ++i
) {
196 new_routing_ids
[i
] = entangled_port
.filter
->GetNextRoutingID();
197 sent_ports
[i
]->filter
= entangled_port
.filter
;
199 // Update the entry for the sent port as it can be in a different process.
200 sent_ports
[i
]->route_id
= new_routing_ids
[i
];
203 // Now send the message to the entangled port.
204 entangled_port
.filter
->Send(new MessagePortMsg_Message(
205 entangled_port
.route_id
, message
, sent_message_port_ids
,
209 void MessagePortService::QueueMessages(int message_port_id
) {
210 if (!message_ports_
.count(message_port_id
)) {
215 MessagePort
& port
= message_ports_
[message_port_id
];
217 port
.filter
->Send(new MessagePortMsg_MessagesQueued(port
.route_id
));
218 port
.queue_for_inflight_messages
= true;
223 void MessagePortService::SendQueuedMessages(
225 const QueuedMessages
& queued_messages
) {
226 if (!message_ports_
.count(message_port_id
)) {
231 // Send the queued messages to the port again. This time they'll reach the
233 MessagePort
& port
= message_ports_
[message_port_id
];
234 port
.queue_for_inflight_messages
= false;
236 // If the port is currently holding messages waiting for the target renderer,
237 // all ports in messages being sent to the port should also be put on hold.
238 if (port
.hold_messages_for_destination
) {
239 for (const auto& message
: queued_messages
)
240 for (int sent_message_port_id
: message
.second
)
241 HoldMessages(sent_message_port_id
);
244 port
.queued_messages
.insert(port
.queued_messages
.begin(),
245 queued_messages
.begin(),
246 queued_messages
.end());
248 if (port
.should_be_destroyed
)
249 ClosePort(message_port_id
);
251 SendQueuedMessagesIfPossible(message_port_id
);
254 void MessagePortService::SendQueuedMessagesIfPossible(int message_port_id
) {
255 if (!message_ports_
.count(message_port_id
)) {
260 MessagePort
& port
= message_ports_
[message_port_id
];
261 if (port
.queue_messages() || !port
.filter
)
264 for (QueuedMessages::iterator iter
= port
.queued_messages
.begin();
265 iter
!= port
.queued_messages
.end(); ++iter
) {
266 PostMessageTo(message_port_id
, iter
->first
, iter
->second
);
268 port
.queued_messages
.clear();
271 void MessagePortService::HoldMessages(int message_port_id
) {
272 if (!message_ports_
.count(message_port_id
)) {
277 // Any ports in messages currently in the queue should also be put on hold.
278 for (const auto& message
: message_ports_
[message_port_id
].queued_messages
)
279 for (int sent_message_port_id
: message
.second
)
280 HoldMessages(sent_message_port_id
);
282 message_ports_
[message_port_id
].hold_messages_for_destination
= true;
285 void MessagePortService::ClosePort(int message_port_id
) {
286 if (!message_ports_
.count(message_port_id
)) {
291 if (message_ports_
[message_port_id
].queue_for_inflight_messages
) {
292 message_ports_
[message_port_id
].should_be_destroyed
= true;
296 // First close any message ports in the queue for this message port.
297 for (const auto& message
: message_ports_
[message_port_id
].queued_messages
)
298 for (int sent_message_port_id
: message
.second
)
299 ClosePort(sent_message_port_id
);
301 Erase(message_port_id
);
304 void MessagePortService::ReleaseMessages(int message_port_id
) {
305 if (!message_ports_
.count(message_port_id
)) {
310 message_ports_
[message_port_id
].hold_messages_for_destination
= false;
311 SendQueuedMessagesIfPossible(message_port_id
);
314 void MessagePortService::Erase(int message_port_id
) {
315 MessagePorts::iterator erase_item
= message_ports_
.find(message_port_id
);
316 DCHECK(erase_item
!= message_ports_
.end());
318 int entangled_id
= erase_item
->second
.entangled_message_port_id
;
319 if (entangled_id
!= MSG_ROUTING_NONE
) {
320 // Do the disentanglement (and be paranoid about the other side existing
321 // just in case something unusual happened during entanglement).
322 if (message_ports_
.count(entangled_id
)) {
323 message_ports_
[entangled_id
].entangled_message_port_id
= MSG_ROUTING_NONE
;
326 message_ports_
.erase(erase_item
);
329 } // namespace content