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 "PluginCreationParameters.h" 38#include "PluginProcess.h" 39#include "PluginProxyMessages.h" 40#include "ShareableBitmap.h" 41#include "WebCoreArgumentCoders.h" 42#include "WebProcessConnection.h" 43#include <WebCore/GraphicsContext.h> 44#include <WebCore/IdentifierRep.h> 45#include <WebCore/NotImplemented.h> 46#include <wtf/TemporaryChange.h> 47#include <wtf/text/WTFString.h> 48 49#if PLATFORM(MAC) 50#include "LayerHostingContext.h" 51#endif 52 53using namespace WebCore; 54 55namespace WebKit { 56 57PassOwnPtr<PluginControllerProxy> PluginControllerProxy::create(WebProcessConnection* connection, const PluginCreationParameters& creationParameters) 58{ 59 return adoptPtr(new PluginControllerProxy(connection, creationParameters)); 60} 61 62PluginControllerProxy::PluginControllerProxy(WebProcessConnection* connection, const PluginCreationParameters& creationParameters) 63 : m_connection(connection) 64 , m_pluginInstanceID(creationParameters.pluginInstanceID) 65 , m_userAgent(creationParameters.userAgent) 66 , m_isPrivateBrowsingEnabled(creationParameters.isPrivateBrowsingEnabled) 67#if USE(ACCELERATED_COMPOSITING) 68 , m_isAcceleratedCompositingEnabled(creationParameters.isAcceleratedCompositingEnabled) 69#endif 70 , m_isInitializing(false) 71 , m_paintTimer(RunLoop::main(), this, &PluginControllerProxy::paint) 72 , m_pluginDestructionProtectCount(0) 73 , m_pluginDestroyTimer(RunLoop::main(), this, &PluginControllerProxy::destroy) 74 , m_waitingForDidUpdate(false) 75 , m_pluginCanceledManualStreamLoad(false) 76#if PLATFORM(MAC) 77 , m_isComplexTextInputEnabled(false) 78#endif 79 , m_contentsScaleFactor(creationParameters.contentsScaleFactor) 80 , m_windowNPObject(0) 81 , m_pluginElementNPObject(0) 82{ 83} 84 85PluginControllerProxy::~PluginControllerProxy() 86{ 87 ASSERT(!m_plugin); 88 89 if (m_windowNPObject) 90 releaseNPObject(m_windowNPObject); 91 92 if (m_pluginElementNPObject) 93 releaseNPObject(m_pluginElementNPObject); 94} 95 96void PluginControllerProxy::setInitializationReply(PassRefPtr<Messages::WebProcessConnection::CreatePlugin::DelayedReply> reply) 97{ 98 ASSERT(!m_initializationReply); 99 m_initializationReply = reply; 100} 101 102PassRefPtr<Messages::WebProcessConnection::CreatePlugin::DelayedReply> PluginControllerProxy::takeInitializationReply() 103{ 104 return m_initializationReply.release(); 105} 106 107bool PluginControllerProxy::initialize(const PluginCreationParameters& creationParameters) 108{ 109 ASSERT(!m_plugin); 110 111 ASSERT(!m_isInitializing); 112 m_isInitializing = true; // Cannot use TemporaryChange here, because this object can be deleted before the function returns. 113 114 m_plugin = NetscapePlugin::create(PluginProcess::shared().netscapePluginModule()); 115 if (!m_plugin) { 116 // This will delete the plug-in controller proxy object. 117 m_connection->removePluginControllerProxy(this, 0); 118 return false; 119 } 120 121 if (creationParameters.windowNPObjectID) 122 m_windowNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(creationParameters.windowNPObjectID, m_plugin.get()); 123 124 bool returnValue = m_plugin->initialize(this, creationParameters.parameters); 125 126 if (!returnValue) { 127 // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only 128 // used as an identifier so it's OK to just get a weak reference. 129 Plugin* plugin = m_plugin.get(); 130 131 m_plugin = 0; 132 133 // This will delete the plug-in controller proxy object. 134 m_connection->removePluginControllerProxy(this, plugin); 135 return false; 136 } 137 138 platformInitialize(creationParameters); 139 140 m_isInitializing = false; 141 return true; 142} 143 144void PluginControllerProxy::destroy() 145{ 146 ASSERT(m_plugin); 147 148 // FIXME: Consider removing m_pluginDestructionProtectCount and always use inSendSync here. 149 if (m_pluginDestructionProtectCount || m_connection->connection()->inSendSync()) { 150 // We have plug-in code on the stack so we can't destroy it right now. 151 // Destroy it later. 152 m_pluginDestroyTimer.startOneShot(0); 153 return; 154 } 155 156 // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only 157 // used as an identifier so it's OK to just get a weak reference. 158 Plugin* plugin = m_plugin.get(); 159 160 m_plugin->destroyPlugin(); 161 m_plugin = 0; 162 163 platformDestroy(); 164 165 // This will delete the plug-in controller proxy object. 166 m_connection->removePluginControllerProxy(this, plugin); 167} 168 169bool PluginControllerProxy::wantsWheelEvents() const 170{ 171 return m_plugin->wantsWheelEvents(); 172} 173 174void PluginControllerProxy::paint() 175{ 176 ASSERT(!m_dirtyRect.isEmpty()); 177 m_paintTimer.stop(); 178 179 if (!m_backingStore) 180 return; 181 182 IntRect dirtyRect = m_dirtyRect; 183 m_dirtyRect = IntRect(); 184 185 ASSERT(m_plugin); 186 187 // Create a graphics context. 188 OwnPtr<GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext(); 189 190#if PLATFORM(MAC) 191 // FIXME: We should really call applyDeviceScaleFactor instead of scale, but that ends up calling into WKSI 192 // which we currently don't have initiated in the plug-in process. 193 graphicsContext->scale(FloatSize(m_contentsScaleFactor, m_contentsScaleFactor)); 194#endif 195 196 if (m_plugin->isTransparent()) 197 graphicsContext->clearRect(dirtyRect); 198 199 m_plugin->paint(graphicsContext.get(), dirtyRect); 200 201 m_connection->connection()->send(Messages::PluginProxy::Update(dirtyRect), m_pluginInstanceID); 202} 203 204void PluginControllerProxy::startPaintTimer() 205{ 206 // Check if we should start the timer. 207 208 if (m_dirtyRect.isEmpty()) 209 return; 210 211 // FIXME: Check clip rect. 212 213 if (m_paintTimer.isActive()) 214 return; 215 216 if (m_waitingForDidUpdate) 217 return; 218 219 // Start the timer. 220 m_paintTimer.startOneShot(0); 221 222 m_waitingForDidUpdate = true; 223} 224 225bool PluginControllerProxy::isPluginVisible() 226{ 227 // FIXME: Implement this. 228 notImplemented(); 229 return false; 230} 231 232void PluginControllerProxy::invalidate(const IntRect& rect) 233{ 234 IntRect dirtyRect = rect; 235 236 // Make sure that the dirty rect is not greater than the plug-in itself. 237 dirtyRect.intersect(IntRect(IntPoint(), m_pluginSize)); 238 m_dirtyRect.unite(dirtyRect); 239 240 startPaintTimer(); 241} 242 243String PluginControllerProxy::userAgent() 244{ 245 return m_userAgent; 246} 247 248void PluginControllerProxy::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups) 249{ 250 m_connection->connection()->send(Messages::PluginProxy::LoadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups), m_pluginInstanceID); 251} 252 253void PluginControllerProxy::cancelStreamLoad(uint64_t streamID) 254{ 255 m_connection->connection()->send(Messages::PluginProxy::CancelStreamLoad(streamID), m_pluginInstanceID); 256} 257 258void PluginControllerProxy::cancelManualStreamLoad() 259{ 260 m_pluginCanceledManualStreamLoad = true; 261 262 m_connection->connection()->send(Messages::PluginProxy::CancelManualStreamLoad(), m_pluginInstanceID); 263} 264 265NPObject* PluginControllerProxy::windowScriptNPObject() 266{ 267 if (!m_windowNPObject) 268 return 0; 269 270 retainNPObject(m_windowNPObject); 271 return m_windowNPObject; 272} 273 274NPObject* PluginControllerProxy::pluginElementNPObject() 275{ 276 if (!m_pluginElementNPObject) { 277 uint64_t pluginElementNPObjectID = 0; 278 279 if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetPluginElementNPObject(), Messages::PluginProxy::GetPluginElementNPObject::Reply(pluginElementNPObjectID), m_pluginInstanceID)) 280 return 0; 281 282 if (!pluginElementNPObjectID) 283 return 0; 284 285 m_pluginElementNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(pluginElementNPObjectID, m_plugin.get()); 286 ASSERT(m_pluginElementNPObject); 287 } 288 289 retainNPObject(m_pluginElementNPObject); 290 return m_pluginElementNPObject; 291} 292 293bool PluginControllerProxy::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) 294{ 295 PluginDestructionProtector protector(this); 296 297 NPVariant npObjectAsNPVariant; 298 OBJECT_TO_NPVARIANT(npObject, npObjectAsNPVariant); 299 300 // Send the NPObject over as an NPVariantData. 301 NPVariantData npObjectAsNPVariantData = m_connection->npRemoteObjectMap()->npVariantToNPVariantData(npObjectAsNPVariant, m_plugin.get()); 302 303 bool returnValue = false; 304 NPVariantData resultData; 305 306 if (!m_connection->connection()->sendSync(Messages::PluginProxy::Evaluate(npObjectAsNPVariantData, scriptString, allowPopups), Messages::PluginProxy::Evaluate::Reply(returnValue, resultData), m_pluginInstanceID)) 307 return false; 308 309 if (!returnValue) 310 return false; 311 312 *result = m_connection->npRemoteObjectMap()->npVariantDataToNPVariant(resultData, m_plugin.get()); 313 return true; 314} 315 316void PluginControllerProxy::setStatusbarText(const String& statusbarText) 317{ 318 m_connection->connection()->send(Messages::PluginProxy::SetStatusbarText(statusbarText), m_pluginInstanceID); 319} 320 321bool PluginControllerProxy::isAcceleratedCompositingEnabled() 322{ 323 return m_isAcceleratedCompositingEnabled; 324} 325 326void PluginControllerProxy::pluginProcessCrashed() 327{ 328 // This should never be called from here. 329 ASSERT_NOT_REACHED(); 330} 331 332void PluginControllerProxy::willSendEventToPlugin() 333{ 334 // This is only used when running plugins in the web process. 335 ASSERT_NOT_REACHED(); 336} 337 338void PluginControllerProxy::didInitializePlugin() 339{ 340 // This should only be called on the plugin in the web process. 341 ASSERT_NOT_REACHED(); 342} 343 344void PluginControllerProxy::didFailToInitializePlugin() 345{ 346 // This should only be called on the plugin in the web process. 347 ASSERT_NOT_REACHED(); 348} 349 350float PluginControllerProxy::contentsScaleFactor() 351{ 352 return m_contentsScaleFactor; 353} 354 355String PluginControllerProxy::proxiesForURL(const String& urlString) 356{ 357 String proxyString; 358 359 if (!m_connection->connection()->sendSync(Messages::PluginProxy::ProxiesForURL(urlString), Messages::PluginProxy::ProxiesForURL::Reply(proxyString), m_pluginInstanceID)) 360 return String(); 361 362 return proxyString; 363} 364 365String PluginControllerProxy::cookiesForURL(const String& urlString) 366{ 367 String cookieString; 368 369 if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(cookieString), m_pluginInstanceID)) 370 return String(); 371 372 return cookieString; 373} 374 375void PluginControllerProxy::setCookiesForURL(const String& urlString, const String& cookieString) 376{ 377 m_connection->connection()->send(Messages::PluginProxy::SetCookiesForURL(urlString, cookieString), m_pluginInstanceID); 378} 379 380bool PluginControllerProxy::isPrivateBrowsingEnabled() 381{ 382 return m_isPrivateBrowsingEnabled; 383} 384 385bool PluginControllerProxy::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password) 386{ 387 bool returnValue; 388 if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetAuthenticationInfo(protectionSpace), Messages::PluginProxy::GetAuthenticationInfo::Reply(returnValue, username, password), m_pluginInstanceID)) 389 return false; 390 391 return returnValue; 392} 393 394void PluginControllerProxy::protectPluginFromDestruction() 395{ 396 m_pluginDestructionProtectCount++; 397} 398 399void PluginControllerProxy::unprotectPluginFromDestruction() 400{ 401 ASSERT(m_pluginDestructionProtectCount); 402 403 m_pluginDestructionProtectCount--; 404} 405 406void PluginControllerProxy::frameDidFinishLoading(uint64_t requestID) 407{ 408 m_plugin->frameDidFinishLoading(requestID); 409} 410 411void PluginControllerProxy::frameDidFail(uint64_t requestID, bool wasCancelled) 412{ 413 m_plugin->frameDidFail(requestID, wasCancelled); 414} 415 416void PluginControllerProxy::geometryDidChange(const IntSize& pluginSize, const IntRect& clipRect, const AffineTransform& pluginToRootViewTransform, float contentsScaleFactor, const ShareableBitmap::Handle& backingStoreHandle) 417{ 418 ASSERT(m_plugin); 419 420 m_pluginSize = pluginSize; 421 422 if (contentsScaleFactor != m_contentsScaleFactor) { 423 m_contentsScaleFactor = contentsScaleFactor; 424 m_plugin->contentsScaleFactorChanged(m_contentsScaleFactor); 425 } 426 427 platformGeometryDidChange(); 428 429 if (!backingStoreHandle.isNull()) { 430 // Create a new backing store. 431 m_backingStore = ShareableBitmap::create(backingStoreHandle); 432 } 433 434 m_plugin->geometryDidChange(pluginSize, clipRect, pluginToRootViewTransform); 435} 436 437void PluginControllerProxy::didEvaluateJavaScript(uint64_t requestID, const String& result) 438{ 439 m_plugin->didEvaluateJavaScript(requestID, result); 440} 441 442void PluginControllerProxy::streamDidReceiveResponse(uint64_t streamID, const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) 443{ 444 m_plugin->streamDidReceiveResponse(streamID, KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers, String()); 445} 446 447void PluginControllerProxy::streamDidReceiveData(uint64_t streamID, const CoreIPC::DataReference& data) 448{ 449 m_plugin->streamDidReceiveData(streamID, reinterpret_cast<const char*>(data.data()), data.size()); 450} 451 452void PluginControllerProxy::streamDidFinishLoading(uint64_t streamID) 453{ 454 m_plugin->streamDidFinishLoading(streamID); 455} 456 457void PluginControllerProxy::streamDidFail(uint64_t streamID, bool wasCancelled) 458{ 459 m_plugin->streamDidFail(streamID, wasCancelled); 460} 461 462void PluginControllerProxy::manualStreamDidReceiveResponse(const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) 463{ 464 if (m_pluginCanceledManualStreamLoad) 465 return; 466 467 m_plugin->manualStreamDidReceiveResponse(KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers, String()); 468} 469 470void PluginControllerProxy::manualStreamDidReceiveData(const CoreIPC::DataReference& data) 471{ 472 if (m_pluginCanceledManualStreamLoad) 473 return; 474 475 m_plugin->manualStreamDidReceiveData(reinterpret_cast<const char*>(data.data()), data.size()); 476} 477 478void PluginControllerProxy::manualStreamDidFinishLoading() 479{ 480 if (m_pluginCanceledManualStreamLoad) 481 return; 482 483 m_plugin->manualStreamDidFinishLoading(); 484} 485 486void PluginControllerProxy::manualStreamDidFail(bool wasCancelled) 487{ 488 if (m_pluginCanceledManualStreamLoad) 489 return; 490 491 m_plugin->manualStreamDidFail(wasCancelled); 492} 493 494void PluginControllerProxy::handleMouseEvent(const WebMouseEvent& mouseEvent, PassRefPtr<Messages::PluginControllerProxy::HandleMouseEvent::DelayedReply> reply) 495{ 496 // Always let the web process think that we've handled this mouse event, even before passing it along to the plug-in. 497 // This is a workaround for 498 // <rdar://problem/9299901> UI process thinks the page is unresponsive when a plug-in is showing a context menu. 499 // The web process sends a synchronous HandleMouseEvent message and the plug-in process spawns a nested 500 // run loop when showing the context menu, so eventually the unresponsiveness timer kicks in in the UI process. 501 // FIXME: We should come up with a better way to do this. 502 reply->send(true); 503 504 m_plugin->handleMouseEvent(mouseEvent); 505} 506 507void PluginControllerProxy::handleWheelEvent(const WebWheelEvent& wheelEvent, bool& handled) 508{ 509 handled = m_plugin->handleWheelEvent(wheelEvent); 510} 511 512void PluginControllerProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent, bool& handled) 513{ 514 handled = m_plugin->handleMouseEnterEvent(mouseEnterEvent); 515} 516 517void PluginControllerProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent, bool& handled) 518{ 519 handled = m_plugin->handleMouseLeaveEvent(mouseLeaveEvent); 520} 521 522void PluginControllerProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent, bool& handled) 523{ 524 handled = m_plugin->handleKeyboardEvent(keyboardEvent); 525} 526 527void PluginControllerProxy::handleEditingCommand(const String& commandName, const String& argument, bool& handled) 528{ 529 handled = m_plugin->handleEditingCommand(commandName, argument); 530} 531 532void PluginControllerProxy::isEditingCommandEnabled(const String& commandName, bool& enabled) 533{ 534 enabled = m_plugin->isEditingCommandEnabled(commandName); 535} 536 537void PluginControllerProxy::handlesPageScaleFactor(bool& isHandled) 538{ 539 isHandled = m_plugin->handlesPageScaleFactor(); 540} 541 542void PluginControllerProxy::paintEntirePlugin() 543{ 544 if (m_pluginSize.isEmpty()) 545 return; 546 547 m_dirtyRect = IntRect(IntPoint(), m_pluginSize); 548 paint(); 549} 550 551void PluginControllerProxy::supportsSnapshotting(bool& isSupported) 552{ 553 isSupported = m_plugin->supportsSnapshotting(); 554} 555 556void PluginControllerProxy::snapshot(ShareableBitmap::Handle& backingStoreHandle) 557{ 558 ASSERT(m_plugin); 559 RefPtr<ShareableBitmap> bitmap = m_plugin->snapshot(); 560 if (!bitmap) 561 return; 562 563 bitmap->createHandle(backingStoreHandle); 564} 565 566void PluginControllerProxy::setFocus(bool hasFocus) 567{ 568 m_plugin->setFocus(hasFocus); 569} 570 571void PluginControllerProxy::didUpdate() 572{ 573 m_waitingForDidUpdate = false; 574 startPaintTimer(); 575} 576 577void PluginControllerProxy::getPluginScriptableNPObject(uint64_t& pluginScriptableNPObjectID) 578{ 579 NPObject* pluginScriptableNPObject = m_plugin->pluginScriptableNPObject(); 580 if (!pluginScriptableNPObject) { 581 pluginScriptableNPObjectID = 0; 582 return; 583 } 584 585 pluginScriptableNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(pluginScriptableNPObject, m_plugin.get()); 586 releaseNPObject(pluginScriptableNPObject); 587} 588 589void PluginControllerProxy::storageBlockingStateChanged(bool isStorageBlockingEnabled) 590{ 591 if (m_storageBlockingEnabled != isStorageBlockingEnabled) { 592 m_storageBlockingEnabled = isStorageBlockingEnabled; 593 m_plugin->storageBlockingStateChanged(m_storageBlockingEnabled); 594 } 595} 596 597void PluginControllerProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled) 598{ 599 m_isPrivateBrowsingEnabled = isPrivateBrowsingEnabled; 600 601 m_plugin->privateBrowsingStateChanged(isPrivateBrowsingEnabled); 602} 603 604void PluginControllerProxy::getFormValue(bool& returnValue, String& formValue) 605{ 606 returnValue = m_plugin->getFormValue(formValue); 607} 608 609#if PLUGIN_ARCHITECTURE(X11) 610uint64_t PluginControllerProxy::createPluginContainer() 611{ 612 uint64_t windowID = 0; 613 m_connection->connection()->sendSync(Messages::PluginProxy::CreatePluginContainer(), Messages::PluginProxy::CreatePluginContainer::Reply(windowID), m_pluginInstanceID); 614 return windowID; 615} 616 617void PluginControllerProxy::windowedPluginGeometryDidChange(const IntRect& frameRect, const IntRect& clipRect, uint64_t windowID) 618{ 619 m_connection->connection()->send(Messages::PluginProxy::WindowedPluginGeometryDidChange(frameRect, clipRect, windowID), m_pluginInstanceID); 620} 621#endif 622 623} // namespace WebKit 624 625#endif // ENABLE(PLUGIN_PROCESS) 626