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