- add sources.
[platform/framework/web/crosswalk.git] / src / content / child / npapi / np_channel_base.cc
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 "base/auto_reset.h"
8 #include "base/containers/hash_tables.h"
9 #include "base/lazy_instance.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/threading/thread_local.h"
12 #include "ipc/ipc_sync_message.h"
13
14 #if defined(OS_POSIX)
15 #include "ipc/ipc_channel_posix.h"
16 #endif
17
18 namespace content {
19
20 namespace {
21
22 typedef base::hash_map<std::string, scoped_refptr<NPChannelBase> > ChannelMap;
23
24 struct ChannelGlobals {
25   ChannelMap channel_map;
26   scoped_refptr<NPChannelBase> current_channel;
27 };
28
29 #if defined(OS_ANDROID)
30 // Workaround for http://crbug.com/298179 - NPChannelBase is only intended
31 // for use on one thread per process. Using TLS to store the globals removes the
32 // worst thread hostility in this class, especially needed for webview which
33 // runs in single-process mode. TODO(joth): Make a complete fix, most likely
34 // as part of addressing http://crbug.com/258510.
35 base::LazyInstance<base::ThreadLocalPointer<ChannelGlobals> >::Leaky
36     g_channels_tls_ptr = LAZY_INSTANCE_INITIALIZER;
37
38 ChannelGlobals* GetChannelGlobals() {
39   ChannelGlobals* globals = g_channels_tls_ptr.Get().Get();
40   if (!globals) {
41     globals = new ChannelGlobals;
42     g_channels_tls_ptr.Get().Set(globals);
43   }
44   return globals;
45 }
46
47 #else
48
49 base::LazyInstance<ChannelGlobals>::Leaky g_channels_globals =
50     LAZY_INSTANCE_INITIALIZER;
51
52 ChannelGlobals* GetChannelGlobals() { return g_channels_globals.Pointer(); }
53
54 #endif  // OS_ANDROID
55
56 ChannelMap* GetChannelMap() {
57   return &GetChannelGlobals()->channel_map;
58 }
59
60 }  // namespace
61
62 NPChannelBase* NPChannelBase::GetChannel(
63     const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode,
64     ChannelFactory factory, base::MessageLoopProxy* ipc_message_loop,
65     bool create_pipe_now, base::WaitableEvent* shutdown_event) {
66   scoped_refptr<NPChannelBase> channel;
67   std::string channel_key = channel_handle.name;
68   ChannelMap::const_iterator iter = GetChannelMap()->find(channel_key);
69   if (iter == GetChannelMap()->end()) {
70     channel = factory();
71   } else {
72     channel = iter->second;
73   }
74
75   DCHECK(channel.get() != NULL);
76
77   if (!channel->channel_valid()) {
78     channel->channel_handle_ = channel_handle;
79     if (mode & IPC::Channel::MODE_SERVER_FLAG) {
80       channel->channel_handle_.name =
81           IPC::Channel::GenerateVerifiedChannelID(channel_key);
82     }
83     channel->mode_ = mode;
84     if (channel->Init(ipc_message_loop, create_pipe_now, shutdown_event)) {
85       (*GetChannelMap())[channel_key] = channel;
86     } else {
87       channel = NULL;
88     }
89   }
90
91   return channel.get();
92 }
93
94 void NPChannelBase::Broadcast(IPC::Message* message) {
95   for (ChannelMap::iterator iter = GetChannelMap()->begin();
96        iter != GetChannelMap()->end();
97        ++iter) {
98     iter->second->Send(new IPC::Message(*message));
99   }
100   delete message;
101 }
102
103 NPChannelBase::NPChannelBase()
104     : mode_(IPC::Channel::MODE_NONE),
105       non_npobject_count_(0),
106       peer_pid_(0),
107       in_remove_route_(false),
108       default_owner_(NULL),
109       channel_valid_(false),
110       in_unblock_dispatch_(0),
111       send_unblocking_only_during_unblock_dispatch_(false) {
112 }
113
114 NPChannelBase::~NPChannelBase() {
115   // TODO(wez): Establish why these would ever be non-empty at teardown.
116   //DCHECK(npobject_listeners_.empty());
117   //DCHECK(proxy_map_.empty());
118   //DCHECK(stub_map_.empty());
119   DCHECK(owner_to_route_.empty());
120   DCHECK(route_to_owner_.empty());
121 }
122
123 NPChannelBase* NPChannelBase::GetCurrentChannel() {
124   return GetChannelGlobals()->current_channel.get();
125 }
126
127 void NPChannelBase::CleanupChannels() {
128   // Make a copy of the references as we can't iterate the map since items will
129   // be removed from it as we clean them up.
130   std::vector<scoped_refptr<NPChannelBase> > channels;
131   for (ChannelMap::const_iterator iter = GetChannelMap()->begin();
132        iter != GetChannelMap()->end();
133        ++iter) {
134     channels.push_back(iter->second);
135   }
136
137   for (size_t i = 0; i < channels.size(); ++i)
138     channels[i]->CleanUp();
139
140   // This will clean up channels added to the map for which subsequent
141   // AddRoute wasn't called
142   GetChannelMap()->clear();
143 }
144
145 NPObjectBase* NPChannelBase::GetNPObjectListenerForRoute(int route_id) {
146   ListenerMap::iterator iter = npobject_listeners_.find(route_id);
147   if (iter == npobject_listeners_.end()) {
148     DLOG(WARNING) << "Invalid route id passed in:" << route_id;
149     return NULL;
150   }
151   return iter->second;
152 }
153
154 base::WaitableEvent* NPChannelBase::GetModalDialogEvent(int render_view_id) {
155   return NULL;
156 }
157
158 bool NPChannelBase::Init(base::MessageLoopProxy* ipc_message_loop,
159                          bool create_pipe_now,
160                          base::WaitableEvent* shutdown_event) {
161 #if defined(OS_POSIX)
162   // Attempting to initialize with an invalid channel handle.
163   // See http://crbug.com/97285 for details.
164   if (mode_ == IPC::Channel::MODE_CLIENT && -1 == channel_handle_.socket.fd)
165     return false;
166 #endif
167
168   channel_.reset(new IPC::SyncChannel(
169       channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
170       shutdown_event));
171
172 #if defined(OS_POSIX)
173   // Check the validity of fd for bug investigation.  Remove after fixed.
174   // See crbug.com/97285 for details.
175   if (mode_ == IPC::Channel::MODE_SERVER)
176     CHECK_NE(-1, channel_->GetClientFileDescriptor());
177 #endif
178
179   channel_valid_ = true;
180   return true;
181 }
182
183 bool NPChannelBase::Send(IPC::Message* message) {
184   if (!channel_) {
185     VLOG(1) << "Channel is NULL; dropping message";
186     delete message;
187     return false;
188   }
189
190   if (send_unblocking_only_during_unblock_dispatch_ &&
191       in_unblock_dispatch_ == 0 &&
192       message->is_sync()) {
193     message->set_unblock(false);
194   }
195
196   return channel_->Send(message);
197 }
198
199 int NPChannelBase::Count() {
200   return static_cast<int>(GetChannelMap()->size());
201 }
202
203 bool NPChannelBase::OnMessageReceived(const IPC::Message& message) {
204   // Push this channel as the current channel being processed. This also forms
205   // a stack of scoped_refptr avoiding ourselves (or any instance higher
206   // up the callstack) from being deleted while processing a message.
207   base::AutoReset<scoped_refptr<NPChannelBase> > keep_alive(
208       &GetChannelGlobals()->current_channel, this);
209
210   bool handled;
211   if (message.should_unblock())
212     in_unblock_dispatch_++;
213   if (message.routing_id() == MSG_ROUTING_CONTROL) {
214     handled = OnControlMessageReceived(message);
215   } else {
216     handled = router_.RouteMessage(message);
217     if (!handled && message.is_sync()) {
218       // The listener has gone away, so we must respond or else the caller will
219       // hang waiting for a reply.
220       IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
221       reply->set_reply_error();
222       Send(reply);
223     }
224   }
225   if (message.should_unblock())
226     in_unblock_dispatch_--;
227
228   return handled;
229 }
230
231 void NPChannelBase::OnChannelConnected(int32 peer_pid) {
232   peer_pid_ = peer_pid;
233 }
234
235 void NPChannelBase::AddRoute(int route_id,
236                              IPC::Listener* listener,
237                              NPObjectBase* npobject) {
238   if (npobject) {
239     npobject_listeners_[route_id] = npobject;
240   } else {
241     non_npobject_count_++;
242   }
243
244   router_.AddRoute(route_id, listener);
245 }
246
247 void NPChannelBase::RemoveRoute(int route_id) {
248   router_.RemoveRoute(route_id);
249
250   ListenerMap::iterator iter = npobject_listeners_.find(route_id);
251   if (iter != npobject_listeners_.end()) {
252     // This was an NPObject proxy or stub, it's not involved in the refcounting.
253
254     // If this RemoveRoute call from the NPObject is a result of us calling
255     // OnChannelError below, don't call erase() here because that'll corrupt
256     // the iterator below.
257     if (in_remove_route_) {
258       iter->second = NULL;
259     } else {
260       npobject_listeners_.erase(iter);
261     }
262
263     return;
264   }
265
266   non_npobject_count_--;
267   DCHECK(non_npobject_count_ >= 0);
268
269   if (!non_npobject_count_) {
270     base::AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true);
271     for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin();
272          npobj_iter != npobject_listeners_.end(); ++npobj_iter) {
273       if (npobj_iter->second) {
274         npobj_iter->second->GetChannelListener()->OnChannelError();
275       }
276     }
277
278     for (ChannelMap::iterator iter = GetChannelMap()->begin();
279          iter != GetChannelMap()->end(); ++iter) {
280       if (iter->second.get() == this) {
281         GetChannelMap()->erase(iter);
282         return;
283       }
284     }
285
286     NOTREACHED();
287   }
288 }
289
290 bool NPChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
291   NOTREACHED() <<
292       "should override in subclass if you care about control messages";
293   return false;
294 }
295
296 void NPChannelBase::OnChannelError() {
297   channel_valid_ = false;
298
299   // TODO(shess): http://crbug.com/97285
300   // Once an error is seen on a channel, remap the channel to prevent
301   // it from being vended again.  Keep the channel in the map so
302   // RemoveRoute() can clean things up correctly.
303   for (ChannelMap::iterator iter = GetChannelMap()->begin();
304        iter != GetChannelMap()->end(); ++iter) {
305     if (iter->second.get() == this) {
306       // Insert new element before invalidating |iter|.
307       (*GetChannelMap())[iter->first + "-error"] = iter->second;
308       GetChannelMap()->erase(iter);
309       break;
310     }
311   }
312 }
313
314 void NPChannelBase::AddMappingForNPObjectProxy(int route_id,
315                                                NPObject* object) {
316   proxy_map_[route_id] = object;
317 }
318
319 void NPChannelBase::RemoveMappingForNPObjectProxy(int route_id) {
320   proxy_map_.erase(route_id);
321 }
322
323 void NPChannelBase::AddMappingForNPObjectStub(int route_id,
324                                               NPObject* object) {
325   DCHECK(object != NULL);
326   stub_map_[object] = route_id;
327 }
328
329 void NPChannelBase::RemoveMappingForNPObjectStub(int route_id,
330                                                  NPObject* object) {
331   DCHECK(object != NULL);
332   stub_map_.erase(object);
333 }
334
335 void NPChannelBase::AddMappingForNPObjectOwner(int route_id,
336                                                struct _NPP* owner) {
337   DCHECK(owner != NULL);
338   route_to_owner_[route_id] = owner;
339   owner_to_route_[owner] = route_id;
340 }
341
342 void NPChannelBase::SetDefaultNPObjectOwner(struct _NPP* owner) {
343   DCHECK(owner != NULL);
344   default_owner_ = owner;
345 }
346
347 void NPChannelBase::RemoveMappingForNPObjectOwner(int route_id) {
348   DCHECK(route_to_owner_.find(route_id) != route_to_owner_.end());
349   owner_to_route_.erase(route_to_owner_[route_id]);
350   route_to_owner_.erase(route_id);
351 }
352
353 NPObject* NPChannelBase::GetExistingNPObjectProxy(int route_id) {
354   ProxyMap::iterator iter = proxy_map_.find(route_id);
355   return iter != proxy_map_.end() ? iter->second : NULL;
356 }
357
358 int NPChannelBase::GetExistingRouteForNPObjectStub(NPObject* npobject) {
359   StubMap::iterator iter = stub_map_.find(npobject);
360   return iter != stub_map_.end() ? iter->second : MSG_ROUTING_NONE;
361 }
362
363 NPP NPChannelBase::GetExistingNPObjectOwner(int route_id) {
364   RouteToOwnerMap::iterator iter = route_to_owner_.find(route_id);
365   return iter != route_to_owner_.end() ? iter->second : default_owner_;
366 }
367
368 int NPChannelBase::GetExistingRouteForNPObjectOwner(NPP owner) {
369   OwnerToRouteMap::iterator iter = owner_to_route_.find(owner);
370   return iter != owner_to_route_.end() ? iter->second : MSG_ROUTING_NONE;
371 }
372
373 }  // namespace content