|
1 // Copyright (c) 2006-2008 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 "base/message_loop.h" |
|
6 #include "base/thread.h" |
|
7 #include "chrome/common/ipc_channel_proxy.h" |
|
8 #include "chrome/common/ipc_logging.h" |
|
9 #include "chrome/common/ipc_message_utils.h" |
|
10 |
|
11 namespace IPC { |
|
12 |
|
13 //----------------------------------------------------------------------------- |
|
14 |
|
15 ChannelProxy::Context::Context(Channel::Listener* listener, |
|
16 MessageFilter* filter, |
|
17 MessageLoop* ipc_message_loop) |
|
18 : listener_message_loop_(MessageLoop::current()), |
|
19 listener_(listener), |
|
20 ipc_message_loop_(ipc_message_loop), |
|
21 channel_(NULL), |
|
22 peer_pid_(0), |
|
23 channel_connected_called_(false) { |
|
24 if (filter) |
|
25 filters_.push_back(filter); |
|
26 } |
|
27 |
|
28 void ChannelProxy::Context::CreateChannel(const std::wstring& id, |
|
29 const Channel::Mode& mode) { |
|
30 DCHECK(channel_ == NULL); |
|
31 channel_id_ = id; |
|
32 channel_ = new Channel(id, mode, this); |
|
33 } |
|
34 |
|
35 bool ChannelProxy::Context::TryFilters(const Message& message) { |
|
36 #ifdef IPC_MESSAGE_LOG_ENABLED |
|
37 Logging* logger = Logging::current(); |
|
38 if (logger->Enabled()) |
|
39 logger->OnPreDispatchMessage(message); |
|
40 #endif |
|
41 |
|
42 for (size_t i = 0; i < filters_.size(); ++i) { |
|
43 if (filters_[i]->OnMessageReceived(message)) { |
|
44 #ifdef IPC_MESSAGE_LOG_ENABLED |
|
45 if (logger->Enabled()) |
|
46 logger->OnPostDispatchMessage(message, channel_id_); |
|
47 #endif |
|
48 return true; |
|
49 } |
|
50 } |
|
51 return false; |
|
52 } |
|
53 |
|
54 // Called on the IPC::Channel thread |
|
55 void ChannelProxy::Context::OnMessageReceived(const Message& message) { |
|
56 // First give a chance to the filters to process this message. |
|
57 if (!TryFilters(message)) |
|
58 OnMessageReceivedNoFilter(message); |
|
59 } |
|
60 |
|
61 // Called on the IPC::Channel thread |
|
62 void ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { |
|
63 // NOTE: This code relies on the listener's message loop not going away while |
|
64 // this thread is active. That should be a reasonable assumption, but it |
|
65 // feels risky. We may want to invent some more indirect way of referring to |
|
66 // a MessageLoop if this becomes a problem. |
|
67 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
|
68 this, &Context::OnDispatchMessage, message)); |
|
69 } |
|
70 |
|
71 // Called on the IPC::Channel thread |
|
72 void ChannelProxy::Context::OnChannelConnected(int32_t peer_pid) { |
|
73 peer_pid_ = peer_pid; |
|
74 for (size_t i = 0; i < filters_.size(); ++i) |
|
75 filters_[i]->OnChannelConnected(peer_pid); |
|
76 |
|
77 // See above comment about using listener_message_loop_ here. |
|
78 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
|
79 this, &Context::OnDispatchConnected)); |
|
80 } |
|
81 |
|
82 // Called on the IPC::Channel thread |
|
83 void ChannelProxy::Context::OnChannelError() { |
|
84 for (size_t i = 0; i < filters_.size(); ++i) |
|
85 filters_[i]->OnChannelError(); |
|
86 |
|
87 // See above comment about using listener_message_loop_ here. |
|
88 listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
|
89 this, &Context::OnDispatchError)); |
|
90 } |
|
91 |
|
92 // Called on the IPC::Channel thread |
|
93 void ChannelProxy::Context::OnChannelOpened() { |
|
94 DCHECK(channel_ != NULL); |
|
95 |
|
96 // Assume a reference to ourselves on behalf of this thread. This reference |
|
97 // will be released when we are closed. |
|
98 AddRef(); |
|
99 |
|
100 if (!channel_->Connect()) { |
|
101 OnChannelError(); |
|
102 return; |
|
103 } |
|
104 |
|
105 for (size_t i = 0; i < filters_.size(); ++i) |
|
106 filters_[i]->OnFilterAdded(channel_); |
|
107 } |
|
108 |
|
109 // Called on the IPC::Channel thread |
|
110 void ChannelProxy::Context::OnChannelClosed() { |
|
111 // It's okay for IPC::ChannelProxy::Close to be called more than once, which |
|
112 // would result in this branch being taken. |
|
113 if (!channel_) |
|
114 return; |
|
115 |
|
116 for (size_t i = 0; i < filters_.size(); ++i) { |
|
117 filters_[i]->OnChannelClosing(); |
|
118 filters_[i]->OnFilterRemoved(); |
|
119 } |
|
120 |
|
121 // We don't need the filters anymore. |
|
122 filters_.clear(); |
|
123 |
|
124 delete channel_; |
|
125 channel_ = NULL; |
|
126 |
|
127 // Balance with the reference taken during startup. This may result in |
|
128 // self-destruction. |
|
129 Release(); |
|
130 } |
|
131 |
|
132 // Called on the IPC::Channel thread |
|
133 void ChannelProxy::Context::OnSendMessage(Message* message) { |
|
134 if (!channel_->Send(message)) |
|
135 OnChannelError(); |
|
136 } |
|
137 |
|
138 // Called on the IPC::Channel thread |
|
139 void ChannelProxy::Context::OnAddFilter(MessageFilter* filter) { |
|
140 filters_.push_back(filter); |
|
141 |
|
142 // If the channel has already been created, then we need to send this message |
|
143 // so that the filter gets access to the Channel. |
|
144 if (channel_) |
|
145 filter->OnFilterAdded(channel_); |
|
146 |
|
147 // Balances the AddRef in ChannelProxy::AddFilter. |
|
148 filter->Release(); |
|
149 } |
|
150 |
|
151 // Called on the IPC::Channel thread |
|
152 void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { |
|
153 for (size_t i = 0; i < filters_.size(); ++i) { |
|
154 if (filters_[i].get() == filter) { |
|
155 filter->OnFilterRemoved(); |
|
156 filters_.erase(filters_.begin() + i); |
|
157 return; |
|
158 } |
|
159 } |
|
160 |
|
161 NOTREACHED() << "filter to be removed not found"; |
|
162 } |
|
163 |
|
164 // Called on the listener's thread |
|
165 void ChannelProxy::Context::OnDispatchMessage(const Message& message) { |
|
166 if (!listener_) |
|
167 return; |
|
168 |
|
169 OnDispatchConnected(); |
|
170 |
|
171 #ifdef IPC_MESSAGE_LOG_ENABLED |
|
172 Logging* logger = Logging::current(); |
|
173 if (message.type() == IPC_LOGGING_ID) { |
|
174 logger->OnReceivedLoggingMessage(message); |
|
175 return; |
|
176 } |
|
177 |
|
178 if (logger->Enabled()) |
|
179 logger->OnPreDispatchMessage(message); |
|
180 #endif |
|
181 |
|
182 listener_->OnMessageReceived(message); |
|
183 |
|
184 #ifdef IPC_MESSAGE_LOG_ENABLED |
|
185 if (logger->Enabled()) |
|
186 logger->OnPostDispatchMessage(message, channel_id_); |
|
187 #endif |
|
188 } |
|
189 |
|
190 // Called on the listener's thread |
|
191 void ChannelProxy::Context::OnDispatchConnected() { |
|
192 if (channel_connected_called_) |
|
193 return; |
|
194 |
|
195 channel_connected_called_ = true; |
|
196 if (listener_) |
|
197 listener_->OnChannelConnected(peer_pid_); |
|
198 } |
|
199 |
|
200 // Called on the listener's thread |
|
201 void ChannelProxy::Context::OnDispatchError() { |
|
202 if (listener_) |
|
203 listener_->OnChannelError(); |
|
204 } |
|
205 |
|
206 //----------------------------------------------------------------------------- |
|
207 |
|
208 ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, |
|
209 Channel::Listener* listener, MessageFilter* filter, |
|
210 MessageLoop* ipc_thread) |
|
211 : context_(new Context(listener, filter, ipc_thread)) { |
|
212 Init(channel_id, mode, ipc_thread, true); |
|
213 } |
|
214 |
|
215 ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode, |
|
216 MessageLoop* ipc_thread, Context* context, |
|
217 bool create_pipe_now) |
|
218 : context_(context) { |
|
219 Init(channel_id, mode, ipc_thread, create_pipe_now); |
|
220 } |
|
221 |
|
222 void ChannelProxy::Init(const std::wstring& channel_id, Channel::Mode mode, |
|
223 MessageLoop* ipc_thread_loop, bool create_pipe_now) { |
|
224 if (create_pipe_now) { |
|
225 // Create the channel immediately. This effectively sets up the |
|
226 // low-level pipe so that the client can connect. Without creating |
|
227 // the pipe immediately, it is possible for a listener to attempt |
|
228 // to connect and get an error since the pipe doesn't exist yet. |
|
229 context_->CreateChannel(channel_id, mode); |
|
230 } else { |
|
231 #if defined(OS_POSIX) |
|
232 // TODO(playmobil): On POSIX, IPC::Channel uses a socketpair(), one side of |
|
233 // which needs to be mapped into the child process' address space. |
|
234 // To know the value of the client side FD we need to have already |
|
235 // created a socketpair which currently occurs in IPC::Channel's |
|
236 // constructor. |
|
237 // If we lazilly construct the IPC::Channel then the caller has no way |
|
238 // of knowing the FD #. |
|
239 // |
|
240 // We can solve this either by having the Channel's creation launch the |
|
241 // subprocess itself or by creating the socketpair() externally. |
|
242 NOTIMPLEMENTED(); |
|
243 #endif // defined(OS_POSIX) |
|
244 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
245 context_.get(), &Context::CreateChannel, channel_id, mode)); |
|
246 } |
|
247 |
|
248 // complete initialization on the background thread |
|
249 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
250 context_.get(), &Context::OnChannelOpened)); |
|
251 } |
|
252 |
|
253 void ChannelProxy::Close() { |
|
254 // Clear the backpointer to the listener so that any pending calls to |
|
255 // Context::OnDispatchMessage or OnDispatchError will be ignored. It is |
|
256 // possible that the channel could be closed while it is receiving messages! |
|
257 context_->Clear(); |
|
258 |
|
259 if (context_->ipc_message_loop()) { |
|
260 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
261 context_.get(), &Context::OnChannelClosed)); |
|
262 } |
|
263 } |
|
264 |
|
265 bool ChannelProxy::Send(Message* message) { |
|
266 #ifdef IPC_MESSAGE_LOG_ENABLED |
|
267 Logging::current()->OnSendMessage(message, context_->channel_id()); |
|
268 #endif |
|
269 |
|
270 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
271 context_.get(), &Context::OnSendMessage, message)); |
|
272 return true; |
|
273 } |
|
274 |
|
275 void ChannelProxy::AddFilter(MessageFilter* filter) { |
|
276 // We want to addref the filter to prevent it from |
|
277 // being destroyed before the OnAddFilter call is invoked. |
|
278 filter->AddRef(); |
|
279 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
280 context_.get(), &Context::OnAddFilter, filter)); |
|
281 } |
|
282 |
|
283 void ChannelProxy::RemoveFilter(MessageFilter* filter) { |
|
284 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
|
285 context_.get(), &Context::OnRemoveFilter, filter)); |
|
286 } |
|
287 |
|
288 #if defined(OS_POSIX) |
|
289 // See the TODO regarding lazy initialization of the channel in |
|
290 // ChannelProxy::Init(). |
|
291 // We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe. |
|
292 void ChannelProxy::GetClientFileDescriptorMapping(int *src_fd, |
|
293 int *dest_fd) const { |
|
294 Channel *channel = context_.get()->channel_; |
|
295 DCHECK(channel); // Channel must have been created first. |
|
296 channel->GetClientFileDescriptorMapping(src_fd, dest_fd); |
|
297 } |
|
298 #endif |
|
299 |
|
300 //----------------------------------------------------------------------------- |
|
301 |
|
302 } // namespace IPC |