- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / message_port_service.cc
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
34 MessagePortService* MessagePortService::GetInstance() {
35   return Singleton<MessagePortService>::get();
36 }
37
38 MessagePortService::MessagePortService()
39     : next_message_port_id_(0) {
40 }
41
42 MessagePortService::~MessagePortService() {
43 }
44
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
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
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
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
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
109 void MessagePortService::PostMessage(
110     int sender_message_port_id,
111     const 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
131 void MessagePortService::PostMessageTo(
132     int message_port_id,
133     const 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
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
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
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
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