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 "PluginProxy.h"
28
29#if ENABLE(PLUGIN_PROCESS)
30
31#include "DataReference.h"
32#include "NPRemoteObjectMap.h"
33#include "NPRuntimeUtilities.h"
34#include "NPVariantData.h"
35#include "PluginController.h"
36#include "PluginControllerProxyMessages.h"
37#include "PluginProcessConnection.h"
38#include "PluginProcessConnectionManager.h"
39#include "ShareableBitmap.h"
40#include "WebCoreArgumentCoders.h"
41#include "WebEvent.h"
42#include "WebProcessConnectionMessages.h"
43#include <WebCore/GraphicsContext.h>
44
45using namespace WebCore;
46
47namespace WebKit {
48
49static uint64_t generatePluginInstanceID()
50{
51    static uint64_t uniquePluginInstanceID;
52    return ++uniquePluginInstanceID;
53}
54
55PassRefPtr<PluginProxy> PluginProxy::create(const String& pluginPath)
56{
57    return adoptRef(new PluginProxy(pluginPath));
58}
59
60PluginProxy::PluginProxy(const String& pluginPath)
61    : m_pluginPath(pluginPath)
62    , m_pluginInstanceID(generatePluginInstanceID())
63    , m_pluginController(0)
64    , m_pluginBackingStoreContainsValidData(false)
65    , m_isStarted(false)
66    , m_waitingForPaintInResponseToUpdate(false)
67    , m_remoteLayerClientID(0)
68{
69}
70
71PluginProxy::~PluginProxy()
72{
73}
74
75void PluginProxy::pluginProcessCrashed()
76{
77    if (m_pluginController)
78        m_pluginController->pluginProcessCrashed();
79}
80
81bool PluginProxy::initialize(PluginController* pluginController, const Parameters& parameters)
82{
83    ASSERT(!m_pluginController);
84    ASSERT(pluginController);
85
86    m_pluginController = pluginController;
87
88    ASSERT(!m_connection);
89    m_connection = PluginProcessConnectionManager::shared().getPluginProcessConnection(m_pluginPath);
90
91    if (!m_connection)
92        return false;
93
94    // Add the plug-in proxy before creating the plug-in; it needs to be in the map because CreatePlugin
95    // can call back out to the plug-in proxy.
96    m_connection->addPluginProxy(this);
97
98    // Ask the plug-in process to create a plug-in.
99    bool result = false;
100
101    uint32_t remoteLayerClientID = 0;
102    if (!m_connection->connection()->sendSync(Messages::WebProcessConnection::CreatePlugin(m_pluginInstanceID, parameters, pluginController->userAgent(), pluginController->isPrivateBrowsingEnabled(), pluginController->isAcceleratedCompositingEnabled()), Messages::WebProcessConnection::CreatePlugin::Reply(result, remoteLayerClientID), 0) || !result) {
103        m_connection->removePluginProxy(this);
104        return false;
105    }
106
107    m_remoteLayerClientID = remoteLayerClientID;
108    m_isStarted = true;
109
110    return true;
111}
112
113void PluginProxy::destroy()
114{
115    ASSERT(m_isStarted);
116
117    m_connection->connection()->sendSync(Messages::WebProcessConnection::DestroyPlugin(m_pluginInstanceID), Messages::WebProcessConnection::DestroyPlugin::Reply(), 0);
118
119    m_isStarted = false;
120    m_pluginController = 0;
121
122    m_connection->removePluginProxy(this);
123}
124
125void PluginProxy::paint(GraphicsContext* graphicsContext, const IntRect& dirtyRect)
126{
127    if (!needsBackingStore() || !m_backingStore)
128        return;
129
130    if (!m_pluginBackingStoreContainsValidData) {
131        m_connection->connection()->sendSync(Messages::PluginControllerProxy::PaintEntirePlugin(), Messages::PluginControllerProxy::PaintEntirePlugin::Reply(), m_pluginInstanceID);
132
133        // Blit the plug-in backing store into our own backing store.
134        OwnPtr<WebCore::GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext();
135        graphicsContext->setCompositeOperation(CompositeCopy);
136
137        m_pluginBackingStore->paint(*graphicsContext, IntPoint(), IntRect(0, 0, m_frameRect.width(), m_frameRect.height()));
138
139        m_pluginBackingStoreContainsValidData = true;
140    }
141
142    IntRect dirtyRectInPluginCoordinates = dirtyRect;
143    dirtyRectInPluginCoordinates.move(-m_frameRect.x(), -m_frameRect.y());
144
145    m_backingStore->paint(*graphicsContext, dirtyRect.location(), dirtyRectInPluginCoordinates);
146
147    if (m_waitingForPaintInResponseToUpdate) {
148        m_waitingForPaintInResponseToUpdate = false;
149        m_connection->connection()->send(Messages::PluginControllerProxy::DidUpdate(), m_pluginInstanceID);
150        return;
151    }
152}
153
154PassRefPtr<ShareableBitmap> PluginProxy::snapshot()
155{
156    ShareableBitmap::Handle snapshotStoreHandle;
157    m_connection->connection()->sendSync(Messages::PluginControllerProxy::Snapshot(), Messages::PluginControllerProxy::Snapshot::Reply(snapshotStoreHandle), m_pluginInstanceID);
158
159    RefPtr<ShareableBitmap> snapshotBuffer = ShareableBitmap::create(snapshotStoreHandle);
160    return snapshotBuffer.release();
161}
162
163bool PluginProxy::isTransparent()
164{
165    // This should never be called from the web process.
166    ASSERT_NOT_REACHED();
167    return false;
168}
169
170void PluginProxy::geometryDidChange(const IntRect& frameRect, const IntRect& clipRect)
171{
172    ASSERT(m_isStarted);
173
174    m_frameRect = frameRect;
175
176    if (!needsBackingStore()) {
177        ShareableBitmap::Handle pluginBackingStoreHandle;
178        m_connection->connection()->send(Messages::PluginControllerProxy::GeometryDidChange(frameRect, clipRect, pluginBackingStoreHandle), m_pluginInstanceID, CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply);
179        return;
180    }
181
182    bool didUpdateBackingStore = false;
183    if (!m_backingStore) {
184        m_backingStore = ShareableBitmap::create(frameRect.size(), ShareableBitmap::SupportsAlpha);
185        didUpdateBackingStore = true;
186    } else if (frameRect.size() != m_backingStore->size()) {
187        // The backing store already exists, just resize it.
188        if (!m_backingStore->resize(frameRect.size()))
189            return;
190
191        didUpdateBackingStore = true;
192    }
193
194    ShareableBitmap::Handle pluginBackingStoreHandle;
195
196    if (didUpdateBackingStore) {
197        // Create a new plug-in backing store.
198        m_pluginBackingStore = ShareableBitmap::createShareable(frameRect.size(), ShareableBitmap::SupportsAlpha);
199        if (!m_pluginBackingStore)
200            return;
201
202        // Create a handle to the plug-in backing store so we can send it over.
203        if (!m_pluginBackingStore->createHandle(pluginBackingStoreHandle)) {
204            m_pluginBackingStore = nullptr;
205            return;
206        }
207
208        m_pluginBackingStoreContainsValidData = false;
209    }
210
211    m_connection->connection()->send(Messages::PluginControllerProxy::GeometryDidChange(frameRect, clipRect, pluginBackingStoreHandle), m_pluginInstanceID, CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply);
212}
213
214void PluginProxy::frameDidFinishLoading(uint64_t requestID)
215{
216    m_connection->connection()->send(Messages::PluginControllerProxy::FrameDidFinishLoading(requestID), m_pluginInstanceID);
217}
218
219void PluginProxy::frameDidFail(uint64_t requestID, bool wasCancelled)
220{
221    m_connection->connection()->send(Messages::PluginControllerProxy::FrameDidFail(requestID, wasCancelled), m_pluginInstanceID);
222}
223
224void PluginProxy::didEvaluateJavaScript(uint64_t requestID, const WTF::String& requestURLString, const WTF::String& result)
225{
226    m_connection->connection()->send(Messages::PluginControllerProxy::DidEvaluateJavaScript(requestID, requestURLString, result), m_pluginInstanceID);
227}
228
229void PluginProxy::streamDidReceiveResponse(uint64_t streamID, const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, const WTF::String& mimeType, const WTF::String& headers)
230{
231    m_connection->connection()->send(Messages::PluginControllerProxy::StreamDidReceiveResponse(streamID, responseURL.string(), streamLength, lastModifiedTime, mimeType, headers), m_pluginInstanceID);
232}
233
234void PluginProxy::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
235{
236    m_connection->connection()->send(Messages::PluginControllerProxy::StreamDidReceiveData(streamID, CoreIPC::DataReference(reinterpret_cast<const uint8_t*>(bytes), length)), m_pluginInstanceID);
237}
238
239void PluginProxy::streamDidFinishLoading(uint64_t streamID)
240{
241    m_connection->connection()->send(Messages::PluginControllerProxy::StreamDidFinishLoading(streamID), m_pluginInstanceID);
242}
243
244void PluginProxy::streamDidFail(uint64_t streamID, bool wasCancelled)
245{
246    m_connection->connection()->send(Messages::PluginControllerProxy::StreamDidFail(streamID, wasCancelled), m_pluginInstanceID);
247}
248
249void PluginProxy::manualStreamDidReceiveResponse(const KURL& responseURL, uint32_t streamLength,  uint32_t lastModifiedTime, const WTF::String& mimeType, const WTF::String& headers)
250{
251    m_connection->connection()->send(Messages::PluginControllerProxy::ManualStreamDidReceiveResponse(responseURL.string(), streamLength, lastModifiedTime, mimeType, headers), m_pluginInstanceID);
252}
253
254void PluginProxy::manualStreamDidReceiveData(const char* bytes, int length)
255{
256    m_connection->connection()->send(Messages::PluginControllerProxy::ManualStreamDidReceiveData(CoreIPC::DataReference(reinterpret_cast<const uint8_t*>(bytes), length)), m_pluginInstanceID);
257}
258
259void PluginProxy::manualStreamDidFinishLoading()
260{
261    m_connection->connection()->send(Messages::PluginControllerProxy::ManualStreamDidFinishLoading(), m_pluginInstanceID);
262}
263
264void PluginProxy::manualStreamDidFail(bool wasCancelled)
265{
266    m_connection->connection()->send(Messages::PluginControllerProxy::ManualStreamDidFail(wasCancelled), m_pluginInstanceID);
267}
268
269bool PluginProxy::handleMouseEvent(const WebMouseEvent& mouseEvent)
270{
271    bool handled = false;
272    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::HandleMouseEvent(mouseEvent), Messages::PluginControllerProxy::HandleMouseEvent::Reply(handled), m_pluginInstanceID))
273        return false;
274
275    return handled;
276}
277
278bool PluginProxy::handleWheelEvent(const WebWheelEvent& wheelEvent)
279{
280    bool handled = false;
281    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::HandleWheelEvent(wheelEvent), Messages::PluginControllerProxy::HandleWheelEvent::Reply(handled), m_pluginInstanceID))
282        return false;
283
284    return handled;
285}
286
287bool PluginProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent)
288{
289    bool handled = false;
290    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::HandleMouseEnterEvent(mouseEnterEvent), Messages::PluginControllerProxy::HandleMouseEnterEvent::Reply(handled), m_pluginInstanceID))
291        return false;
292
293    return handled;
294}
295
296bool PluginProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent)
297{
298    bool handled = false;
299    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::HandleMouseLeaveEvent(mouseLeaveEvent), Messages::PluginControllerProxy::HandleMouseLeaveEvent::Reply(handled), m_pluginInstanceID))
300        return false;
301
302    return handled;
303}
304
305bool PluginProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
306{
307    bool handled = false;
308    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::HandleKeyboardEvent(keyboardEvent), Messages::PluginControllerProxy::HandleKeyboardEvent::Reply(handled), m_pluginInstanceID))
309        return false;
310
311    return handled;
312}
313
314void PluginProxy::setFocus(bool hasFocus)
315{
316    m_connection->connection()->send(Messages::PluginControllerProxy::SetFocus(hasFocus), m_pluginInstanceID);
317}
318
319NPObject* PluginProxy::pluginScriptableNPObject()
320{
321    uint64_t pluginScriptableNPObjectID = 0;
322
323    if (!m_connection->connection()->sendSync(Messages::PluginControllerProxy::GetPluginScriptableNPObject(), Messages::PluginControllerProxy::GetPluginScriptableNPObject::Reply(pluginScriptableNPObjectID), m_pluginInstanceID))
324        return 0;
325
326    if (!pluginScriptableNPObjectID)
327        return 0;
328
329    return m_connection->npRemoteObjectMap()->createNPObjectProxy(pluginScriptableNPObjectID, this);
330}
331
332#if PLATFORM(MAC)
333void PluginProxy::windowFocusChanged(bool hasFocus)
334{
335    m_connection->connection()->send(Messages::PluginControllerProxy::WindowFocusChanged(hasFocus), m_pluginInstanceID);
336}
337
338void PluginProxy::windowAndViewFramesChanged(const WebCore::IntRect& windowFrameInScreenCoordinates, const WebCore::IntRect& viewFrameInWindowCoordinates)
339{
340    m_connection->connection()->send(Messages::PluginControllerProxy::WindowAndViewFramesChanged(windowFrameInScreenCoordinates, viewFrameInWindowCoordinates), m_pluginInstanceID);
341}
342
343void PluginProxy::windowVisibilityChanged(bool isVisible)
344{
345    m_connection->connection()->send(Messages::PluginControllerProxy::WindowVisibilityChanged(isVisible), m_pluginInstanceID);
346}
347
348uint64_t PluginProxy::pluginComplexTextInputIdentifier() const
349{
350    return m_pluginInstanceID;
351}
352
353void PluginProxy::sendComplexTextInput(const String& textInput)
354{
355    m_connection->connection()->send(Messages::PluginControllerProxy::SendComplexTextInput(textInput), m_pluginInstanceID);
356}
357
358#endif
359
360void PluginProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled)
361{
362    m_connection->connection()->send(Messages::PluginControllerProxy::PrivateBrowsingStateChanged(isPrivateBrowsingEnabled), m_pluginInstanceID);
363}
364
365PluginController* PluginProxy::controller()
366{
367    return m_pluginController;
368}
369
370void PluginProxy::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups)
371{
372    m_pluginController->loadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups);
373}
374
375void PluginProxy::proxiesForURL(const String& urlString, String& proxyString)
376{
377    proxyString = m_pluginController->proxiesForURL(urlString);
378}
379
380void PluginProxy::cookiesForURL(const String& urlString, String& cookieString)
381{
382    cookieString = m_pluginController->cookiesForURL(urlString);
383}
384
385void PluginProxy::setCookiesForURL(const String& urlString, const String& cookieString)
386{
387    m_pluginController->setCookiesForURL(urlString, cookieString);
388}
389
390void PluginProxy::getWindowScriptNPObject(uint64_t& windowScriptNPObjectID)
391{
392    NPObject* windowScriptNPObject = m_pluginController->windowScriptNPObject();
393    if (!windowScriptNPObject) {
394        windowScriptNPObjectID = 0;
395        return;
396    }
397
398    windowScriptNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(windowScriptNPObject, this);
399    releaseNPObject(windowScriptNPObject);
400}
401
402void PluginProxy::getPluginElementNPObject(uint64_t& pluginElementNPObjectID)
403{
404    NPObject* pluginElementNPObject = m_pluginController->pluginElementNPObject();
405    if (!pluginElementNPObject) {
406        pluginElementNPObjectID = 0;
407        return;
408    }
409
410    pluginElementNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(pluginElementNPObject, this);
411    releaseNPObject(pluginElementNPObject);
412}
413
414void PluginProxy::evaluate(const NPVariantData& npObjectAsVariantData, const String& scriptString, bool allowPopups, bool& returnValue, NPVariantData& resultData)
415{
416    PluginController::PluginDestructionProtector protector(m_pluginController);
417
418    NPVariant npObjectAsVariant = m_connection->npRemoteObjectMap()->npVariantDataToNPVariant(npObjectAsVariantData, this);
419    if (!NPVARIANT_IS_OBJECT(npObjectAsVariant) || !(NPVARIANT_TO_OBJECT(npObjectAsVariant))) {
420        returnValue = false;
421        return;
422    }
423
424    NPVariant result;
425    returnValue = m_pluginController->evaluate(NPVARIANT_TO_OBJECT(npObjectAsVariant), scriptString, &result, allowPopups);
426    if (!returnValue)
427        return;
428
429    // Convert the NPVariant to an NPVariantData.
430    resultData = m_connection->npRemoteObjectMap()->npVariantToNPVariantData(result, this);
431
432    // And release the result.
433    releaseNPVariantValue(&result);
434
435    releaseNPVariantValue(&npObjectAsVariant);
436}
437
438void PluginProxy::cancelStreamLoad(uint64_t streamID)
439{
440    m_pluginController->cancelStreamLoad(streamID);
441}
442
443void PluginProxy::cancelManualStreamLoad()
444{
445    m_pluginController->cancelManualStreamLoad();
446}
447
448void PluginProxy::setStatusbarText(const String& statusbarText)
449{
450    m_pluginController->setStatusbarText(statusbarText);
451}
452
453#if PLATFORM(MAC)
454void PluginProxy::setComplexTextInputEnabled(bool complexTextInputEnabled)
455{
456    m_pluginController->setComplexTextInputEnabled(complexTextInputEnabled);
457}
458#endif
459
460void PluginProxy::update(const IntRect& paintedRect)
461{
462    if (paintedRect == m_frameRect)
463        m_pluginBackingStoreContainsValidData = true;
464
465    IntRect paintedRectPluginCoordinates = paintedRect;
466    paintedRectPluginCoordinates.move(-m_frameRect.x(), -m_frameRect.y());
467
468    if (m_backingStore) {
469        // Blit the plug-in backing store into our own backing store.
470        OwnPtr<GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext();
471        graphicsContext->setCompositeOperation(CompositeCopy);
472        m_pluginBackingStore->paint(*graphicsContext, paintedRectPluginCoordinates.location(),
473                                    paintedRectPluginCoordinates);
474    }
475
476    // Ask the controller to invalidate the rect for us.
477    m_waitingForPaintInResponseToUpdate = true;
478    m_pluginController->invalidate(paintedRectPluginCoordinates);
479}
480
481} // namespace WebKit
482
483#endif // ENABLE(PLUGIN_PROCESS)
484