1 // Copyright (c) 2012 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 "ppapi/proxy/host_dispatcher.h" 6 7 #include "base/debug/trace_event.h" 8 #include "base/logging.h" 9 #include "ppapi/c/private/ppb_proxy_private.h" 10 #include "ppapi/c/ppb_var.h" 11 #include "ppapi/proxy/host_var_serialization_rules.h" 12 #include "ppapi/proxy/interface_list.h" 13 #include "ppapi/proxy/ppapi_messages.h" 14 #include "ppapi/proxy/resource_creation_proxy.h" 15 #include "ppapi/shared_impl/ppapi_globals.h" 16 17 namespace ppapi { 18 namespace proxy { 19 20 namespace { 21 22 typedef std::map<PP_Instance, HostDispatcher*> InstanceToDispatcherMap; 23 InstanceToDispatcherMap* g_instance_to_dispatcher = NULL; 24 25 typedef std::map<PP_Module, HostDispatcher*> ModuleToDispatcherMap; 26 ModuleToDispatcherMap* g_module_to_dispatcher = NULL; 27 28 PP_Bool ReserveInstanceID(PP_Module module, PP_Instance instance) { 29 // Default to returning true (usable) failure. Otherwise, if there's some 30 // kind of communication error or the plugin just crashed, we'll get into an 31 // infinite loop generating new instnace IDs since we think they're all in 32 // use. 33 ModuleToDispatcherMap::const_iterator found = 34 g_module_to_dispatcher->find(module); 35 if (found == g_module_to_dispatcher->end()) { 36 NOTREACHED(); 37 return PP_TRUE; 38 } 39 40 bool usable = true; 41 if (!found->second->Send(new PpapiMsg_ReserveInstanceId(instance, &usable))) 42 return PP_TRUE; 43 return PP_FromBool(usable); 44 } 45 46 // Saves the state of the given bool and puts it back when it goes out of 47 // scope. 48 class BoolRestorer { 49 public: 50 BoolRestorer(bool* var) : var_(var), old_value_(*var) { 51 } 52 ~BoolRestorer() { 53 *var_ = old_value_; 54 } 55 private: 56 bool* var_; 57 bool old_value_; 58 }; 59 60 } // namespace 61 62 HostDispatcher::HostDispatcher(PP_Module module, 63 PP_GetInterface_Func local_get_interface, 64 const PpapiPermissions& permissions) 65 : Dispatcher(local_get_interface, permissions), 66 pp_module_(module), 67 ppb_proxy_(NULL), 68 allow_plugin_reentrancy_(false), 69 weak_ptr_factory_(this) { 70 if (!g_module_to_dispatcher) 71 g_module_to_dispatcher = new ModuleToDispatcherMap; 72 (*g_module_to_dispatcher)[pp_module_] = this; 73 74 SetSerializationRules(new HostVarSerializationRules); 75 76 ppb_proxy_ = reinterpret_cast<const PPB_Proxy_Private*>( 77 local_get_interface(PPB_PROXY_PRIVATE_INTERFACE)); 78 DCHECK(ppb_proxy_) << "The proxy interface should always be supported."; 79 80 ppb_proxy_->SetReserveInstanceIDCallback(pp_module_, &ReserveInstanceID); 81 } 82 83 HostDispatcher::~HostDispatcher() { 84 g_module_to_dispatcher->erase(pp_module_); 85 } 86 87 bool HostDispatcher::InitHostWithChannel( 88 Delegate* delegate, 89 base::ProcessId peer_pid, 90 const IPC::ChannelHandle& channel_handle, 91 bool is_client, 92 const ppapi::Preferences& preferences) { 93 if (!Dispatcher::InitWithChannel(delegate, peer_pid, channel_handle, 94 is_client)) 95 return false; 96 Send(new PpapiMsg_SetPreferences(preferences)); 97 return true; 98 } 99 100 // static 101 HostDispatcher* HostDispatcher::GetForInstance(PP_Instance instance) { 102 if (!g_instance_to_dispatcher) 103 return NULL; 104 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find( 105 instance); 106 if (found == g_instance_to_dispatcher->end()) 107 return NULL; 108 return found->second; 109 } 110 111 // static 112 void HostDispatcher::SetForInstance(PP_Instance instance, 113 HostDispatcher* dispatcher) { 114 if (!g_instance_to_dispatcher) 115 g_instance_to_dispatcher = new InstanceToDispatcherMap; 116 (*g_instance_to_dispatcher)[instance] = dispatcher; 117 } 118 119 // static 120 void HostDispatcher::RemoveForInstance(PP_Instance instance) { 121 if (!g_instance_to_dispatcher) 122 return; 123 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find( 124 instance); 125 if (found != g_instance_to_dispatcher->end()) 126 g_instance_to_dispatcher->erase(found); 127 } 128 129 bool HostDispatcher::IsPlugin() const { 130 return false; 131 } 132 133 bool HostDispatcher::Send(IPC::Message* msg) { 134 TRACE_EVENT2("ppapi proxy", "HostDispatcher::Send", 135 "Class", IPC_MESSAGE_ID_CLASS(msg->type()), 136 "Line", IPC_MESSAGE_ID_LINE(msg->type())); 137 138 // Normal sync messages are set to unblock, which would normally cause the 139 // plugin to be reentered to process them. We only want to do this when we 140 // know the plugin is in a state to accept reentrancy. Since the plugin side 141 // never clears this flag on messages it sends, we can't get deadlock, but we 142 // may still get reentrancy in the host as a result. 143 if (!allow_plugin_reentrancy_) 144 msg->set_unblock(false); 145 146 if (msg->is_sync()) { 147 // Don't allow sending sync messages during module shutdown. Seee the "else" 148 // block below for why. 149 CHECK(!PP_ToBool(ppb_proxy()->IsInModuleDestructor(pp_module()))); 150 151 // Prevent the dispatcher from going away during sync calls. Scenarios 152 // where this could happen include a Send for a sync message which while 153 // waiting for the reply, dispatches an incoming ExecuteScript call which 154 // destroys the plugin module and in turn the dispatcher. 155 ScopedModuleReference scoped_ref(this); 156 157 FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_, 158 BeginBlockOnSyncMessage()); 159 bool result = Dispatcher::Send(msg); 160 FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_, 161 EndBlockOnSyncMessage()); 162 163 return result; 164 } else { 165 // We don't want to have a scoped ref for async message cases since since 166 // async messages are sent during module desruction. In this case, the 167 // module will have a 0 refcount and addrefing and releasing it will 168 // reenter the destructor and it will crash. 169 return Dispatcher::Send(msg); 170 } 171 } 172 173 bool HostDispatcher::OnMessageReceived(const IPC::Message& msg) { 174 // Prevent the dispatcher from going away during a message handler. This must 175 // be at the outermost scope so it's released last. 176 ScopedModuleReference death_grip(this); 177 178 TRACE_EVENT2("ppapi proxy", "HostDispatcher::OnMessageReceived", 179 "Class", IPC_MESSAGE_ID_CLASS(msg.type()), 180 "Line", IPC_MESSAGE_ID_LINE(msg.type())); 181 182 // We only want to allow reentrancy when the most recent message from the 183 // plugin was a scripting message. We save the old state of the flag on the 184 // stack in case we're (we are the host) being reentered ourselves. The flag 185 // is set to false here for all messages, and then the scripting API will 186 // explicitly set it to true during processing of those messages that can be 187 // reentered. 188 BoolRestorer restorer(&allow_plugin_reentrancy_); 189 allow_plugin_reentrancy_ = false; 190 191 for (size_t i = 0; i < filters_.size(); i++) { 192 if (filters_[i]->OnMessageReceived(msg)) 193 return true; 194 } 195 196 bool handled = true; 197 IPC_BEGIN_MESSAGE_MAP(HostDispatcher, msg) 198 IPC_MESSAGE_HANDLER(PpapiHostMsg_LogWithSource, OnHostMsgLogWithSource) 199 IPC_MESSAGE_UNHANDLED(handled = false) 200 IPC_END_MESSAGE_MAP() 201 202 if (handled) 203 return true; 204 return Dispatcher::OnMessageReceived(msg); 205 206 // Note: |this| may be deleted once the death_grip goes out of scope! 207 } 208 209 void HostDispatcher::OnChannelError() { 210 Dispatcher::OnChannelError(); // Stop using the channel. 211 212 // Tell the host about the crash so it can clean up and display notification. 213 ppb_proxy_->PluginCrashed(pp_module()); 214 } 215 216 const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) { 217 const void* proxied_interface = 218 InterfaceList::GetInstance()->GetInterfaceForPPP(iface_name); 219 if (!proxied_interface) 220 return NULL; // Don't have a proxy for this interface, don't query further. 221 222 PluginSupportedMap::iterator iter(plugin_supported_.find(iface_name)); 223 if (iter == plugin_supported_.end()) { 224 // Need to query. Cache the result so we only do this once. 225 bool supported = false; 226 227 bool previous_reentrancy_value = allow_plugin_reentrancy_; 228 allow_plugin_reentrancy_ = true; 229 Send(new PpapiMsg_SupportsInterface(iface_name, &supported)); 230 allow_plugin_reentrancy_ = previous_reentrancy_value; 231 232 std::pair<PluginSupportedMap::iterator, bool> iter_success_pair; 233 iter_success_pair = plugin_supported_.insert( 234 PluginSupportedMap::value_type(iface_name, supported)); 235 iter = iter_success_pair.first; 236 } 237 if (iter->second) 238 return proxied_interface; 239 return NULL; 240 } 241 242 base::Closure HostDispatcher::AddSyncMessageStatusObserver( 243 SyncMessageStatusObserver* obs) { 244 sync_status_observer_list_.AddObserver(obs); 245 return base::Bind(&HostDispatcher::RemoveSyncMessageStatusObserver, 246 weak_ptr_factory_.GetWeakPtr(), 247 obs); 248 } 249 250 void HostDispatcher::RemoveSyncMessageStatusObserver( 251 SyncMessageStatusObserver* obs) { 252 sync_status_observer_list_.RemoveObserver(obs); 253 } 254 255 void HostDispatcher::AddFilter(IPC::Listener* listener) { 256 filters_.push_back(listener); 257 } 258 259 void HostDispatcher::OnInvalidMessageReceived() { 260 // TODO(brettw) bug 95345 kill the plugin when an invalid message is 261 // received. 262 } 263 264 void HostDispatcher::OnHostMsgLogWithSource(PP_Instance instance, 265 int int_log_level, 266 const std::string& source, 267 const std::string& value) { 268 PP_LogLevel level = static_cast<PP_LogLevel>(int_log_level); 269 if (instance) { 270 PpapiGlobals::Get()->LogWithSource(instance, level, source, value); 271 } else { 272 PpapiGlobals::Get()->BroadcastLogWithSource(pp_module_, level, 273 source, value); 274 } 275 } 276 277 // ScopedModuleReference ------------------------------------------------------- 278 279 ScopedModuleReference::ScopedModuleReference(Dispatcher* dispatcher) 280 : dispatcher_(NULL) { 281 if (!dispatcher->IsPlugin()) { 282 dispatcher_ = static_cast<HostDispatcher*>(dispatcher); 283 dispatcher_->ppb_proxy()->AddRefModule(dispatcher_->pp_module()); 284 } 285 } 286 287 ScopedModuleReference::~ScopedModuleReference() { 288 if (dispatcher_) 289 dispatcher_->ppb_proxy()->ReleaseModule(dispatcher_->pp_module()); 290 } 291 292 } // namespace proxy 293 } // namespace ppapi 294