| /* |
| * Copyright (C) 2010 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "PluginControllerProxy.h" |
| |
| #if ENABLE(PLUGIN_PROCESS) |
| |
| #include "DataReference.h" |
| #include "NPObjectProxy.h" |
| #include "NPRemoteObjectMap.h" |
| #include "NPRuntimeUtilities.h" |
| #include "NPVariantData.h" |
| #include "NetscapePlugin.h" |
| #include "PluginProcess.h" |
| #include "PluginProxyMessages.h" |
| #include "ShareableBitmap.h" |
| #include "WebCoreArgumentCoders.h" |
| #include "WebProcessConnection.h" |
| #include <WebCore/GraphicsContext.h> |
| #include <wtf/text/WTFString.h> |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| PassOwnPtr<PluginControllerProxy> PluginControllerProxy::create(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled) |
| { |
| return adoptPtr(new PluginControllerProxy(connection, pluginInstanceID, userAgent, isPrivateBrowsingEnabled, isAcceleratedCompositingEnabled)); |
| } |
| |
| PluginControllerProxy::PluginControllerProxy(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled) |
| : m_connection(connection) |
| , m_pluginInstanceID(pluginInstanceID) |
| , m_userAgent(userAgent) |
| , m_isPrivateBrowsingEnabled(isPrivateBrowsingEnabled) |
| , m_isAcceleratedCompositingEnabled(isAcceleratedCompositingEnabled) |
| , m_paintTimer(RunLoop::main(), this, &PluginControllerProxy::paint) |
| , m_pluginDestructionProtectCount(0) |
| , m_pluginDestroyTimer(RunLoop::main(), this, &PluginControllerProxy::destroy) |
| , m_waitingForDidUpdate(false) |
| , m_pluginCanceledManualStreamLoad(false) |
| #if PLATFORM(MAC) |
| , m_isComplexTextInputEnabled(false) |
| #endif |
| , m_windowNPObject(0) |
| , m_pluginElementNPObject(0) |
| { |
| } |
| |
| PluginControllerProxy::~PluginControllerProxy() |
| { |
| ASSERT(!m_plugin); |
| |
| if (m_windowNPObject) |
| releaseNPObject(m_windowNPObject); |
| |
| if (m_pluginElementNPObject) |
| releaseNPObject(m_pluginElementNPObject); |
| } |
| |
| bool PluginControllerProxy::initialize(const Plugin::Parameters& parameters) |
| { |
| ASSERT(!m_plugin); |
| |
| m_plugin = NetscapePlugin::create(PluginProcess::shared().netscapePluginModule()); |
| if (!m_plugin) { |
| // This will delete the plug-in controller proxy object. |
| m_connection->removePluginControllerProxy(this, 0); |
| return false; |
| } |
| |
| if (!m_plugin->initialize(this, parameters)) { |
| // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only |
| // used as an identifier so it's OK to just get a weak reference. |
| Plugin* plugin = m_plugin.get(); |
| |
| m_plugin = 0; |
| |
| // This will delete the plug-in controller proxy object. |
| m_connection->removePluginControllerProxy(this, plugin); |
| return false; |
| } |
| |
| platformInitialize(); |
| |
| return true; |
| } |
| |
| void PluginControllerProxy::destroy() |
| { |
| ASSERT(m_plugin); |
| |
| if (m_pluginDestructionProtectCount) { |
| // We have plug-in code on the stack so we can't destroy it right now. |
| // Destroy it later. |
| m_pluginDestroyTimer.startOneShot(0); |
| return; |
| } |
| |
| // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only |
| // used as an identifier so it's OK to just get a weak reference. |
| Plugin* plugin = m_plugin.get(); |
| |
| m_plugin->destroy(); |
| m_plugin = 0; |
| |
| platformDestroy(); |
| |
| // This will delete the plug-in controller proxy object. |
| m_connection->removePluginControllerProxy(this, plugin); |
| } |
| |
| void PluginControllerProxy::paint() |
| { |
| ASSERT(!m_dirtyRect.isEmpty()); |
| m_paintTimer.stop(); |
| |
| if (!m_backingStore) |
| return; |
| |
| IntRect dirtyRect = m_dirtyRect; |
| m_dirtyRect = IntRect(); |
| |
| ASSERT(m_plugin); |
| |
| // Create a graphics context. |
| OwnPtr<GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext(); |
| |
| graphicsContext->translate(-m_frameRect.x(), -m_frameRect.y()); |
| |
| if (m_plugin->isTransparent()) |
| graphicsContext->clearRect(dirtyRect); |
| |
| m_plugin->paint(graphicsContext.get(), dirtyRect); |
| |
| m_connection->connection()->send(Messages::PluginProxy::Update(dirtyRect), m_pluginInstanceID); |
| } |
| |
| void PluginControllerProxy::startPaintTimer() |
| { |
| // Check if we should start the timer. |
| |
| if (m_dirtyRect.isEmpty()) |
| return; |
| |
| // FIXME: Check clip rect. |
| |
| if (m_paintTimer.isActive()) |
| return; |
| |
| if (m_waitingForDidUpdate) |
| return; |
| |
| // Start the timer. |
| m_paintTimer.startOneShot(0); |
| |
| m_waitingForDidUpdate = true; |
| } |
| |
| void PluginControllerProxy::invalidate(const IntRect& rect) |
| { |
| // Convert the dirty rect to window coordinates. |
| IntRect dirtyRect = rect; |
| dirtyRect.move(m_frameRect.x(), m_frameRect.y()); |
| |
| // Make sure that the dirty rect is not greater than the plug-in itself. |
| dirtyRect.intersect(m_frameRect); |
| |
| m_dirtyRect.unite(dirtyRect); |
| |
| startPaintTimer(); |
| } |
| |
| String PluginControllerProxy::userAgent() |
| { |
| return m_userAgent; |
| } |
| |
| 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) |
| { |
| m_connection->connection()->send(Messages::PluginProxy::LoadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups), m_pluginInstanceID); |
| } |
| |
| void PluginControllerProxy::cancelStreamLoad(uint64_t streamID) |
| { |
| m_connection->connection()->send(Messages::PluginProxy::CancelStreamLoad(streamID), m_pluginInstanceID); |
| } |
| |
| void PluginControllerProxy::cancelManualStreamLoad() |
| { |
| m_pluginCanceledManualStreamLoad = true; |
| |
| m_connection->connection()->send(Messages::PluginProxy::CancelManualStreamLoad(), m_pluginInstanceID); |
| } |
| |
| NPObject* PluginControllerProxy::windowScriptNPObject() |
| { |
| if (!m_windowNPObject) { |
| uint64_t windowScriptNPObjectID = 0; |
| |
| if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetWindowScriptNPObject(), Messages::PluginProxy::GetWindowScriptNPObject::Reply(windowScriptNPObjectID), m_pluginInstanceID)) |
| return 0; |
| |
| if (!windowScriptNPObjectID) |
| return 0; |
| |
| m_windowNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(windowScriptNPObjectID, m_plugin.get()); |
| ASSERT(m_windowNPObject); |
| } |
| |
| retainNPObject(m_windowNPObject); |
| return m_windowNPObject; |
| } |
| |
| NPObject* PluginControllerProxy::pluginElementNPObject() |
| { |
| if (!m_pluginElementNPObject) { |
| uint64_t pluginElementNPObjectID = 0; |
| |
| if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetPluginElementNPObject(), Messages::PluginProxy::GetPluginElementNPObject::Reply(pluginElementNPObjectID), m_pluginInstanceID)) |
| return 0; |
| |
| if (!pluginElementNPObjectID) |
| return 0; |
| |
| m_pluginElementNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(pluginElementNPObjectID, m_plugin.get()); |
| ASSERT(m_pluginElementNPObject); |
| } |
| |
| retainNPObject(m_pluginElementNPObject); |
| return m_pluginElementNPObject; |
| } |
| |
| bool PluginControllerProxy::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups) |
| { |
| PluginDestructionProtector protector(this); |
| |
| NPVariant npObjectAsNPVariant; |
| OBJECT_TO_NPVARIANT(npObject, npObjectAsNPVariant); |
| |
| // Send the NPObject over as an NPVariantData. |
| NPVariantData npObjectAsNPVariantData = m_connection->npRemoteObjectMap()->npVariantToNPVariantData(npObjectAsNPVariant, m_plugin.get()); |
| |
| bool returnValue = false; |
| NPVariantData resultData; |
| |
| if (!m_connection->connection()->sendSync(Messages::PluginProxy::Evaluate(npObjectAsNPVariantData, scriptString, allowPopups), Messages::PluginProxy::Evaluate::Reply(returnValue, resultData), m_pluginInstanceID)) |
| return false; |
| |
| if (!returnValue) |
| return false; |
| |
| *result = m_connection->npRemoteObjectMap()->npVariantDataToNPVariant(resultData, m_plugin.get()); |
| return true; |
| } |
| |
| void PluginControllerProxy::setStatusbarText(const String& statusbarText) |
| { |
| m_connection->connection()->send(Messages::PluginProxy::SetStatusbarText(statusbarText), m_pluginInstanceID); |
| } |
| |
| bool PluginControllerProxy::isAcceleratedCompositingEnabled() |
| { |
| return m_isAcceleratedCompositingEnabled; |
| } |
| |
| void PluginControllerProxy::pluginProcessCrashed() |
| { |
| // This should never be called from here. |
| ASSERT_NOT_REACHED(); |
| } |
| |
| #if PLATFORM(MAC) |
| void PluginControllerProxy::setComplexTextInputEnabled(bool complexTextInputEnabled) |
| { |
| if (m_isComplexTextInputEnabled == complexTextInputEnabled) |
| return; |
| |
| m_isComplexTextInputEnabled = complexTextInputEnabled; |
| |
| m_connection->connection()->send(Messages::PluginProxy::SetComplexTextInputEnabled(complexTextInputEnabled), m_pluginInstanceID); |
| } |
| |
| mach_port_t PluginControllerProxy::compositingRenderServerPort() |
| { |
| return PluginProcess::shared().compositingRenderServerPort(); |
| } |
| #endif |
| |
| String PluginControllerProxy::proxiesForURL(const String& urlString) |
| { |
| String proxyString; |
| |
| if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(proxyString), m_pluginInstanceID)) |
| return String(); |
| |
| return proxyString; |
| } |
| |
| String PluginControllerProxy::cookiesForURL(const String& urlString) |
| { |
| String cookieString; |
| |
| if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(cookieString), m_pluginInstanceID)) |
| return String(); |
| |
| return cookieString; |
| } |
| |
| void PluginControllerProxy::setCookiesForURL(const String& urlString, const String& cookieString) |
| { |
| m_connection->connection()->send(Messages::PluginProxy::SetCookiesForURL(urlString, cookieString), m_pluginInstanceID); |
| } |
| |
| bool PluginControllerProxy::isPrivateBrowsingEnabled() |
| { |
| return m_isPrivateBrowsingEnabled; |
| } |
| |
| void PluginControllerProxy::protectPluginFromDestruction() |
| { |
| m_pluginDestructionProtectCount++; |
| } |
| |
| void PluginControllerProxy::unprotectPluginFromDestruction() |
| { |
| ASSERT(m_pluginDestructionProtectCount); |
| |
| m_pluginDestructionProtectCount--; |
| } |
| |
| void PluginControllerProxy::frameDidFinishLoading(uint64_t requestID) |
| { |
| m_plugin->frameDidFinishLoading(requestID); |
| } |
| |
| void PluginControllerProxy::frameDidFail(uint64_t requestID, bool wasCancelled) |
| { |
| m_plugin->frameDidFail(requestID, wasCancelled); |
| } |
| |
| void PluginControllerProxy::geometryDidChange(const IntRect& frameRect, const IntRect& clipRect, const ShareableBitmap::Handle& backingStoreHandle) |
| { |
| m_frameRect = frameRect; |
| m_clipRect = clipRect; |
| |
| ASSERT(m_plugin); |
| |
| platformGeometryDidChange(); |
| |
| if (!backingStoreHandle.isNull()) { |
| // Create a new backing store. |
| m_backingStore = ShareableBitmap::create(backingStoreHandle); |
| } |
| |
| m_plugin->geometryDidChange(frameRect, clipRect); |
| } |
| |
| void PluginControllerProxy::didEvaluateJavaScript(uint64_t requestID, const String& requestURLString, const String& result) |
| { |
| m_plugin->didEvaluateJavaScript(requestID, requestURLString, result); |
| } |
| |
| void PluginControllerProxy::streamDidReceiveResponse(uint64_t streamID, const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) |
| { |
| m_plugin->streamDidReceiveResponse(streamID, KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers); |
| } |
| |
| void PluginControllerProxy::streamDidReceiveData(uint64_t streamID, const CoreIPC::DataReference& data) |
| { |
| m_plugin->streamDidReceiveData(streamID, reinterpret_cast<const char*>(data.data()), data.size()); |
| } |
| |
| void PluginControllerProxy::streamDidFinishLoading(uint64_t streamID) |
| { |
| m_plugin->streamDidFinishLoading(streamID); |
| } |
| |
| void PluginControllerProxy::streamDidFail(uint64_t streamID, bool wasCancelled) |
| { |
| m_plugin->streamDidFail(streamID, wasCancelled); |
| } |
| |
| void PluginControllerProxy::manualStreamDidReceiveResponse(const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers) |
| { |
| if (m_pluginCanceledManualStreamLoad) |
| return; |
| |
| m_plugin->manualStreamDidReceiveResponse(KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers); |
| } |
| |
| void PluginControllerProxy::manualStreamDidReceiveData(const CoreIPC::DataReference& data) |
| { |
| if (m_pluginCanceledManualStreamLoad) |
| return; |
| |
| m_plugin->manualStreamDidReceiveData(reinterpret_cast<const char*>(data.data()), data.size()); |
| } |
| |
| void PluginControllerProxy::manualStreamDidFinishLoading() |
| { |
| if (m_pluginCanceledManualStreamLoad) |
| return; |
| |
| m_plugin->manualStreamDidFinishLoading(); |
| } |
| |
| void PluginControllerProxy::manualStreamDidFail(bool wasCancelled) |
| { |
| if (m_pluginCanceledManualStreamLoad) |
| return; |
| |
| m_plugin->manualStreamDidFail(wasCancelled); |
| } |
| |
| void PluginControllerProxy::handleMouseEvent(const WebMouseEvent& mouseEvent, PassRefPtr<Messages::PluginControllerProxy::HandleMouseEvent::DelayedReply> reply) |
| { |
| // Always let the web process think that we've handled this mouse event, even before passing it along to the plug-in. |
| // This is a workaround for |
| // <rdar://problem/9299901> UI process thinks the page is unresponsive when a plug-in is showing a context menu. |
| // The web process sends a synchronous HandleMouseEvent message and the plug-in process spawns a nested |
| // run loop when showing the context menu, so eventually the unresponsiveness timer kicks in in the UI process. |
| // FIXME: We should come up with a better way to do this. |
| reply->send(true); |
| |
| m_plugin->handleMouseEvent(mouseEvent); |
| } |
| |
| void PluginControllerProxy::handleWheelEvent(const WebWheelEvent& wheelEvent, bool& handled) |
| { |
| handled = m_plugin->handleWheelEvent(wheelEvent); |
| } |
| |
| void PluginControllerProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent, bool& handled) |
| { |
| handled = m_plugin->handleMouseEnterEvent(mouseEnterEvent); |
| } |
| |
| void PluginControllerProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent, bool& handled) |
| { |
| handled = m_plugin->handleMouseLeaveEvent(mouseLeaveEvent); |
| } |
| |
| void PluginControllerProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent, bool& handled) |
| { |
| handled = m_plugin->handleKeyboardEvent(keyboardEvent); |
| } |
| |
| void PluginControllerProxy::paintEntirePlugin() |
| { |
| if (m_frameRect.isEmpty()) |
| return; |
| |
| m_dirtyRect = m_frameRect; |
| paint(); |
| } |
| |
| void PluginControllerProxy::snapshot(ShareableBitmap::Handle& backingStoreHandle) |
| { |
| ASSERT(m_plugin); |
| RefPtr<ShareableBitmap> bitmap = m_plugin->snapshot(); |
| if (!bitmap) |
| return; |
| |
| bitmap->createHandle(backingStoreHandle); |
| } |
| |
| void PluginControllerProxy::setFocus(bool hasFocus) |
| { |
| m_plugin->setFocus(hasFocus); |
| } |
| |
| void PluginControllerProxy::didUpdate() |
| { |
| m_waitingForDidUpdate = false; |
| startPaintTimer(); |
| } |
| |
| void PluginControllerProxy::getPluginScriptableNPObject(uint64_t& pluginScriptableNPObjectID) |
| { |
| NPObject* pluginScriptableNPObject = m_plugin->pluginScriptableNPObject(); |
| if (!pluginScriptableNPObject) { |
| pluginScriptableNPObjectID = 0; |
| return; |
| } |
| |
| pluginScriptableNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(pluginScriptableNPObject, m_plugin.get()); |
| releaseNPObject(pluginScriptableNPObject); |
| } |
| |
| void PluginControllerProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled) |
| { |
| m_plugin->privateBrowsingStateChanged(isPrivateBrowsingEnabled); |
| } |
| |
| } // namespace WebKit |
| |
| #endif // ENABLE(PLUGIN_PROCESS) |