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