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