OLD | NEW |
| (Empty) |
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::string& 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 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::string& 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::string& 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::string& 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 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
232 context_.get(), &Context::CreateChannel, channel_id, mode)); | |
233 } | |
234 | |
235 // complete initialization on the background thread | |
236 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
237 context_.get(), &Context::OnChannelOpened)); | |
238 } | |
239 | |
240 void ChannelProxy::Close() { | |
241 // Clear the backpointer to the listener so that any pending calls to | |
242 // Context::OnDispatchMessage or OnDispatchError will be ignored. It is | |
243 // possible that the channel could be closed while it is receiving messages! | |
244 context_->Clear(); | |
245 | |
246 if (context_->ipc_message_loop()) { | |
247 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
248 context_.get(), &Context::OnChannelClosed)); | |
249 } | |
250 } | |
251 | |
252 bool ChannelProxy::Send(Message* message) { | |
253 #ifdef IPC_MESSAGE_LOG_ENABLED | |
254 Logging::current()->OnSendMessage(message, context_->channel_id()); | |
255 #endif | |
256 | |
257 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
258 context_.get(), &Context::OnSendMessage, message)); | |
259 return true; | |
260 } | |
261 | |
262 void ChannelProxy::AddFilter(MessageFilter* filter) { | |
263 // We want to addref the filter to prevent it from | |
264 // being destroyed before the OnAddFilter call is invoked. | |
265 filter->AddRef(); | |
266 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
267 context_.get(), &Context::OnAddFilter, filter)); | |
268 } | |
269 | |
270 void ChannelProxy::RemoveFilter(MessageFilter* filter) { | |
271 context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( | |
272 context_.get(), &Context::OnRemoveFilter, filter)); | |
273 } | |
274 | |
275 #if defined(OS_POSIX) | |
276 // See the TODO regarding lazy initialization of the channel in | |
277 // ChannelProxy::Init(). | |
278 // We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe. | |
279 int ChannelProxy::GetClientFileDescriptor() const { | |
280 Channel *channel = context_.get()->channel_; | |
281 DCHECK(channel); // Channel must have been created first. | |
282 return channel->GetClientFileDescriptor(); | |
283 } | |
284 #endif | |
285 | |
286 //----------------------------------------------------------------------------- | |
287 | |
288 } // namespace IPC | |
OLD | NEW |