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