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 "PluginControllerProxy.h" 28 29#if ENABLE(PLUGIN_PROCESS) 30 31#include "DataReference.h" 32#include "NPObjectProxy.h" 33#include "NPRemoteObjectMap.h" 34#include "NPRuntimeUtilities.h" 35#include "NPVariantData.h" 36#include "NetscapePlugin.h" 37#include "PluginProcess.h" 38#include "PluginProxyMessages.h" 39#include "ShareableBitmap.h" 40#include "WebCoreArgumentCoders.h" 41#include "WebProcessConnection.h" 42#include <WebCore/GraphicsContext.h> 43#include <wtf/text/WTFString.h> 44 45using namespace WebCore; 46 47namespace WebKit { 48 49PassOwnPtr<PluginControllerProxy> PluginControllerProxy::create(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled) 50{ 51 return adoptPtr(new PluginControllerProxy(connection, pluginInstanceID, userAgent, isPrivateBrowsingEnabled, isAcceleratedCompositingEnabled)); 52} 53 54PluginControllerProxy::PluginControllerProxy(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled) 55 : m_connection(connection) 56 , m_pluginInstanceID(pluginInstanceID) 57 , m_userAgent(userAgent) 58 , m_isPrivateBrowsingEnabled(isPrivateBrowsingEnabled) 59 , m_isAcceleratedCompositingEnabled(isAcceleratedCompositingEnabled) 60 , m_paintTimer(RunLoop::main(), this, &PluginControllerProxy::paint) 61 , m_pluginDestructionProtectCount(0) 62 , m_pluginDestroyTimer(RunLoop::main(), this, &PluginControllerProxy::destroy) 63 , m_waitingForDidUpdate(false) 64 , m_pluginCanceledManualStreamLoad(false) 65#if PLATFORM(MAC) 66 , m_isComplexTextInputEnabled(false) 67#endif 68 , m_windowNPObject(0) 69 , m_pluginElementNPObject(0) 70{ 71} 72 73PluginControllerProxy::~PluginControllerProxy() 74{ 75 ASSERT(!m_plugin); 76 77 if (m_windowNPObject) 78 releaseNPObject(m_windowNPObject); 79 80 if (m_pluginElementNPObject) 81 releaseNPObject(m_pluginElementNPObject); 82} 83 84bool PluginControllerProxy::initialize(const Plugin::Parameters& parameters) 85{ 86 ASSERT(!m_plugin); 87 88 m_plugin = NetscapePlugin::create(PluginProcess::shared().netscapePluginModule()); 89 if (!m_plugin) { 90 // This will delete the plug-in controller proxy object. 91 m_connection->removePluginControllerProxy(this, 0); 92 return false; 93 } 94 95 if (!m_plugin->initialize(this, parameters)) { 96 // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only 97 // used as an identifier so it's OK to just get a weak reference. 98 Plugin* plugin = m_plugin.get(); 99 100 m_plugin = 0; 101 102 // This will delete the plug-in controller proxy object. 103 m_connection->removePluginControllerProxy(this, plugin); 104 return false; 105 } 106 107 platformInitialize(); 108 109 return true; 110} 111 112void PluginControllerProxy::destroy() 113{ 114 ASSERT(m_plugin); 115 116 if (m_pluginDestructionProtectCount) { 117 // We have plug-in code on the stack so we can't destroy it right now. 118 // Destroy it later. 119 m_pluginDestroyTimer.startOneShot(0); 120 return; 121 } 122 123 // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only 124 // used as an identifier so it's OK to just get a weak reference. 125 Plugin* plugin = m_plugin.get(); 126 127 m_plugin->destroy(); 128 m_plugin = 0; 129 130 platformDestroy(); 131 132 // This will delete the plug-in controller proxy object. 133 m_connection->removePluginControllerProxy(this, plugin); 134} 135 136void PluginControllerProxy::paint() 137{ 138 ASSERT(!m_dirtyRect.isEmpty()); 139 m_paintTimer.stop(); 140 141 if (!m_backingStore) 142 return; 143 144 IntRect dirtyRect = m_dirtyRect; 145 m_dirtyRect = IntRect(); 146 147 ASSERT(m_plugin); 148 149 // Create a graphics context. 150 OwnPtr<GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext(); 151 152 graphicsContext->translate(-m_frameRect.x(), -m_frameRect.y()); 153 154 if (m_plugin->isTransparent()) 155 graphicsContext->clearRect(dirtyRect); 156 157 m_plugin->paint(graphicsContext.get(), dirtyRect); 158 159 m_connection->connection()->send(Messages::PluginProxy::Update(dirtyRect), m_pluginInstanceID); 160} 161 162void PluginControllerProxy::startPaintTimer() 163{ 164 // Check if we should start the timer. 165 166 if (m_dirtyRect.isEmpty()) 167 return; 168 169 // FIXME: Check clip rect. 170 171 if (m_paintTimer.isActive()) 172 return; 173 174 if (m_waitingForDidUpdate) 175 return; 176 177 // Start the timer. 178 m_paintTimer.startOneShot(0); 179 180 m_waitingForDidUpdate = true; 181} 182 183void PluginControllerProxy::invalidate(const IntRect& rect) 184{ 185 // Convert the dirty rect to window coordinates. 186 IntRect dirtyRect = rect; 187 dirtyRect.move(m_frameRect.x(), m_frameRect.y()); 188 189 // Make sure that the dirty rect is not greater than the plug-in itself. 190 dirtyRect.intersect(m_frameRect); 191 192 m_dirtyRect.unite(dirtyRect); 193 194 startPaintTimer(); 195} 196 197String PluginControllerProxy::userAgent() 198{ 199 return m_userAgent; 200} 201 202void PluginControllerProxy::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups) 203{ 204 m_connection->connection()->send(Messages::PluginProxy::LoadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups), m_pluginInstanceID); 205} 206 207void PluginControllerProxy::cancelStreamLoad(uint64_t streamID) 208{ 209 m_connection->connection()->send(Messages::PluginProxy::CancelStreamLoad(streamID), m_pluginInstanceID); 210} 211 212void PluginControllerProxy::cancelManualStreamLoad() 213{ 214 m_pluginCanceledManualStreamLoad = true; 215 216 m_connection->connection()->send(Messages::PluginProxy::CancelManualStreamLoad(), m_pluginInstanceID); 217} 218 219NPObject* PluginControllerProxy::windowScriptNPObject() 220{ 221 if (!m_windowNPObject) { 222 uint64_t windowScriptNPObjectID = 0; 223 224 if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetWindowScriptNPObject(), Messages::PluginProxy::GetWindowScriptNPObject::Reply(windowScriptNPObjectID), m_pluginInstanceID)) 225 return 0; 226 227 if (!windowScriptNPObjectID) 228 return 0; 229 230 m_windowNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(windowScriptNPObjectID, m_plugin.get()); 231 ASSERT(m_windowNPObject); 232 } 233 234 retainNPObject(m_windowNPObject); 235 return m_windowNPObject; 236} 237 238NPObject* PluginControllerProxy::pluginElementNPObject() 239{ 240 if (!m_pluginElementNPObject) { 241 uint64_t pluginElementNPObjectID = 0; 242 243 if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetPluginElementNPObject(), Messages::PluginProxy::GetPluginElementNPObject::Reply(pluginElementNPObjectID), m_pluginInstanceID)) 244 return 0; 245 246 if (!pluginElementNPObjectID) 247 return 0; 248 249 m_pluginElementNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(pluginElementNPObjectID, m_plugin.get()); 250 ASSERT(m_pluginElementNPObject); 251 } 252 253 retainNPObject(m_pluginElementNPObject); 254 return m_pluginElementNPObject; 255} 256 257bool PluginControllerProxy::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) 258{ 259 PluginDestructionProtector protector(this); 260 261 NPVariant npObjectAsNPVariant; 262 OBJECT_TO_NPVARIANT(npObject, npObjectAsNPVariant); 263 264 // Send the NPObject over as an NPVariantData. 265 NPVariantData npObjectAsNPVariantData = m_connection->npRemoteObjectMap()->npVariantToNPVariantData(npObjectAsNPVariant, m_plugin.get()); 266 267 bool returnValue = false; 268 NPVariantData resultData; 269 270 if (!m_connection->connection()->sendSync(Messages::PluginProxy::Evaluate(npObjectAsNPVariantData, scriptString, allowPopups), Messages::PluginProxy::Evaluate::Reply(returnValue, resultData), m_pluginInstanceID)) 271 return false; 272 273 if (!returnValue) 274 return false; 275 276 *result = m_connection->npRemoteObjectMap()->npVariantDataToNPVariant(resultData, m_plugin.get()); 277 return true; 278} 279 280void PluginControllerProxy::setStatusbarText(const String& statusbarText) 281{ 282 m_connection->connection()->send(Messages::PluginProxy::SetStatusbarText(statusbarText), m_pluginInstanceID); 283} 284 285bool PluginControllerProxy::isAcceleratedCompositingEnabled() 286{ 287 return m_isAcceleratedCompositingEnabled; 288} 289 290void PluginControllerProxy::pluginProcessCrashed() 291{ 292 // This should never be called from here. 293 ASSERT_NOT_REACHED(); 294} 295 296#if PLATFORM(MAC) 297void PluginControllerProxy::setComplexTextInputEnabled(bool complexTextInputEnabled) 298{ 299 if (m_isComplexTextInputEnabled == complexTextInputEnabled) 300 return; 301 302 m_isComplexTextInputEnabled = complexTextInputEnabled; 303 304 m_connection->connection()->send(Messages::PluginProxy::SetComplexTextInputEnabled(complexTextInputEnabled), m_pluginInstanceID); 305} 306 307mach_port_t PluginControllerProxy::compositingRenderServerPort() 308{ 309 return PluginProcess::shared().compositingRenderServerPort(); 310} 311#endif 312 313String PluginControllerProxy::proxiesForURL(const String& urlString) 314{ 315 String proxyString; 316 317 if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(proxyString), m_pluginInstanceID)) 318 return String(); 319 320 return proxyString; 321} 322 323String PluginControllerProxy::cookiesForURL(const String& urlString) 324{ 325 String cookieString; 326 327 if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(cookieString), m_pluginInstanceID)) 328 return String(); 329 330 return cookieString; 331} 332 333void PluginControllerProxy::setCookiesForURL(const String& urlString, const String& cookieString) 334{ 335 m_connection->connection()->send(Messages::PluginProxy::SetCookiesForURL(urlString, cookieString), m_pluginInstanceID); 336} 337 338bool PluginControllerProxy::isPrivateBrowsingEnabled() 339{ 340 return m_isPrivateBrowsingEnabled; 341} 342 343void PluginControllerProxy::protectPluginFromDestruction() 344{ 345 m_pluginDestructionProtectCount++; 346} 347 348void PluginControllerProxy::unprotectPluginFromDestruction() 349{ 350 ASSERT(m_pluginDestructionProtectCount); 351 352 m_pluginDestructionProtectCount--; 353} 354 355void PluginControllerProxy::frameDidFinishLoading(uint64_t requestID) 356{ 357 m_plugin->frameDidFinishLoading(requestID); 358} 359 360void PluginControllerProxy::frameDidFail(uint64_t requestID, bool wasCancelled) 361{ 362 m_plugin->frameDidFail(requestID, wasCancelled); 363} 364 365void PluginControllerProxy::geometryDidChange(const IntRect& frameRect, const IntRect& clipRect, const ShareableBitmap::Handle& backingStoreHandle) 366{ 367 m_frameRect = frameRect; 368 m_clipRect = clipRect; 369 370 ASSERT(m_plugin); 371 372 platformGeometryDidChange(); 373 374 if (!backingStoreHandle.isNull()) { 375 // Create a new backing store. 376 m_backingStore = ShareableBitmap::create(backingStoreHandle); 377 } 378 379 m_plugin->geometryDidChange(frameRect, clipRect); 380} 381 382void PluginControllerProxy::didEvaluateJavaScript(uint64_t requestID, const String& requestURLString, const String& result) 383{ 384 m_plugin->didEvaluateJavaScript(requestID, requestURLString, result); 385} 386 387void PluginControllerProxy::streamDidReceiveResponse(uint64_t streamID, const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) 388{ 389 m_plugin->streamDidReceiveResponse(streamID, KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers); 390} 391 392void PluginControllerProxy::streamDidReceiveData(uint64_t streamID, const CoreIPC::DataReference& data) 393{ 394 m_plugin->streamDidReceiveData(streamID, reinterpret_cast<const char*>(data.data()), data.size()); 395} 396 397void PluginControllerProxy::streamDidFinishLoading(uint64_t streamID) 398{ 399 m_plugin->streamDidFinishLoading(streamID); 400} 401 402void PluginControllerProxy::streamDidFail(uint64_t streamID, bool wasCancelled) 403{ 404 m_plugin->streamDidFail(streamID, wasCancelled); 405} 406 407void PluginControllerProxy::manualStreamDidReceiveResponse(const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) 408{ 409 if (m_pluginCanceledManualStreamLoad) 410 return; 411 412 m_plugin->manualStreamDidReceiveResponse(KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers); 413} 414 415void PluginControllerProxy::manualStreamDidReceiveData(const CoreIPC::DataReference& data) 416{ 417 if (m_pluginCanceledManualStreamLoad) 418 return; 419 420 m_plugin->manualStreamDidReceiveData(reinterpret_cast<const char*>(data.data()), data.size()); 421} 422 423void PluginControllerProxy::manualStreamDidFinishLoading() 424{ 425 if (m_pluginCanceledManualStreamLoad) 426 return; 427 428 m_plugin->manualStreamDidFinishLoading(); 429} 430 431void PluginControllerProxy::manualStreamDidFail(bool wasCancelled) 432{ 433 if (m_pluginCanceledManualStreamLoad) 434 return; 435 436 m_plugin->manualStreamDidFail(wasCancelled); 437} 438 439void PluginControllerProxy::handleMouseEvent(const WebMouseEvent& mouseEvent, PassRefPtr<Messages::PluginControllerProxy::HandleMouseEvent::DelayedReply> reply) 440{ 441 // Always let the web process think that we've handled this mouse event, even before passing it along to the plug-in. 442 // This is a workaround for 443 // <rdar://problem/9299901> UI process thinks the page is unresponsive when a plug-in is showing a context menu. 444 // The web process sends a synchronous HandleMouseEvent message and the plug-in process spawns a nested 445 // run loop when showing the context menu, so eventually the unresponsiveness timer kicks in in the UI process. 446 // FIXME: We should come up with a better way to do this. 447 reply->send(true); 448 449 m_plugin->handleMouseEvent(mouseEvent); 450} 451 452void PluginControllerProxy::handleWheelEvent(const WebWheelEvent& wheelEvent, bool& handled) 453{ 454 handled = m_plugin->handleWheelEvent(wheelEvent); 455} 456 457void PluginControllerProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent, bool& handled) 458{ 459 handled = m_plugin->handleMouseEnterEvent(mouseEnterEvent); 460} 461 462void PluginControllerProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent, bool& handled) 463{ 464 handled = m_plugin->handleMouseLeaveEvent(mouseLeaveEvent); 465} 466 467void PluginControllerProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent, bool& handled) 468{ 469 handled = m_plugin->handleKeyboardEvent(keyboardEvent); 470} 471 472void PluginControllerProxy::paintEntirePlugin() 473{ 474 if (m_frameRect.isEmpty()) 475 return; 476 477 m_dirtyRect = m_frameRect; 478 paint(); 479} 480 481void PluginControllerProxy::snapshot(ShareableBitmap::Handle& backingStoreHandle) 482{ 483 ASSERT(m_plugin); 484 RefPtr<ShareableBitmap> bitmap = m_plugin->snapshot(); 485 if (!bitmap) 486 return; 487 488 bitmap->createHandle(backingStoreHandle); 489} 490 491void PluginControllerProxy::setFocus(bool hasFocus) 492{ 493 m_plugin->setFocus(hasFocus); 494} 495 496void PluginControllerProxy::didUpdate() 497{ 498 m_waitingForDidUpdate = false; 499 startPaintTimer(); 500} 501 502void PluginControllerProxy::getPluginScriptableNPObject(uint64_t& pluginScriptableNPObjectID) 503{ 504 NPObject* pluginScriptableNPObject = m_plugin->pluginScriptableNPObject(); 505 if (!pluginScriptableNPObject) { 506 pluginScriptableNPObjectID = 0; 507 return; 508 } 509 510 pluginScriptableNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(pluginScriptableNPObject, m_plugin.get()); 511 releaseNPObject(pluginScriptableNPObject); 512} 513 514void PluginControllerProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled) 515{ 516 m_plugin->privateBrowsingStateChanged(isPrivateBrowsingEnabled); 517} 518 519} // namespace WebKit 520 521#endif // ENABLE(PLUGIN_PROCESS) 522