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 45 using namespace WebCore; 46 47 namespace WebKit { 48 49 PassOwnPtr<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 54 PluginControllerProxy::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 73 PluginControllerProxy::~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 84 bool 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 112 void 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 136 void 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 162 void 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 183 void 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 197 String PluginControllerProxy::userAgent() 198 { 199 return m_userAgent; 200 } 201 202 void 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 207 void PluginControllerProxy::cancelStreamLoad(uint64_t streamID) 208 { 209 m_connection->connection()->send(Messages::PluginProxy::CancelStreamLoad(streamID), m_pluginInstanceID); 210 } 211 212 void PluginControllerProxy::cancelManualStreamLoad() 213 { 214 m_pluginCanceledManualStreamLoad = true; 215 216 m_connection->connection()->send(Messages::PluginProxy::CancelManualStreamLoad(), m_pluginInstanceID); 217 } 218 219 NPObject* 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 238 NPObject* 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 257 bool 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 280 void PluginControllerProxy::setStatusbarText(const String& statusbarText) 281 { 282 m_connection->connection()->send(Messages::PluginProxy::SetStatusbarText(statusbarText), m_pluginInstanceID); 283 } 284 285 bool PluginControllerProxy::isAcceleratedCompositingEnabled() 286 { 287 return m_isAcceleratedCompositingEnabled; 288 } 289 290 void PluginControllerProxy::pluginProcessCrashed() 291 { 292 // This should never be called from here. 293 ASSERT_NOT_REACHED(); 294 } 295 296 #if PLATFORM(MAC) 297 void 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 307 mach_port_t PluginControllerProxy::compositingRenderServerPort() 308 { 309 return PluginProcess::shared().compositingRenderServerPort(); 310 } 311 #endif 312 313 String 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 323 String 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 333 void PluginControllerProxy::setCookiesForURL(const String& urlString, const String& cookieString) 334 { 335 m_connection->connection()->send(Messages::PluginProxy::SetCookiesForURL(urlString, cookieString), m_pluginInstanceID); 336 } 337 338 bool PluginControllerProxy::isPrivateBrowsingEnabled() 339 { 340 return m_isPrivateBrowsingEnabled; 341 } 342 343 void PluginControllerProxy::protectPluginFromDestruction() 344 { 345 m_pluginDestructionProtectCount++; 346 } 347 348 void PluginControllerProxy::unprotectPluginFromDestruction() 349 { 350 ASSERT(m_pluginDestructionProtectCount); 351 352 m_pluginDestructionProtectCount--; 353 } 354 355 void PluginControllerProxy::frameDidFinishLoading(uint64_t requestID) 356 { 357 m_plugin->frameDidFinishLoading(requestID); 358 } 359 360 void PluginControllerProxy::frameDidFail(uint64_t requestID, bool wasCancelled) 361 { 362 m_plugin->frameDidFail(requestID, wasCancelled); 363 } 364 365 void 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 382 void PluginControllerProxy::didEvaluateJavaScript(uint64_t requestID, const String& requestURLString, const String& result) 383 { 384 m_plugin->didEvaluateJavaScript(requestID, requestURLString, result); 385 } 386 387 void 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 392 void 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 397 void PluginControllerProxy::streamDidFinishLoading(uint64_t streamID) 398 { 399 m_plugin->streamDidFinishLoading(streamID); 400 } 401 402 void PluginControllerProxy::streamDidFail(uint64_t streamID, bool wasCancelled) 403 { 404 m_plugin->streamDidFail(streamID, wasCancelled); 405 } 406 407 void 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 415 void 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 423 void PluginControllerProxy::manualStreamDidFinishLoading() 424 { 425 if (m_pluginCanceledManualStreamLoad) 426 return; 427 428 m_plugin->manualStreamDidFinishLoading(); 429 } 430 431 void PluginControllerProxy::manualStreamDidFail(bool wasCancelled) 432 { 433 if (m_pluginCanceledManualStreamLoad) 434 return; 435 436 m_plugin->manualStreamDidFail(wasCancelled); 437 } 438 439 void 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 452 void PluginControllerProxy::handleWheelEvent(const WebWheelEvent& wheelEvent, bool& handled) 453 { 454 handled = m_plugin->handleWheelEvent(wheelEvent); 455 } 456 457 void PluginControllerProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent, bool& handled) 458 { 459 handled = m_plugin->handleMouseEnterEvent(mouseEnterEvent); 460 } 461 462 void PluginControllerProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent, bool& handled) 463 { 464 handled = m_plugin->handleMouseLeaveEvent(mouseLeaveEvent); 465 } 466 467 void PluginControllerProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent, bool& handled) 468 { 469 handled = m_plugin->handleKeyboardEvent(keyboardEvent); 470 } 471 472 void PluginControllerProxy::paintEntirePlugin() 473 { 474 if (m_frameRect.isEmpty()) 475 return; 476 477 m_dirtyRect = m_frameRect; 478 paint(); 479 } 480 481 void 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 491 void PluginControllerProxy::setFocus(bool hasFocus) 492 { 493 m_plugin->setFocus(hasFocus); 494 } 495 496 void PluginControllerProxy::didUpdate() 497 { 498 m_waitingForDidUpdate = false; 499 startPaintTimer(); 500 } 501 502 void 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 514 void PluginControllerProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled) 515 { 516 m_plugin->privateBrowsingStateChanged(isPrivateBrowsingEnabled); 517 } 518 519 } // namespace WebKit 520 521 #endif // ENABLE(PLUGIN_PROCESS) 522