OLD | NEW |
| (Empty) |
1 // Copyright (c) 2009 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 "chrome/plugin/plugin_channel_base.h" | |
6 | |
7 #include <stack> | |
8 | |
9 #include "base/auto_reset.h" | |
10 #include "base/hash_tables.h" | |
11 #include "base/lazy_instance.h" | |
12 #include "base/string_number_conversions.h" | |
13 #include "content/common/child_process.h" | |
14 #include "ipc/ipc_sync_message.h" | |
15 | |
16 #if defined(OS_POSIX) | |
17 #include "ipc/ipc_channel_posix.h" | |
18 #endif | |
19 | |
20 typedef base::hash_map<std::string, scoped_refptr<PluginChannelBase> > | |
21 PluginChannelMap; | |
22 | |
23 static PluginChannelMap g_plugin_channels_; | |
24 | |
25 static base::LazyInstance<std::stack<scoped_refptr<PluginChannelBase> > > | |
26 lazy_plugin_channel_stack_(base::LINKER_INITIALIZED); | |
27 | |
28 static int next_pipe_id = 0; | |
29 | |
30 PluginChannelBase* PluginChannelBase::GetChannel( | |
31 const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode, | |
32 PluginChannelFactory factory, MessageLoop* ipc_message_loop, | |
33 bool create_pipe_now) { | |
34 scoped_refptr<PluginChannelBase> channel; | |
35 std::string channel_key = channel_handle.name; | |
36 PluginChannelMap::const_iterator iter = g_plugin_channels_.find(channel_key); | |
37 if (iter == g_plugin_channels_.end()) { | |
38 channel = factory(); | |
39 } else { | |
40 channel = iter->second; | |
41 } | |
42 | |
43 DCHECK(channel != NULL); | |
44 | |
45 if (!channel->channel_valid()) { | |
46 channel->channel_handle_ = channel_handle; | |
47 if (mode & IPC::Channel::MODE_SERVER_FLAG) { | |
48 channel->channel_handle_.name.append("."); | |
49 channel->channel_handle_.name.append(base::IntToString(next_pipe_id++)); | |
50 } | |
51 channel->mode_ = mode; | |
52 if (channel->Init(ipc_message_loop, create_pipe_now)) { | |
53 g_plugin_channels_[channel_key] = channel; | |
54 } else { | |
55 channel = NULL; | |
56 } | |
57 } | |
58 | |
59 return channel; | |
60 } | |
61 | |
62 void PluginChannelBase::Broadcast(IPC::Message* message) { | |
63 for (PluginChannelMap::iterator iter = g_plugin_channels_.begin(); | |
64 iter != g_plugin_channels_.end(); | |
65 ++iter) { | |
66 iter->second->Send(new IPC::Message(*message)); | |
67 } | |
68 delete message; | |
69 } | |
70 | |
71 PluginChannelBase::PluginChannelBase() | |
72 : mode_(IPC::Channel::MODE_NONE), | |
73 plugin_count_(0), | |
74 peer_pid_(0), | |
75 in_remove_route_(false), | |
76 channel_valid_(false), | |
77 in_unblock_dispatch_(0), | |
78 send_unblocking_only_during_unblock_dispatch_(false) { | |
79 } | |
80 | |
81 PluginChannelBase::~PluginChannelBase() { | |
82 } | |
83 | |
84 PluginChannelBase* PluginChannelBase::GetCurrentChannel() { | |
85 return lazy_plugin_channel_stack_.Pointer()->top(); | |
86 } | |
87 | |
88 void PluginChannelBase::CleanupChannels() { | |
89 // Make a copy of the references as we can't iterate the map since items will | |
90 // be removed from it as we clean them up. | |
91 std::vector<scoped_refptr<PluginChannelBase> > channels; | |
92 for (PluginChannelMap::const_iterator iter = g_plugin_channels_.begin(); | |
93 iter != g_plugin_channels_.end(); | |
94 ++iter) { | |
95 channels.push_back(iter->second); | |
96 } | |
97 | |
98 for (size_t i = 0; i < channels.size(); ++i) | |
99 channels[i]->CleanUp(); | |
100 | |
101 // This will clean up channels added to the map for which subsequent | |
102 // AddRoute wasn't called | |
103 g_plugin_channels_.clear(); | |
104 } | |
105 | |
106 NPObjectBase* PluginChannelBase::GetNPObjectListenerForRoute(int route_id) { | |
107 ListenerMap::iterator iter = npobject_listeners_.find(route_id); | |
108 if (iter == npobject_listeners_.end()) { | |
109 DLOG(WARNING) << "Invalid route id passed in:" << route_id; | |
110 return NULL; | |
111 } | |
112 return iter->second; | |
113 } | |
114 | |
115 bool PluginChannelBase::Init(MessageLoop* ipc_message_loop, | |
116 bool create_pipe_now) { | |
117 channel_.reset(new IPC::SyncChannel( | |
118 channel_handle_, mode_, this, ipc_message_loop, create_pipe_now, | |
119 ChildProcess::current()->GetShutDownEvent())); | |
120 channel_valid_ = true; | |
121 return true; | |
122 } | |
123 | |
124 bool PluginChannelBase::Send(IPC::Message* message) { | |
125 if (!channel_.get()) { | |
126 delete message; | |
127 return false; | |
128 } | |
129 | |
130 if (send_unblocking_only_during_unblock_dispatch_ && | |
131 in_unblock_dispatch_ == 0 && | |
132 message->is_sync()) { | |
133 message->set_unblock(false); | |
134 } | |
135 | |
136 return channel_->Send(message); | |
137 } | |
138 | |
139 int PluginChannelBase::Count() { | |
140 return static_cast<int>(g_plugin_channels_.size()); | |
141 } | |
142 | |
143 bool PluginChannelBase::OnMessageReceived(const IPC::Message& message) { | |
144 // This call might cause us to be deleted, so keep an extra reference to | |
145 // ourself so that we can send the reply and decrement back in_dispatch_. | |
146 lazy_plugin_channel_stack_.Pointer()->push( | |
147 scoped_refptr<PluginChannelBase>(this)); | |
148 | |
149 bool handled; | |
150 if (message.should_unblock()) | |
151 in_unblock_dispatch_++; | |
152 if (message.routing_id() == MSG_ROUTING_CONTROL) { | |
153 handled = OnControlMessageReceived(message); | |
154 } else { | |
155 handled = router_.RouteMessage(message); | |
156 if (!handled && message.is_sync()) { | |
157 // The listener has gone away, so we must respond or else the caller will | |
158 // hang waiting for a reply. | |
159 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); | |
160 reply->set_reply_error(); | |
161 Send(reply); | |
162 } | |
163 } | |
164 if (message.should_unblock()) | |
165 in_unblock_dispatch_--; | |
166 | |
167 lazy_plugin_channel_stack_.Pointer()->pop(); | |
168 return handled; | |
169 } | |
170 | |
171 void PluginChannelBase::OnChannelConnected(int32 peer_pid) { | |
172 peer_pid_ = peer_pid; | |
173 } | |
174 | |
175 void PluginChannelBase::AddRoute(int route_id, | |
176 IPC::Channel::Listener* listener, | |
177 NPObjectBase* npobject) { | |
178 if (npobject) { | |
179 npobject_listeners_[route_id] = npobject; | |
180 } else { | |
181 plugin_count_++; | |
182 } | |
183 | |
184 router_.AddRoute(route_id, listener); | |
185 } | |
186 | |
187 void PluginChannelBase::RemoveRoute(int route_id) { | |
188 router_.RemoveRoute(route_id); | |
189 | |
190 ListenerMap::iterator iter = npobject_listeners_.find(route_id); | |
191 if (iter != npobject_listeners_.end()) { | |
192 // This was an NPObject proxy or stub, it's not involved in the refcounting. | |
193 | |
194 // If this RemoveRoute call from the NPObject is a result of us calling | |
195 // OnChannelError below, don't call erase() here because that'll corrupt | |
196 // the iterator below. | |
197 if (in_remove_route_) { | |
198 iter->second = NULL; | |
199 } else { | |
200 npobject_listeners_.erase(iter); | |
201 } | |
202 | |
203 return; | |
204 } | |
205 | |
206 plugin_count_--; | |
207 DCHECK(plugin_count_ >= 0); | |
208 | |
209 if (!plugin_count_) { | |
210 AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true); | |
211 for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin(); | |
212 npobj_iter != npobject_listeners_.end(); ++npobj_iter) { | |
213 if (npobj_iter->second) { | |
214 IPC::Channel::Listener* channel_listener = | |
215 npobj_iter->second->GetChannelListener(); | |
216 DCHECK(channel_listener != NULL); | |
217 channel_listener->OnChannelError(); | |
218 } | |
219 } | |
220 | |
221 for (PluginChannelMap::iterator iter = g_plugin_channels_.begin(); | |
222 iter != g_plugin_channels_.end(); ++iter) { | |
223 if (iter->second == this) { | |
224 g_plugin_channels_.erase(iter); | |
225 return; | |
226 } | |
227 } | |
228 | |
229 NOTREACHED(); | |
230 } | |
231 } | |
232 | |
233 bool PluginChannelBase::OnControlMessageReceived(const IPC::Message& msg) { | |
234 NOTREACHED() << | |
235 "should override in subclass if you care about control messages"; | |
236 return false; | |
237 } | |
238 | |
239 void PluginChannelBase::OnChannelError() { | |
240 channel_valid_ = false; | |
241 } | |
OLD | NEW |