Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / child / npapi / np_channel_base.cc
blob341a3da14e1d55686ce312de94c6da56f6e749dc
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.
5 #include "content/child/npapi/np_channel_base.h"
7 #include "base/auto_reset.h"
8 #include "base/containers/hash_tables.h"
9 #include "base/files/scoped_file.h"
10 #include "base/lazy_instance.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/threading/thread_local.h"
14 #include "ipc/ipc_sync_message.h"
16 #if defined(OS_POSIX)
17 #include "base/files/file_util.h"
18 #include "ipc/ipc_channel_posix.h"
19 #endif
21 namespace content {
23 namespace {
25 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
27 struct ChannelGlobals {
28 ChannelMap channel_map;
29 scoped_refptr<NPChannelBase> current_channel;
32 #if defined(OS_ANDROID)
33 // Workaround for http://crbug.com/298179 - NPChannelBase is only intended
34 // for use on one thread per process. Using TLS to store the globals removes the
35 // worst thread hostility in this class, especially needed for webview which
36 // runs in single-process mode. TODO(joth): Make a complete fix, most likely
37 // as part of addressing http://crbug.com/258510.
38 base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
39 g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
41 ChannelGlobals* GetChannelGlobals() {
42 ChannelGlobals* globals = g_channels_tls_ptr.Get().Get();
43 if (!globals) {
44 globals = new ChannelGlobals;
45 g_channels_tls_ptr.Get().Set(globals);
47 return globals;
50 #else
52 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
53 LAZY_INSTANCE_INITIALIZER;
55 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
57 #endif // OS_ANDROID
59 ChannelMap* GetChannelMap() {
60 return &GetChannelGlobals()->channel_map;
63 } // namespace
65 NPChannelBase* NPChannelBase::GetChannel(
66 const IPC::ChannelHandle& channel_handle,
67 IPC::Channel::Mode mode,
68 ChannelFactory factory,
69 base::SingleThreadTaskRunner* ipc_task_runner,
70 bool create_pipe_now,
71 base::WaitableEvent* shutdown_event,
72 IPC::AttachmentBroker* broker) {
73 #if defined(OS_POSIX)
74 // On POSIX the channel_handle conveys an FD (socket) which is duped by the
75 // kernel during the IPC message exchange (via the SCM_RIGHTS mechanism).
76 // Ensure we do not leak this FD.
77 base::ScopedFD fd(channel_handle.socket.auto_close ?
78 channel_handle.socket.fd : -1);
79 #endif
81 scoped_refptr<NPChannelBase> channel;
82 std::string channel_key = channel_handle.name;
83 ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key);
84 if (iter == GetChannelMap()->end()) {
85 channel = factory();
86 } else {
87 channel = iter->second;
90 DCHECK(channel.get() != NULL);
92 if (!channel->channel_valid()) {
93 channel->channel_handle_ = channel_handle;
94 #if defined(OS_POSIX)
95 ignore_result(fd.release());
96 #endif
97 if (mode & IPC::Channel::MODE_SERVER_FLAG) {
98 channel->channel_handle_.name =
99 IPC::Channel::GenerateVerifiedChannelID(channel_key);
101 channel->mode_ = mode;
102 if (channel->Init(ipc_task_runner, create_pipe_now, shutdown_event,
103 broker)) {
104 (*GetChannelMap())[channel_key] = channel;
105 } else {
106 channel = NULL;
110 return channel.get();
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));
119 delete message;
122 NPChannelBase::NPChannelBase()
123 : mode_(IPC::Channel::MODE_NONE),
124 non_npobject_count_(0),
125 peer_pid_(0),
126 in_remove_route_(false),
127 default_owner_(NULL),
128 channel_valid_(false),
129 in_unblock_dispatch_(0),
130 send_unblocking_only_during_unblock_dispatch_(false) {
133 NPChannelBase::~NPChannelBase() {
134 // TODO(wez): Establish why these would ever be non-empty at teardown.
135 //DCHECK(npobject_listeners_.empty());
136 //DCHECK(proxy_map_.empty());
137 //DCHECK(stub_map_.empty());
138 DCHECK(owner_to_route_.empty());
139 DCHECK(route_to_owner_.empty());
142 NPChannelBase* NPChannelBase::GetCurrentChannel() {
143 return GetChannelGlobals()->current_channel.get();
146 void NPChannelBase::CleanupChannels() {
147 // Make a copy of the references as we can't iterate the map since items will
148 // be removed from it as we clean them up.
149 std::vector<scoped_refptr<NPChannelBase> > channels;
150 for (ChannelMap::const_iterator iter = GetChannelMap()->begin();
151 iter != GetChannelMap()->end();
152 ++iter) {
153 channels.push_back(iter->second);
156 for (size_t i = 0; i < channels.size(); ++i)
157 channels[i]->CleanUp();
159 // This will clean up channels added to the map for which subsequent
160 // AddRoute wasn't called
161 GetChannelMap()->clear();
164 NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) {
165 ListenerMap::iterator iter = npobject_listeners_.find(route_id);
166 if (iter == npobject_listeners_.end()) {
167 DLOG(WARNING) << "Invalid route id passed in:" << route_id;
168 return NULL;
170 return iter->second;
173 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
174 return NULL;
177 bool NPChannelBase::Init(base::SingleThreadTaskRunner* ipc_task_runner,
178 bool create_pipe_now,
179 base::WaitableEvent* shutdown_event,
180 IPC::AttachmentBroker* broker) {
181 #if defined(OS_POSIX)
182 // Attempting to initialize with an invalid channel handle.
183 // See http://crbug.com/97285 for details.
184 if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd)
185 return false;
186 #endif
188 channel_ =
189 IPC::SyncChannel::Create(channel_handle_, mode_, this, ipc_task_runner,
190 create_pipe_now, shutdown_event, broker);
192 #if defined(OS_POSIX)
193 // Check the validity of fd for bug investigation. Remove after fixed.
194 // See crbug.com/97285 for details.
195 if (mode_ == IPC::Channel::MODE_SERVER)
196 CHECK_NE(-1, channel_->GetClientFileDescriptor());
197 #endif
199 channel_valid_ = true;
200 return true;
203 bool NPChannelBase::Send(IPC::Message* message) {
204 if (!channel_) {
205 VLOG(1) << "Channel is NULL; dropping message";
206 delete message;
207 return false;
210 if (send_unblocking_only_during_unblock_dispatch_ &&
211 in_unblock_dispatch_ == 0 &&
212 message->is_sync()) {
213 message->set_unblock(false);
216 return channel_->Send(message);
219 int NPChannelBase::Count() {
220 return static_cast<int>(GetChannelMap()->size());
223 bool NPChannelBase::OnMessageReceived(const IPC::Message& message) {
224 // Push this channel as the current channel being processed. This also forms
225 // a stack of scoped_refptr avoiding ourselves (or any instance higher
226 // up the callstack) from being deleted while processing a message.
227 base::AutoReset<scoped_refptr<NPChannelBase> > keep_alive(
228 &GetChannelGlobals()->current_channel, this);
230 bool handled;
231 if (message.should_unblock())
232 in_unblock_dispatch_++;
233 if (message.routing_id() == MSG_ROUTING_CONTROL) {
234 handled = OnControlMessageReceived(message);
235 } else {
236 handled = router_.RouteMessage(message);
237 if (!handled && message.is_sync()) {
238 // The listener has gone away, so we must respond or else the caller will
239 // hang waiting for a reply.
240 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
241 reply->set_reply_error();
242 Send(reply);
245 if (message.should_unblock())
246 in_unblock_dispatch_--;
248 return handled;
251 void NPChannelBase::OnChannelConnected(int32 peer_pid) {
252 peer_pid_ = peer_pid;
255 void NPChannelBase::AddRoute(int route_id,
256 IPC::Listener* listener,
257 NPObjectBase* npobject) {
258 if (npobject) {
259 npobject_listeners_[route_id] = npobject;
260 } else {
261 non_npobject_count_++;
264 router_.AddRoute(route_id, listener);
267 void NPChannelBase::RemoveRoute(int route_id) {
268 router_.RemoveRoute(route_id);
270 ListenerMap::iterator iter = npobject_listeners_.find(route_id);
271 if (iter != npobject_listeners_.end()) {
272 // This was an NPObject proxy or stub, it's not involved in the refcounting.
274 // If this RemoveRoute call from the NPObject is a result of us calling
275 // OnChannelError below, don't call erase() here because that'll corrupt
276 // the iterator below.
277 if (in_remove_route_) {
278 iter->second = NULL;
279 } else {
280 npobject_listeners_.erase(iter);
283 return;
286 non_npobject_count_--;
287 DCHECK(non_npobject_count_ >= 0);
289 if (!non_npobject_count_) {
290 base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true);
291 for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin();
292 npobj_iter != npobject_listeners_.end(); ++npobj_iter) {
293 if (npobj_iter->second) {
294 npobj_iter->second->GetChannelListener()->OnChannelError();
298 for (ChannelMap::iterator iter = GetChannelMap()->begin();
299 iter != GetChannelMap()->end(); ++iter) {
300 if (iter->second.get() == this) {
301 GetChannelMap()->erase(iter);
302 return;
306 NOTREACHED();
310 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
311 NOTREACHED() <<
312 "should override in subclass if you care about control messages";
313 return false;
316 void NPChannelBase::OnChannelError() {
317 channel_valid_ = false;
319 // TODO(shess): http://crbug.com/97285
320 // Once an error is seen on a channel, remap the channel to prevent
321 // it from being vended again. Keep the channel in the map so
322 // RemoveRoute() can clean things up correctly.
323 for (ChannelMap::iterator iter = GetChannelMap()->begin();
324 iter != GetChannelMap()->end(); ++iter) {
325 if (iter->second.get() == this) {
326 // Insert new element before invalidating |iter|.
327 (*GetChannelMap())[iter->first + "-error"] = iter->second;
328 GetChannelMap()->erase(iter);
329 break;
334 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
335 NPObject* object) {
336 proxy_map_[route_id] = object;
339 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
340 proxy_map_.erase(route_id);
343 void NPChannelBase::AddMappingForNPObjectStub(int route_id,
344 NPObject* object) {
345 DCHECK(object != NULL);
346 stub_map_[object] = route_id;
349 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
350 NPObject* object) {
351 DCHECK(object != NULL);
352 stub_map_.erase(object);
355 void NPChannelBase::AddMappingForNPObjectOwner(int route_id,
356 struct _NPP* owner) {
357 DCHECK(owner != NULL);
358 route_to_owner_[route_id] = owner;
359 owner_to_route_[owner] = route_id;
362 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
363 DCHECK(owner != NULL);
364 default_owner_ = owner;
367 void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id) {
368 DCHECK(route_to_owner_.find(route_id) != route_to_owner_.end());
369 owner_to_route_.erase(route_to_owner_[route_id]);
370 route_to_owner_.erase(route_id);
373 NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) {
374 ProxyMap::iterator iter = proxy_map_.find(route_id);
375 return iter != proxy_map_.end() ? iter->second : NULL;
378 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) {
379 StubMap::iterator iter = stub_map_.find(npobject);
380 return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE;
383 NPP NPChannelBase::GetExistingNPObjectOwner(int route_id) {
384 RouteToOwnerMap::iterator iter = route_to_owner_.find(route_id);
385 return iter != route_to_owner_.end() ? iter->second : default_owner_;
388 int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner) {
389 OwnerToRouteMap::iterator iter = owner_to_route_.find(owner);
390 return iter != owner_to_route_.end() ? iter->second : MSG_ROUTING_NONE;
393 } // namespace content