1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "WebProcessConnection.h" 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30 31#include "ActivityAssertion.h" 32#include "ArgumentCoders.h" 33#include "ConnectionStack.h" 34#include "NPObjectMessageReceiverMessages.h" 35#include "NPRemoteObjectMap.h" 36#include "PluginControllerProxy.h" 37#include "PluginCreationParameters.h" 38#include "PluginProcess.h" 39#include "PluginProcessConnectionMessages.h" 40#include "PluginProxyMessages.h" 41#include "WebProcessConnectionMessages.h" 42#include <WebCore/AudioHardwareListener.h> 43#include <unistd.h> 44#include <wtf/RunLoop.h> 45 46using namespace WebCore; 47 48namespace WebKit { 49 50PassRefPtr<WebProcessConnection> WebProcessConnection::create(IPC::Connection::Identifier connectionIdentifier) 51{ 52 return adoptRef(new WebProcessConnection(connectionIdentifier)); 53} 54 55WebProcessConnection::~WebProcessConnection() 56{ 57 ASSERT(m_pluginControllers.isEmpty()); 58 ASSERT(!m_npRemoteObjectMap); 59 ASSERT(!m_connection); 60} 61 62WebProcessConnection::WebProcessConnection(IPC::Connection::Identifier connectionIdentifier) 63{ 64 m_connection = IPC::Connection::createServerConnection(connectionIdentifier, this, RunLoop::main()); 65 m_npRemoteObjectMap = NPRemoteObjectMap::create(m_connection.get()); 66 67 m_connection->setOnlySendMessagesAsDispatchWhenWaitingForSyncReplyWhenProcessingSuchAMessage(true); 68 m_connection->open(); 69} 70 71void WebProcessConnection::addPluginControllerProxy(std::unique_ptr<PluginControllerProxy> pluginController) 72{ 73 uint64_t pluginInstanceID = pluginController->pluginInstanceID(); 74 75 ASSERT(!m_pluginControllers.contains(pluginInstanceID)); 76 m_pluginControllers.set(pluginInstanceID, WTF::move(pluginController)); 77} 78 79void WebProcessConnection::destroyPluginControllerProxy(PluginControllerProxy* pluginController) 80{ 81 // This may end up calling removePluginControllerProxy which ends up deleting 82 // the WebProcessConnection object if this was the last object. 83 pluginController->destroy(); 84} 85 86void WebProcessConnection::removePluginControllerProxy(PluginControllerProxy* pluginController, Plugin* plugin) 87{ 88 unsigned pluginInstanceID = pluginController->pluginInstanceID(); 89 { 90 ASSERT(m_pluginControllers.contains(pluginInstanceID)); 91 92 std::unique_ptr<PluginControllerProxy> pluginControllerUniquePtr = m_pluginControllers.take(pluginInstanceID); 93 ASSERT(pluginControllerUniquePtr.get() == pluginController); 94 } 95 96 pluginDidBecomeHidden(pluginInstanceID); 97 98 // Invalidate all objects related to this plug-in. 99 if (plugin) 100 m_npRemoteObjectMap->pluginDestroyed(plugin); 101 102 if (!m_pluginControllers.isEmpty()) 103 return; 104 105 m_npRemoteObjectMap = nullptr; 106 107 // The last plug-in went away, close this connection. 108 m_connection->invalidate(); 109 m_connection = nullptr; 110 111 // This will cause us to be deleted. 112 PluginProcess::shared().removeWebProcessConnection(this); 113} 114 115void WebProcessConnection::setGlobalException(const String& exceptionString) 116{ 117 IPC::Connection* connection = ConnectionStack::shared().current(); 118 if (!connection) 119 return; 120 121 connection->sendSync(Messages::PluginProcessConnection::SetException(exceptionString), Messages::PluginProcessConnection::SetException::Reply(), 0); 122} 123 124void WebProcessConnection::didReceiveMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder) 125{ 126 ConnectionStack::CurrentConnectionPusher currentConnection(ConnectionStack::shared(), connection); 127 128 if (decoder.messageReceiverName() == Messages::WebProcessConnection::messageReceiverName()) { 129 didReceiveWebProcessConnectionMessage(connection, decoder); 130 return; 131 } 132 133 if (!decoder.destinationID()) { 134 ASSERT_NOT_REACHED(); 135 return; 136 } 137 138 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(decoder.destinationID()); 139 if (!pluginControllerProxy) 140 return; 141 142 PluginController::PluginDestructionProtector protector(pluginControllerProxy->asPluginController()); 143 pluginControllerProxy->didReceivePluginControllerProxyMessage(connection, decoder); 144} 145 146void WebProcessConnection::didReceiveSyncMessage(IPC::Connection* connection, IPC::MessageDecoder& decoder, std::unique_ptr<IPC::MessageEncoder>& replyEncoder) 147{ 148 ConnectionStack::CurrentConnectionPusher currentConnection(ConnectionStack::shared(), connection); 149 150 uint64_t destinationID = decoder.destinationID(); 151 152 if (!destinationID) { 153 didReceiveSyncWebProcessConnectionMessage(connection, decoder, replyEncoder); 154 return; 155 } 156 157 if (decoder.messageReceiverName() == Messages::NPObjectMessageReceiver::messageReceiverName()) { 158 m_npRemoteObjectMap->didReceiveSyncMessage(connection, decoder, replyEncoder); 159 return; 160 } 161 162 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(decoder.destinationID()); 163 if (!pluginControllerProxy) 164 return; 165 166 PluginController::PluginDestructionProtector protector(pluginControllerProxy->asPluginController()); 167 pluginControllerProxy->didReceiveSyncPluginControllerProxyMessage(connection, decoder, replyEncoder); 168} 169 170void WebProcessConnection::didClose(IPC::Connection*) 171{ 172 // The web process crashed. Destroy all the plug-in controllers. Destroying the last plug-in controller 173 // will cause the web process connection itself to be destroyed. 174 Vector<PluginControllerProxy*> pluginControllers; 175 for (auto it = m_pluginControllers.values().begin(), end = m_pluginControllers.values().end(); it != end; ++it) 176 pluginControllers.append(it->get()); 177 178 for (size_t i = 0; i < pluginControllers.size(); ++i) 179 destroyPluginControllerProxy(pluginControllers[i]); 180} 181 182void WebProcessConnection::destroyPlugin(uint64_t pluginInstanceID, bool asynchronousCreationIncomplete, PassRefPtr<Messages::WebProcessConnection::DestroyPlugin::DelayedReply> reply) 183{ 184 // We return immediately from this synchronous IPC. We want to make sure the plugin destruction is just about to start so audio playback 185 // will finish soon after returning. However we don't want to wait for destruction to complete fully as that may take a while. 186 reply->send(); 187 188 // Ensure we don't clamp any timers during destruction 189 ActivityAssertion activityAssertion(PluginProcess::shared().connectionActivity()); 190 191 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(pluginInstanceID); 192 193 // If there is no PluginControllerProxy then this plug-in doesn't exist yet and we probably have nothing to do. 194 if (!pluginControllerProxy) { 195 // If the plugin we're supposed to destroy was requested asynchronously and doesn't exist yet, 196 // we need to flag the instance ID so it is not created later. 197 if (asynchronousCreationIncomplete) 198 m_asynchronousInstanceIDsToIgnore.add(pluginInstanceID); 199 200 return; 201 } 202 203 destroyPluginControllerProxy(pluginControllerProxy); 204} 205 206void WebProcessConnection::didReceiveInvalidMessage(IPC::Connection*, IPC::StringReference, IPC::StringReference) 207{ 208 // FIXME: Implement. 209} 210 211void WebProcessConnection::createPluginInternal(const PluginCreationParameters& creationParameters, bool& result, bool& wantsWheelEvents, uint32_t& remoteLayerClientID) 212{ 213 auto pluginControllerProxy = std::make_unique<PluginControllerProxy>(this, creationParameters); 214 215 PluginControllerProxy* pluginControllerProxyPtr = pluginControllerProxy.get(); 216 217 // Make sure to add the proxy to the map before initializing it, since the plug-in might call out to the web process from 218 // its NPP_New function. This will hand over ownership of the proxy to the web process connection. 219 addPluginControllerProxy(WTF::move(pluginControllerProxy)); 220 221 // Now try to initialize the plug-in. 222 result = pluginControllerProxyPtr->initialize(creationParameters); 223 224 if (!result) 225 return; 226 227 wantsWheelEvents = pluginControllerProxyPtr->wantsWheelEvents(); 228#if PLATFORM(COCOA) 229 remoteLayerClientID = pluginControllerProxyPtr->remoteLayerClientID(); 230#else 231 UNUSED_PARAM(remoteLayerClientID); 232#endif 233} 234 235void WebProcessConnection::createPlugin(const PluginCreationParameters& creationParameters, PassRefPtr<Messages::WebProcessConnection::CreatePlugin::DelayedReply> reply) 236{ 237 // Ensure we don't clamp any timers during initialization 238 ActivityAssertion activityAssertion(PluginProcess::shared().connectionActivity()); 239 240 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(creationParameters.pluginInstanceID); 241 242 // The controller proxy for the plug-in we're being asked to create synchronously might already exist if it was requested asynchronously before. 243 if (pluginControllerProxy) { 244 // It might still be in the middle of initialization in which case we have to let that initialization complete and respond to this message later. 245 if (pluginControllerProxy->isInitializing()) { 246 pluginControllerProxy->setInitializationReply(reply); 247 return; 248 } 249 250 // If its initialization is complete then we need to respond to this message with the correct information about its creation. 251#if PLATFORM(COCOA) 252 reply->send(true, pluginControllerProxy->wantsWheelEvents(), pluginControllerProxy->remoteLayerClientID()); 253#else 254 reply->send(true, pluginControllerProxy->wantsWheelEvents(), 0); 255#endif 256 return; 257 } 258 259 // The plugin we're supposed to create might have been requested asynchronously before. 260 // In that case we need to create it synchronously now but flag the instance ID so we don't recreate it asynchronously later. 261 if (creationParameters.asynchronousCreationIncomplete) 262 m_asynchronousInstanceIDsToIgnore.add(creationParameters.pluginInstanceID); 263 264 bool result = false; 265 bool wantsWheelEvents = false; 266 uint32_t remoteLayerClientID = 0; 267 createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID); 268 269 reply->send(result, wantsWheelEvents, remoteLayerClientID); 270} 271 272void WebProcessConnection::createPluginAsynchronously(const PluginCreationParameters& creationParameters) 273{ 274 // In the time since this plugin was requested asynchronously we might have created it synchronously or destroyed it. 275 // In either of those cases we need to ignore this creation request. 276 if (m_asynchronousInstanceIDsToIgnore.contains(creationParameters.pluginInstanceID)) { 277 m_asynchronousInstanceIDsToIgnore.remove(creationParameters.pluginInstanceID); 278 return; 279 } 280 281 // This version of CreatePlugin is only used by plug-ins that are known to behave when started asynchronously. 282 bool result = false; 283 bool wantsWheelEvents = false; 284 uint32_t remoteLayerClientID = 0; 285 286 if (creationParameters.artificialPluginInitializationDelayEnabled) { 287 unsigned artificialPluginInitializationDelay = 5; 288 sleep(artificialPluginInitializationDelay); 289 } 290 291 // Since plug-in creation can often message to the WebProcess synchronously (with NPP_Evaluate for example) 292 // we need to make sure that the web process will handle the plug-in process's synchronous messages, 293 // even if the web process is waiting on a synchronous reply itself. 294 // Normally the plug-in process doesn't give its synchronous messages the special flag to allow for that. 295 // We can force it to do so by incrementing the "DispatchMessageMarkedDispatchWhenWaitingForSyncReply" count. 296 m_connection->incrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount(); 297 298 // The call to createPluginInternal can potentially cause the plug-in to be destroyed and 299 // thus free the WebProcessConnection object. Protect it. 300 Ref<WebProcessConnection> protect(*this); 301 createPluginInternal(creationParameters, result, wantsWheelEvents, remoteLayerClientID); 302 303 if (!m_connection) { 304 // createPluginInternal caused the connection to go away. 305 return; 306 } 307 308 m_connection->decrementDispatchMessageMarkedDispatchWhenWaitingForSyncReplyCount(); 309 310 // If someone asked for this plug-in synchronously while it was in the middle of being created then we need perform the 311 // synchronous reply instead of sending the asynchronous reply. 312 PluginControllerProxy* pluginControllerProxy = m_pluginControllers.get(creationParameters.pluginInstanceID); 313 ASSERT(pluginControllerProxy); 314 if (RefPtr<Messages::WebProcessConnection::CreatePlugin::DelayedReply> delayedSyncReply = pluginControllerProxy->takeInitializationReply()) { 315 delayedSyncReply->send(result, wantsWheelEvents, remoteLayerClientID); 316 return; 317 } 318 319 // Otherwise, send the asynchronous results now. 320 if (!result) { 321 m_connection->sendSync(Messages::PluginProxy::DidFailToCreatePlugin(), Messages::PluginProxy::DidFailToCreatePlugin::Reply(), creationParameters.pluginInstanceID); 322 return; 323 } 324 325 m_connection->sendSync(Messages::PluginProxy::DidCreatePlugin(wantsWheelEvents, remoteLayerClientID), Messages::PluginProxy::DidCreatePlugin::Reply(), creationParameters.pluginInstanceID); 326} 327 328void WebProcessConnection::pluginDidBecomeVisible(unsigned pluginInstanceID) 329{ 330 bool oldState = m_visiblePluginInstanceIDs.isEmpty(); 331 332 m_visiblePluginInstanceIDs.add(pluginInstanceID); 333 334 ASSERT(m_visiblePluginInstanceIDs.size() <= m_pluginControllers.size()); 335 336 if (oldState != m_visiblePluginInstanceIDs.isEmpty()) 337 PluginProcess::shared().pluginsForWebProcessDidBecomeVisible(); 338} 339 340void WebProcessConnection::pluginDidBecomeHidden(unsigned pluginInstanceID) 341{ 342 bool oldState = m_visiblePluginInstanceIDs.isEmpty(); 343 344 m_visiblePluginInstanceIDs.remove(pluginInstanceID); 345 346 if (oldState != m_visiblePluginInstanceIDs.isEmpty()) 347 PluginProcess::shared().pluginsForWebProcessDidBecomeHidden(); 348} 349 350void WebProcessConnection::audioHardwareDidBecomeActive() 351{ 352 m_connection->send(Messages::PluginProcessConnection::AudioHardwareDidBecomeActive(), 0); 353} 354 355void WebProcessConnection::audioHardwareDidBecomeInactive() 356{ 357 m_connection->send(Messages::PluginProcessConnection::AudioHardwareDidBecomeInactive(), 0); 358} 359 360} // namespace WebKit 361 362#endif // ENABLE(NETSCAPE_PLUGIN_API) 363