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 "PluginProcess.h"
38#include "PluginProxyMessages.h"
39#include "ShareableBitmap.h"
40#include "WebCoreArgumentCoders.h"
41#include "WebProcessConnection.h"
42#include <WebCore/GraphicsContext.h>
43#include <wtf/text/WTFString.h>
44
45using namespace WebCore;
46
47namespace WebKit {
48
49PassOwnPtr<PluginControllerProxy> PluginControllerProxy::create(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled)
50{
51    return adoptPtr(new PluginControllerProxy(connection, pluginInstanceID, userAgent, isPrivateBrowsingEnabled, isAcceleratedCompositingEnabled));
52}
53
54PluginControllerProxy::PluginControllerProxy(WebProcessConnection* connection, uint64_t pluginInstanceID, const String& userAgent, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled)
55    : m_connection(connection)
56    , m_pluginInstanceID(pluginInstanceID)
57    , m_userAgent(userAgent)
58    , m_isPrivateBrowsingEnabled(isPrivateBrowsingEnabled)
59    , m_isAcceleratedCompositingEnabled(isAcceleratedCompositingEnabled)
60    , m_paintTimer(RunLoop::main(), this, &PluginControllerProxy::paint)
61    , m_pluginDestructionProtectCount(0)
62    , m_pluginDestroyTimer(RunLoop::main(), this, &PluginControllerProxy::destroy)
63    , m_waitingForDidUpdate(false)
64    , m_pluginCanceledManualStreamLoad(false)
65#if PLATFORM(MAC)
66    , m_isComplexTextInputEnabled(false)
67#endif
68    , m_windowNPObject(0)
69    , m_pluginElementNPObject(0)
70{
71}
72
73PluginControllerProxy::~PluginControllerProxy()
74{
75    ASSERT(!m_plugin);
76
77    if (m_windowNPObject)
78        releaseNPObject(m_windowNPObject);
79
80    if (m_pluginElementNPObject)
81        releaseNPObject(m_pluginElementNPObject);
82}
83
84bool PluginControllerProxy::initialize(const Plugin::Parameters& parameters)
85{
86    ASSERT(!m_plugin);
87
88    m_plugin = NetscapePlugin::create(PluginProcess::shared().netscapePluginModule());
89    if (!m_plugin) {
90        // This will delete the plug-in controller proxy object.
91        m_connection->removePluginControllerProxy(this, 0);
92        return false;
93    }
94
95    if (!m_plugin->initialize(this, parameters)) {
96        // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only
97        // used as an identifier so it's OK to just get a weak reference.
98        Plugin* plugin = m_plugin.get();
99
100        m_plugin = 0;
101
102        // This will delete the plug-in controller proxy object.
103        m_connection->removePluginControllerProxy(this, plugin);
104        return false;
105    }
106
107    platformInitialize();
108
109    return true;
110}
111
112void PluginControllerProxy::destroy()
113{
114    ASSERT(m_plugin);
115
116    if (m_pluginDestructionProtectCount) {
117        // We have plug-in code on the stack so we can't destroy it right now.
118        // Destroy it later.
119        m_pluginDestroyTimer.startOneShot(0);
120        return;
121    }
122
123    // Get the plug-in so we can pass it to removePluginControllerProxy. The pointer is only
124    // used as an identifier so it's OK to just get a weak reference.
125    Plugin* plugin = m_plugin.get();
126
127    m_plugin->destroy();
128    m_plugin = 0;
129
130    platformDestroy();
131
132    // This will delete the plug-in controller proxy object.
133    m_connection->removePluginControllerProxy(this, plugin);
134}
135
136void PluginControllerProxy::paint()
137{
138    ASSERT(!m_dirtyRect.isEmpty());
139    m_paintTimer.stop();
140
141    if (!m_backingStore)
142        return;
143
144    IntRect dirtyRect = m_dirtyRect;
145    m_dirtyRect = IntRect();
146
147    ASSERT(m_plugin);
148
149    // Create a graphics context.
150    OwnPtr<GraphicsContext> graphicsContext = m_backingStore->createGraphicsContext();
151
152    graphicsContext->translate(-m_frameRect.x(), -m_frameRect.y());
153
154    if (m_plugin->isTransparent())
155        graphicsContext->clearRect(dirtyRect);
156
157    m_plugin->paint(graphicsContext.get(), dirtyRect);
158
159    m_connection->connection()->send(Messages::PluginProxy::Update(dirtyRect), m_pluginInstanceID);
160}
161
162void PluginControllerProxy::startPaintTimer()
163{
164    // Check if we should start the timer.
165
166    if (m_dirtyRect.isEmpty())
167        return;
168
169    // FIXME: Check clip rect.
170
171    if (m_paintTimer.isActive())
172        return;
173
174    if (m_waitingForDidUpdate)
175        return;
176
177    // Start the timer.
178    m_paintTimer.startOneShot(0);
179
180    m_waitingForDidUpdate = true;
181}
182
183void PluginControllerProxy::invalidate(const IntRect& rect)
184{
185    // Convert the dirty rect to window coordinates.
186    IntRect dirtyRect = rect;
187    dirtyRect.move(m_frameRect.x(), m_frameRect.y());
188
189    // Make sure that the dirty rect is not greater than the plug-in itself.
190    dirtyRect.intersect(m_frameRect);
191
192    m_dirtyRect.unite(dirtyRect);
193
194    startPaintTimer();
195}
196
197String PluginControllerProxy::userAgent()
198{
199    return m_userAgent;
200}
201
202void PluginControllerProxy::loadURL(uint64_t requestID, const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody, bool allowPopups)
203{
204    m_connection->connection()->send(Messages::PluginProxy::LoadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups), m_pluginInstanceID);
205}
206
207void PluginControllerProxy::cancelStreamLoad(uint64_t streamID)
208{
209    m_connection->connection()->send(Messages::PluginProxy::CancelStreamLoad(streamID), m_pluginInstanceID);
210}
211
212void PluginControllerProxy::cancelManualStreamLoad()
213{
214    m_pluginCanceledManualStreamLoad = true;
215
216    m_connection->connection()->send(Messages::PluginProxy::CancelManualStreamLoad(), m_pluginInstanceID);
217}
218
219NPObject* PluginControllerProxy::windowScriptNPObject()
220{
221    if (!m_windowNPObject) {
222        uint64_t windowScriptNPObjectID = 0;
223
224        if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetWindowScriptNPObject(), Messages::PluginProxy::GetWindowScriptNPObject::Reply(windowScriptNPObjectID), m_pluginInstanceID))
225            return 0;
226
227        if (!windowScriptNPObjectID)
228            return 0;
229
230        m_windowNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(windowScriptNPObjectID, m_plugin.get());
231        ASSERT(m_windowNPObject);
232    }
233
234    retainNPObject(m_windowNPObject);
235    return m_windowNPObject;
236}
237
238NPObject* PluginControllerProxy::pluginElementNPObject()
239{
240    if (!m_pluginElementNPObject) {
241        uint64_t pluginElementNPObjectID = 0;
242
243        if (!m_connection->connection()->sendSync(Messages::PluginProxy::GetPluginElementNPObject(), Messages::PluginProxy::GetPluginElementNPObject::Reply(pluginElementNPObjectID), m_pluginInstanceID))
244            return 0;
245
246        if (!pluginElementNPObjectID)
247            return 0;
248
249        m_pluginElementNPObject = m_connection->npRemoteObjectMap()->createNPObjectProxy(pluginElementNPObjectID, m_plugin.get());
250        ASSERT(m_pluginElementNPObject);
251    }
252
253    retainNPObject(m_pluginElementNPObject);
254    return m_pluginElementNPObject;
255}
256
257bool PluginControllerProxy::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result, bool allowPopups)
258{
259    PluginDestructionProtector protector(this);
260
261    NPVariant npObjectAsNPVariant;
262    OBJECT_TO_NPVARIANT(npObject, npObjectAsNPVariant);
263
264    // Send the NPObject over as an NPVariantData.
265    NPVariantData npObjectAsNPVariantData = m_connection->npRemoteObjectMap()->npVariantToNPVariantData(npObjectAsNPVariant, m_plugin.get());
266
267    bool returnValue = false;
268    NPVariantData resultData;
269
270    if (!m_connection->connection()->sendSync(Messages::PluginProxy::Evaluate(npObjectAsNPVariantData, scriptString, allowPopups), Messages::PluginProxy::Evaluate::Reply(returnValue, resultData), m_pluginInstanceID))
271        return false;
272
273    if (!returnValue)
274        return false;
275
276    *result = m_connection->npRemoteObjectMap()->npVariantDataToNPVariant(resultData, m_plugin.get());
277    return true;
278}
279
280void PluginControllerProxy::setStatusbarText(const String& statusbarText)
281{
282    m_connection->connection()->send(Messages::PluginProxy::SetStatusbarText(statusbarText), m_pluginInstanceID);
283}
284
285bool PluginControllerProxy::isAcceleratedCompositingEnabled()
286{
287    return m_isAcceleratedCompositingEnabled;
288}
289
290void PluginControllerProxy::pluginProcessCrashed()
291{
292    // This should never be called from here.
293    ASSERT_NOT_REACHED();
294}
295
296#if PLATFORM(MAC)
297void PluginControllerProxy::setComplexTextInputEnabled(bool complexTextInputEnabled)
298{
299    if (m_isComplexTextInputEnabled == complexTextInputEnabled)
300        return;
301
302    m_isComplexTextInputEnabled = complexTextInputEnabled;
303
304    m_connection->connection()->send(Messages::PluginProxy::SetComplexTextInputEnabled(complexTextInputEnabled), m_pluginInstanceID);
305}
306
307mach_port_t PluginControllerProxy::compositingRenderServerPort()
308{
309    return PluginProcess::shared().compositingRenderServerPort();
310}
311#endif
312
313String PluginControllerProxy::proxiesForURL(const String& urlString)
314{
315    String proxyString;
316
317    if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(proxyString), m_pluginInstanceID))
318        return String();
319
320    return proxyString;
321}
322
323String PluginControllerProxy::cookiesForURL(const String& urlString)
324{
325    String cookieString;
326
327    if (!m_connection->connection()->sendSync(Messages::PluginProxy::CookiesForURL(urlString), Messages::PluginProxy::CookiesForURL::Reply(cookieString), m_pluginInstanceID))
328        return String();
329
330    return cookieString;
331}
332
333void PluginControllerProxy::setCookiesForURL(const String& urlString, const String& cookieString)
334{
335    m_connection->connection()->send(Messages::PluginProxy::SetCookiesForURL(urlString, cookieString), m_pluginInstanceID);
336}
337
338bool PluginControllerProxy::isPrivateBrowsingEnabled()
339{
340    return m_isPrivateBrowsingEnabled;
341}
342
343void PluginControllerProxy::protectPluginFromDestruction()
344{
345    m_pluginDestructionProtectCount++;
346}
347
348void PluginControllerProxy::unprotectPluginFromDestruction()
349{
350    ASSERT(m_pluginDestructionProtectCount);
351
352    m_pluginDestructionProtectCount--;
353}
354
355void PluginControllerProxy::frameDidFinishLoading(uint64_t requestID)
356{
357    m_plugin->frameDidFinishLoading(requestID);
358}
359
360void PluginControllerProxy::frameDidFail(uint64_t requestID, bool wasCancelled)
361{
362    m_plugin->frameDidFail(requestID, wasCancelled);
363}
364
365void PluginControllerProxy::geometryDidChange(const IntRect& frameRect, const IntRect& clipRect, const ShareableBitmap::Handle& backingStoreHandle)
366{
367    m_frameRect = frameRect;
368    m_clipRect = clipRect;
369
370    ASSERT(m_plugin);
371
372    platformGeometryDidChange();
373
374    if (!backingStoreHandle.isNull()) {
375        // Create a new backing store.
376        m_backingStore = ShareableBitmap::create(backingStoreHandle);
377    }
378
379    m_plugin->geometryDidChange(frameRect, clipRect);
380}
381
382void PluginControllerProxy::didEvaluateJavaScript(uint64_t requestID, const String& requestURLString, const String& result)
383{
384    m_plugin->didEvaluateJavaScript(requestID, requestURLString, result);
385}
386
387void PluginControllerProxy::streamDidReceiveResponse(uint64_t streamID, const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers)
388{
389    m_plugin->streamDidReceiveResponse(streamID, KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers);
390}
391
392void PluginControllerProxy::streamDidReceiveData(uint64_t streamID, const CoreIPC::DataReference& data)
393{
394    m_plugin->streamDidReceiveData(streamID, reinterpret_cast<const char*>(data.data()), data.size());
395}
396
397void PluginControllerProxy::streamDidFinishLoading(uint64_t streamID)
398{
399    m_plugin->streamDidFinishLoading(streamID);
400}
401
402void PluginControllerProxy::streamDidFail(uint64_t streamID, bool wasCancelled)
403{
404    m_plugin->streamDidFail(streamID, wasCancelled);
405}
406
407void PluginControllerProxy::manualStreamDidReceiveResponse(const String& responseURLString, uint32_t streamLength, uint32_t lastModifiedTime, const String& mimeType, const String& headers)
408{
409    if (m_pluginCanceledManualStreamLoad)
410        return;
411
412    m_plugin->manualStreamDidReceiveResponse(KURL(ParsedURLString, responseURLString), streamLength, lastModifiedTime, mimeType, headers);
413}
414
415void PluginControllerProxy::manualStreamDidReceiveData(const CoreIPC::DataReference& data)
416{
417    if (m_pluginCanceledManualStreamLoad)
418        return;
419
420    m_plugin->manualStreamDidReceiveData(reinterpret_cast<const char*>(data.data()), data.size());
421}
422
423void PluginControllerProxy::manualStreamDidFinishLoading()
424{
425    if (m_pluginCanceledManualStreamLoad)
426        return;
427
428    m_plugin->manualStreamDidFinishLoading();
429}
430
431void PluginControllerProxy::manualStreamDidFail(bool wasCancelled)
432{
433    if (m_pluginCanceledManualStreamLoad)
434        return;
435
436    m_plugin->manualStreamDidFail(wasCancelled);
437}
438
439void PluginControllerProxy::handleMouseEvent(const WebMouseEvent& mouseEvent, PassRefPtr<Messages::PluginControllerProxy::HandleMouseEvent::DelayedReply> reply)
440{
441    // Always let the web process think that we've handled this mouse event, even before passing it along to the plug-in.
442    // This is a workaround for
443    // <rdar://problem/9299901> UI process thinks the page is unresponsive when a plug-in is showing a context menu.
444    // The web process sends a synchronous HandleMouseEvent message and the plug-in process spawns a nested
445    // run loop when showing the context menu, so eventually the unresponsiveness timer kicks in in the UI process.
446    // FIXME: We should come up with a better way to do this.
447    reply->send(true);
448
449    m_plugin->handleMouseEvent(mouseEvent);
450}
451
452void PluginControllerProxy::handleWheelEvent(const WebWheelEvent& wheelEvent, bool& handled)
453{
454    handled = m_plugin->handleWheelEvent(wheelEvent);
455}
456
457void PluginControllerProxy::handleMouseEnterEvent(const WebMouseEvent& mouseEnterEvent, bool& handled)
458{
459    handled = m_plugin->handleMouseEnterEvent(mouseEnterEvent);
460}
461
462void PluginControllerProxy::handleMouseLeaveEvent(const WebMouseEvent& mouseLeaveEvent, bool& handled)
463{
464    handled = m_plugin->handleMouseLeaveEvent(mouseLeaveEvent);
465}
466
467void PluginControllerProxy::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent, bool& handled)
468{
469    handled = m_plugin->handleKeyboardEvent(keyboardEvent);
470}
471
472void PluginControllerProxy::paintEntirePlugin()
473{
474    if (m_frameRect.isEmpty())
475        return;
476
477    m_dirtyRect = m_frameRect;
478    paint();
479}
480
481void PluginControllerProxy::snapshot(ShareableBitmap::Handle& backingStoreHandle)
482{
483    ASSERT(m_plugin);
484    RefPtr<ShareableBitmap> bitmap = m_plugin->snapshot();
485    if (!bitmap)
486        return;
487
488    bitmap->createHandle(backingStoreHandle);
489}
490
491void PluginControllerProxy::setFocus(bool hasFocus)
492{
493    m_plugin->setFocus(hasFocus);
494}
495
496void PluginControllerProxy::didUpdate()
497{
498    m_waitingForDidUpdate = false;
499    startPaintTimer();
500}
501
502void PluginControllerProxy::getPluginScriptableNPObject(uint64_t& pluginScriptableNPObjectID)
503{
504    NPObject* pluginScriptableNPObject = m_plugin->pluginScriptableNPObject();
505    if (!pluginScriptableNPObject) {
506        pluginScriptableNPObjectID = 0;
507        return;
508    }
509
510    pluginScriptableNPObjectID = m_connection->npRemoteObjectMap()->registerNPObject(pluginScriptableNPObject, m_plugin.get());
511    releaseNPObject(pluginScriptableNPObject);
512}
513
514void PluginControllerProxy::privateBrowsingStateChanged(bool isPrivateBrowsingEnabled)
515{
516    m_plugin->privateBrowsingStateChanged(isPrivateBrowsingEnabled);
517}
518
519} // namespace WebKit
520
521#endif // ENABLE(PLUGIN_PROCESS)
522