OLD | NEW |
| (Empty) |
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/child/npapi/np_channel_base.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include "base/auto_reset.h" | |
10 #include "base/containers/hash_tables.h" | |
11 #include "base/files/scoped_file.h" | |
12 #include "base/lazy_instance.h" | |
13 #include "base/single_thread_task_runner.h" | |
14 #include "base/threading/thread_local.h" | |
15 #include "build/build_config.h" | |
16 #include "ipc/ipc_sync_message.h" | |
17 | |
18 #if defined(OS_POSIX) | |
19 #include "base/files/file_util.h" | |
20 #include "ipc/ipc_channel_posix.h" | |
21 #endif | |
22 | |
23 namespace content { | |
24 | |
25 namespace { | |
26 | |
27 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap; | |
28 | |
29 struct ChannelGlobals { | |
30 ChannelMap channel_map; | |
31 scoped_refptr<NPChannelBase> current_channel; | |
32 }; | |
33 | |
34 #if defined(OS_ANDROID) | |
35 // Workaround for http://crbug.com/298179 - NPChannelBase is only intended | |
36 // for use on one thread per process. Using TLS to store the globals removes the | |
37 // worst thread hostility in this class, especially needed for webview which | |
38 // runs in single-process mode. TODO(joth): Make a complete fix, most likely | |
39 // as part of addressing http://crbug.com/258510. | |
40 base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky | |
41 g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER; | |
42 | |
43 ChannelGlobals* GetChannelGlobals() { | |
44 ChannelGlobals* globals = g_channels_tls_ptr.Get().Get(); | |
45 if (!globals) { | |
46 globals = new ChannelGlobals; | |
47 g_channels_tls_ptr.Get().Set(globals); | |
48 } | |
49 return globals; | |
50 } | |
51 | |
52 #else | |
53 | |
54 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals = | |
55 LAZY_INSTANCE_INITIALIZER; | |
56 | |
57 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); } | |
58 | |
59 #endif // OS_ANDROID | |
60 | |
61 ChannelMap* GetChannelMap() { | |
62 return &GetChannelGlobals()->channel_map; | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 NPChannelBase* NPChannelBase::GetChannel( | |
68 const IPC::ChannelHandle& channel_handle, | |
69 IPC::Channel::Mode mode, | |
70 ChannelFactory factory, | |
71 base::SingleThreadTaskRunner* ipc_task_runner, | |
72 bool create_pipe_now, | |
73 base::WaitableEvent* shutdown_event) { | |
74 #if defined(OS_POSIX) | |
75 // On POSIX the channel_handle conveys an FD (socket) which is duped by the | |
76 // kernel during the IPC message exchange (via the SCM_RIGHTS mechanism). | |
77 // Ensure we do not leak this FD. | |
78 base::ScopedFD fd(channel_handle.socket.auto_close ? | |
79 channel_handle.socket.fd : -1); | |
80 #endif | |
81 | |
82 scoped_refptr<NPChannelBase> channel; | |
83 std::string channel_key = channel_handle.name; | |
84 ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key); | |
85 if (iter == GetChannelMap()->end()) { | |
86 channel = factory(); | |
87 } else { | |
88 channel = iter->second; | |
89 } | |
90 | |
91 DCHECK(channel.get() != NULL); | |
92 | |
93 if (!channel->channel_valid()) { | |
94 channel->channel_handle_ = channel_handle; | |
95 #if defined(OS_POSIX) | |
96 ignore_result(fd.release()); | |
97 #endif | |
98 if (mode & IPC::Channel::MODE_SERVER_FLAG) { | |
99 channel->channel_handle_.name = | |
100 IPC::Channel::GenerateVerifiedChannelID(channel_key); | |
101 } | |
102 channel->mode_ = mode; | |
103 if (channel->Init(ipc_task_runner, create_pipe_now, shutdown_event)) { | |
104 (*GetChannelMap())[channel_key] = channel; | |
105 } else { | |
106 channel = NULL; | |
107 } | |
108 } | |
109 | |
110 return channel.get(); | |
111 } | |
112 | |
113 void NPChannelBase::Broadcast(IPC::Message* message) { | |
114 for (ChannelMap::iterator iter = GetChannelMap()->begin(); | |
115 iter != GetChannelMap()->end(); | |
116 ++iter) { | |
117 iter->second->Send(new IPC::Message(*message)); | |
118 } | |
119 delete message; | |
120 } | |
121 | |
122 NPChannelBase::NPChannelBase() | |
123 : mode_(IPC::Channel::MODE_NONE), | |
124 non_npobject_count_(0), | |
125 peer_pid_(0), | |
126 channel_valid_(false), | |
127 in_unblock_dispatch_(0), | |
128 send_unblocking_only_during_unblock_dispatch_(false) { | |
129 } | |
130 | |
131 NPChannelBase::~NPChannelBase() { | |
132 } | |
133 | |
134 NPChannelBase* NPChannelBase::GetCurrentChannel() { | |
135 return GetChannelGlobals()->current_channel.get(); | |
136 } | |
137 | |
138 void NPChannelBase::CleanupChannels() { | |
139 // Make a copy of the references as we can't iterate the map since items will | |
140 // be removed from it as we clean them up. | |
141 std::vector<scoped_refptr<NPChannelBase> > channels; | |
142 for (ChannelMap::const_iterator iter = GetChannelMap()->begin(); | |
143 iter != GetChannelMap()->end(); | |
144 ++iter) { | |
145 channels.push_back(iter->second); | |
146 } | |
147 | |
148 for (size_t i = 0; i < channels.size(); ++i) | |
149 channels[i]->CleanUp(); | |
150 | |
151 // This will clean up channels added to the map for which subsequent | |
152 // AddRoute wasn't called | |
153 GetChannelMap()->clear(); | |
154 } | |
155 | |
156 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) { | |
157 return NULL; | |
158 } | |
159 | |
160 bool NPChannelBase::Init(base::SingleThreadTaskRunner* ipc_task_runner, | |
161 bool create_pipe_now, | |
162 base::WaitableEvent* shutdown_event) { | |
163 #if defined(OS_POSIX) | |
164 // Attempting to initialize with an invalid channel handle. | |
165 // See http://crbug.com/97285 for details. | |
166 if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd) | |
167 return false; | |
168 #endif | |
169 | |
170 channel_ = | |
171 IPC::SyncChannel::Create(channel_handle_, mode_, this, ipc_task_runner, | |
172 create_pipe_now, shutdown_event); | |
173 | |
174 #if defined(OS_POSIX) | |
175 // Check the validity of fd for bug investigation. Remove after fixed. | |
176 // See crbug.com/97285 for details. | |
177 if (mode_ == IPC::Channel::MODE_SERVER) | |
178 CHECK_NE(-1, channel_->GetClientFileDescriptor()); | |
179 #endif | |
180 | |
181 channel_valid_ = true; | |
182 return true; | |
183 } | |
184 | |
185 bool NPChannelBase::Send(IPC::Message* message) { | |
186 if (!channel_) { | |
187 VLOG(1) << "Channel is NULL; dropping message"; | |
188 delete message; | |
189 return false; | |
190 } | |
191 | |
192 if (send_unblocking_only_during_unblock_dispatch_ && | |
193 in_unblock_dispatch_ == 0 && | |
194 message->is_sync()) { | |
195 message->set_unblock(false); | |
196 } | |
197 | |
198 return channel_->Send(message); | |
199 } | |
200 | |
201 bool NPChannelBase::OnMessageReceived(const IPC::Message& message) { | |
202 // Push this channel as the current channel being processed. This also forms | |
203 // a stack of scoped_refptr avoiding ourselves (or any instance higher | |
204 // up the callstack) from being deleted while processing a message. | |
205 base::AutoReset<scoped_refptr<NPChannelBase> > keep_alive( | |
206 &GetChannelGlobals()->current_channel, this); | |
207 | |
208 bool handled; | |
209 if (message.should_unblock()) | |
210 in_unblock_dispatch_++; | |
211 if (message.routing_id() == MSG_ROUTING_CONTROL) { | |
212 handled = OnControlMessageReceived(message); | |
213 } else { | |
214 handled = router_.RouteMessage(message); | |
215 if (!handled && message.is_sync()) { | |
216 // The listener has gone away, so we must respond or else the caller will | |
217 // hang waiting for a reply. | |
218 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); | |
219 reply->set_reply_error(); | |
220 Send(reply); | |
221 } | |
222 } | |
223 if (message.should_unblock()) | |
224 in_unblock_dispatch_--; | |
225 | |
226 return handled; | |
227 } | |
228 | |
229 void NPChannelBase::OnChannelConnected(int32_t peer_pid) { | |
230 peer_pid_ = peer_pid; | |
231 } | |
232 | |
233 void NPChannelBase::AddRoute(int route_id, IPC::Listener* listener) { | |
234 non_npobject_count_++; | |
235 router_.AddRoute(route_id, listener); | |
236 } | |
237 | |
238 void NPChannelBase::RemoveRoute(int route_id) { | |
239 router_.RemoveRoute(route_id); | |
240 | |
241 non_npobject_count_--; | |
242 DCHECK(non_npobject_count_ >= 0); | |
243 | |
244 if (!non_npobject_count_) { | |
245 for (ChannelMap::iterator iter = GetChannelMap()->begin(); | |
246 iter != GetChannelMap()->end(); ++iter) { | |
247 if (iter->second.get() == this) { | |
248 GetChannelMap()->erase(iter); | |
249 return; | |
250 } | |
251 } | |
252 | |
253 NOTREACHED(); | |
254 } | |
255 } | |
256 | |
257 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) { | |
258 NOTREACHED() << | |
259 "should override in subclass if you care about control messages"; | |
260 return false; | |
261 } | |
262 | |
263 void NPChannelBase::OnChannelError() { | |
264 channel_valid_ = false; | |
265 | |
266 // TODO(shess): http://crbug.com/97285 | |
267 // Once an error is seen on a channel, remap the channel to prevent | |
268 // it from being vended again. Keep the channel in the map so | |
269 // RemoveRoute() can clean things up correctly. | |
270 for (ChannelMap::iterator iter = GetChannelMap()->begin(); | |
271 iter != GetChannelMap()->end(); ++iter) { | |
272 if (iter->second.get() == this) { | |
273 // Insert new element before invalidating |iter|. | |
274 (*GetChannelMap())[iter->first + "-error"] = iter->second; | |
275 GetChannelMap()->erase(iter); | |
276 break; | |
277 } | |
278 } | |
279 } | |
280 | |
281 } // namespace content | |
OLD | NEW |