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 "NetscapePlugin.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "NPRuntimeObjectMap.h"
32#include "NPRuntimeUtilities.h"
33#include "NetscapePluginStream.h"
34#include "PluginController.h"
35#include "ShareableBitmap.h"
36#include <WebCore/GraphicsContext.h>
37#include <WebCore/HTTPHeaderMap.h>
38#include <WebCore/IntRect.h>
39#include <WebCore/KURL.h>
40#include <WebCore/SharedBuffer.h>
41#include <runtime/JSObject.h>
42#include <utility>
43#include <wtf/text/CString.h>
44
45using namespace WebCore;
46
47namespace WebKit {
48
49// The plug-in that we're currently calling NPP_New for.
50static NetscapePlugin* currentNPPNewPlugin;
51
52PassRefPtr<NetscapePlugin> NetscapePlugin::create(PassRefPtr<NetscapePluginModule> pluginModule)
53{
54    if (!pluginModule)
55        return 0;
56
57    return adoptRef(new NetscapePlugin(pluginModule));
58}
59
60NetscapePlugin::NetscapePlugin(PassRefPtr<NetscapePluginModule> pluginModule)
61    : m_nextRequestID(0)
62    , m_pluginModule(pluginModule)
63    , m_npWindow()
64    , m_isStarted(false)
65#if PLATFORM(MAC)
66    , m_isWindowed(false)
67#else
68    , m_isWindowed(true)
69#endif
70    , m_isTransparent(false)
71    , m_inNPPNew(false)
72    , m_shouldUseManualLoader(false)
73    , m_hasCalledSetWindow(false)
74    , m_nextTimerID(0)
75#if PLATFORM(MAC)
76    , m_drawingModel(static_cast<NPDrawingModel>(-1))
77    , m_eventModel(static_cast<NPEventModel>(-1))
78    , m_pluginReturnsNonretainedLayer(!m_pluginModule->pluginQuirks().contains(PluginQuirks::ReturnsRetainedCoreAnimationLayer))
79    , m_layerHostingMode(LayerHostingModeDefault)
80    , m_currentMouseEvent(0)
81    , m_pluginHasFocus(false)
82    , m_windowHasFocus(false)
83    , m_pluginWantsLegacyCocoaTextInput(true)
84    , m_isComplexTextInputEnabled(false)
85    , m_hasHandledAKeyDownEvent(false)
86    , m_ignoreNextKeyUpEventCounter(0)
87#ifndef NP_NO_CARBON
88    , m_nullEventTimer(RunLoop::main(), this, &NetscapePlugin::nullEventTimerFired)
89    , m_npCGContext()
90#endif
91#elif PLUGIN_ARCHITECTURE(X11)
92    , m_drawable(0)
93    , m_pluginDisplay(0)
94#if PLATFORM(GTK)
95    , m_platformPluginWidget(0)
96#endif
97#endif
98{
99    m_npp.ndata = this;
100    m_npp.pdata = 0;
101
102    m_pluginModule->incrementLoadCount();
103}
104
105NetscapePlugin::~NetscapePlugin()
106{
107    ASSERT(!m_isStarted);
108    ASSERT(m_timers.isEmpty());
109
110    m_pluginModule->decrementLoadCount();
111}
112
113PassRefPtr<NetscapePlugin> NetscapePlugin::fromNPP(NPP npp)
114{
115    if (!npp)
116        return 0;
117
118    return static_cast<NetscapePlugin*>(npp->ndata);
119}
120
121void NetscapePlugin::invalidate(const NPRect* invalidRect)
122{
123    IntRect rect;
124
125    if (!invalidRect)
126        rect = IntRect(0, 0, m_pluginSize.width(), m_pluginSize.height());
127    else
128        rect = IntRect(invalidRect->left, invalidRect->top, invalidRect->right - invalidRect->left, invalidRect->bottom - invalidRect->top);
129
130    if (platformInvalidate(rect))
131        return;
132
133    controller()->invalidate(rect);
134}
135
136const char* NetscapePlugin::userAgent(NPP npp)
137{
138    if (npp)
139        return fromNPP(npp)->userAgent();
140
141    if (currentNPPNewPlugin)
142        return currentNPPNewPlugin->userAgent();
143
144    return 0;
145}
146
147const char* NetscapePlugin::userAgent()
148{
149#if PLUGIN_ARCHITECTURE(WIN)
150    static const char* MozillaUserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0";
151
152    if (quirks().contains(PluginQuirks::WantsMozillaUserAgent))
153        return MozillaUserAgent;
154#endif
155
156    if (m_userAgent.isNull()) {
157        String userAgent = controller()->userAgent();
158        ASSERT(!userAgent.isNull());
159
160#if PLUGIN_ARCHITECTURE(MAC)
161        if (quirks().contains(PluginQuirks::AppendVersion3UserAgent))
162            userAgent.append(" Version/3.2.1");
163#endif
164
165        m_userAgent = userAgent.utf8();
166    }
167    return m_userAgent.data();
168}
169
170void NetscapePlugin::loadURL(const String& method, const String& urlString, const String& target, const HTTPHeaderMap& headerFields, const Vector<uint8_t>& httpBody,
171                             bool sendNotification, void* notificationData)
172{
173    uint64_t requestID = ++m_nextRequestID;
174
175    controller()->loadURL(requestID, method, urlString, target, headerFields, httpBody, allowPopups());
176
177    if (target.isNull()) {
178        // The browser is going to send the data in a stream, create a plug-in stream.
179        RefPtr<NetscapePluginStream> pluginStream = NetscapePluginStream::create(this, requestID, urlString, sendNotification, notificationData);
180        ASSERT(!m_streams.contains(requestID));
181
182        m_streams.set(requestID, pluginStream.release());
183        return;
184    }
185
186    if (sendNotification) {
187        // Eventually we are going to get a frameDidFinishLoading or frameDidFail call for this request.
188        // Keep track of the notification data so we can call NPP_URLNotify.
189        ASSERT(!m_pendingURLNotifications.contains(requestID));
190        m_pendingURLNotifications.set(requestID, std::make_pair(urlString, notificationData));
191    }
192}
193
194NPError NetscapePlugin::destroyStream(NPStream* stream, NPReason reason)
195{
196    NetscapePluginStream* pluginStream = 0;
197
198    for (StreamsMap::const_iterator it = m_streams.begin(), end = m_streams.end(); it != end; ++it) {
199        if (it->value->npStream() == stream) {
200            pluginStream = it->value.get();
201            break;
202        }
203    }
204
205    if (!pluginStream)
206        return NPERR_INVALID_INSTANCE_ERROR;
207
208    return pluginStream->destroy(reason);
209}
210
211void NetscapePlugin::setIsWindowed(bool isWindowed)
212{
213    // Once the plugin has started, it's too late to change whether the plugin is windowed or not.
214    // (This is true in Firefox and Chrome, too.) Disallow setting m_isWindowed in that case to
215    // keep our internal state consistent.
216    if (m_isStarted)
217        return;
218
219    m_isWindowed = isWindowed;
220}
221
222void NetscapePlugin::setIsTransparent(bool isTransparent)
223{
224    m_isTransparent = isTransparent;
225}
226
227void NetscapePlugin::setStatusbarText(const String& statusbarText)
228{
229    controller()->setStatusbarText(statusbarText);
230}
231
232static void (*setExceptionFunction)(const String&);
233
234void NetscapePlugin::setSetExceptionFunction(void (*function)(const String&))
235{
236    ASSERT(!setExceptionFunction || setExceptionFunction == function);
237    setExceptionFunction = function;
238}
239
240void NetscapePlugin::setException(const String& exceptionString)
241{
242    ASSERT(setExceptionFunction);
243    setExceptionFunction(exceptionString);
244}
245
246bool NetscapePlugin::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result)
247{
248    return controller()->evaluate(npObject, scriptString, result, allowPopups());
249}
250
251bool NetscapePlugin::isPrivateBrowsingEnabled()
252{
253    return controller()->isPrivateBrowsingEnabled();
254}
255
256NPObject* NetscapePlugin::windowScriptNPObject()
257{
258    return controller()->windowScriptNPObject();
259}
260
261NPObject* NetscapePlugin::pluginElementNPObject()
262{
263    return controller()->pluginElementNPObject();
264}
265
266void NetscapePlugin::cancelStreamLoad(NetscapePluginStream* pluginStream)
267{
268    if (pluginStream == m_manualStream) {
269        controller()->cancelManualStreamLoad();
270        return;
271    }
272
273    // Ask the plug-in controller to cancel this stream load.
274    controller()->cancelStreamLoad(pluginStream->streamID());
275}
276
277void NetscapePlugin::removePluginStream(NetscapePluginStream* pluginStream)
278{
279    if (pluginStream == m_manualStream) {
280        m_manualStream = 0;
281        return;
282    }
283
284    ASSERT(m_streams.get(pluginStream->streamID()) == pluginStream);
285    m_streams.remove(pluginStream->streamID());
286}
287
288bool NetscapePlugin::isAcceleratedCompositingEnabled()
289{
290#if USE(ACCELERATED_COMPOSITING)
291    return controller()->isAcceleratedCompositingEnabled();
292#else
293    return false;
294#endif
295}
296
297void NetscapePlugin::pushPopupsEnabledState(bool state)
298{
299    m_popupEnabledStates.append(state);
300}
301
302void NetscapePlugin::popPopupsEnabledState()
303{
304    ASSERT(!m_popupEnabledStates.isEmpty());
305
306    m_popupEnabledStates.removeLast();
307}
308
309void NetscapePlugin::pluginThreadAsyncCall(void (*function)(void*), void* userData)
310{
311    RunLoop::main()->dispatch(WTF::bind(&NetscapePlugin::handlePluginThreadAsyncCall, this, function, userData));
312}
313
314void NetscapePlugin::handlePluginThreadAsyncCall(void (*function)(void*), void* userData)
315{
316    if (!m_isStarted)
317        return;
318
319    function(userData);
320}
321
322PassOwnPtr<NetscapePlugin::Timer> NetscapePlugin::Timer::create(NetscapePlugin* netscapePlugin, unsigned timerID, unsigned interval, bool repeat, TimerFunc timerFunc)
323{
324    return adoptPtr(new Timer(netscapePlugin, timerID, interval, repeat, timerFunc));
325}
326
327NetscapePlugin::Timer::Timer(NetscapePlugin* netscapePlugin, unsigned timerID, unsigned interval, bool repeat, TimerFunc timerFunc)
328    : m_netscapePlugin(netscapePlugin)
329    , m_timerID(timerID)
330    , m_interval(interval)
331    , m_repeat(repeat)
332    , m_timerFunc(timerFunc)
333    , m_timer(RunLoop::main(), this, &Timer::timerFired)
334{
335}
336
337NetscapePlugin::Timer::~Timer()
338{
339}
340
341void NetscapePlugin::Timer::start()
342{
343    double timeInterval = m_interval / 1000.0;
344
345    if (m_repeat)
346        m_timer.startRepeating(timeInterval);
347    else
348        m_timer.startOneShot(timeInterval);
349}
350
351void NetscapePlugin::Timer::stop()
352{
353    m_timer.stop();
354}
355
356void NetscapePlugin::Timer::timerFired()
357{
358    m_timerFunc(&m_netscapePlugin->m_npp, m_timerID);
359
360    if (!m_repeat)
361        m_netscapePlugin->unscheduleTimer(m_timerID);
362}
363
364uint32_t NetscapePlugin::scheduleTimer(unsigned interval, bool repeat, void (*timerFunc)(NPP, unsigned timerID))
365{
366    if (!timerFunc)
367        return 0;
368
369    // FIXME: Handle wrapping around.
370    unsigned timerID = ++m_nextTimerID;
371
372    OwnPtr<Timer> timer = Timer::create(this, timerID, interval, repeat, timerFunc);
373
374    // FIXME: Based on the plug-in visibility, figure out if we should throttle the timer, or if we should start it at all.
375    timer->start();
376    m_timers.set(timerID, timer.release());
377
378    return timerID;
379}
380
381void NetscapePlugin::unscheduleTimer(unsigned timerID)
382{
383    if (OwnPtr<Timer> timer = m_timers.take(timerID))
384        timer->stop();
385}
386
387double NetscapePlugin::contentsScaleFactor()
388{
389    return controller()->contentsScaleFactor();
390}
391
392String NetscapePlugin::proxiesForURL(const String& urlString)
393{
394    return controller()->proxiesForURL(urlString);
395}
396
397String NetscapePlugin::cookiesForURL(const String& urlString)
398{
399    return controller()->cookiesForURL(urlString);
400}
401
402void NetscapePlugin::setCookiesForURL(const String& urlString, const String& cookieString)
403{
404    controller()->setCookiesForURL(urlString, cookieString);
405}
406
407bool NetscapePlugin::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password)
408{
409    return controller()->getAuthenticationInfo(protectionSpace, username, password);
410}
411
412NPError NetscapePlugin::NPP_New(NPMIMEType pluginType, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* savedData)
413{
414    return m_pluginModule->pluginFuncs().newp(pluginType, &m_npp, mode, argc, argn, argv, savedData);
415}
416
417NPError NetscapePlugin::NPP_Destroy(NPSavedData** savedData)
418{
419    return m_pluginModule->pluginFuncs().destroy(&m_npp, savedData);
420}
421
422NPError NetscapePlugin::NPP_SetWindow(NPWindow* npWindow)
423{
424    return m_pluginModule->pluginFuncs().setwindow(&m_npp, npWindow);
425}
426
427NPError NetscapePlugin::NPP_NewStream(NPMIMEType mimeType, NPStream* stream, NPBool seekable, uint16_t* streamType)
428{
429    return m_pluginModule->pluginFuncs().newstream(&m_npp, mimeType, stream, seekable, streamType);
430}
431
432NPError NetscapePlugin::NPP_DestroyStream(NPStream* stream, NPReason reason)
433{
434    return m_pluginModule->pluginFuncs().destroystream(&m_npp, stream, reason);
435}
436
437void NetscapePlugin::NPP_StreamAsFile(NPStream* stream, const char* filename)
438{
439    return m_pluginModule->pluginFuncs().asfile(&m_npp, stream, filename);
440}
441
442int32_t NetscapePlugin::NPP_WriteReady(NPStream* stream)
443{
444    return m_pluginModule->pluginFuncs().writeready(&m_npp, stream);
445}
446
447int32_t NetscapePlugin::NPP_Write(NPStream* stream, int32_t offset, int32_t len, void* buffer)
448{
449    return m_pluginModule->pluginFuncs().write(&m_npp, stream, offset, len, buffer);
450}
451
452int16_t NetscapePlugin::NPP_HandleEvent(void* event)
453{
454    return m_pluginModule->pluginFuncs().event(&m_npp, event);
455}
456
457void NetscapePlugin::NPP_URLNotify(const char* url, NPReason reason, void* notifyData)
458{
459    m_pluginModule->pluginFuncs().urlnotify(&m_npp, url, reason, notifyData);
460}
461
462NPError NetscapePlugin::NPP_GetValue(NPPVariable variable, void *value)
463{
464    if (!m_pluginModule->pluginFuncs().getvalue)
465        return NPERR_GENERIC_ERROR;
466
467    return m_pluginModule->pluginFuncs().getvalue(&m_npp, variable, value);
468}
469
470NPError NetscapePlugin::NPP_SetValue(NPNVariable variable, void *value)
471{
472    if (!m_pluginModule->pluginFuncs().setvalue)
473        return NPERR_GENERIC_ERROR;
474
475    return m_pluginModule->pluginFuncs().setvalue(&m_npp, variable, value);
476}
477
478void NetscapePlugin::callSetWindow()
479{
480    if (wantsPluginRelativeNPWindowCoordinates()) {
481        m_npWindow.x = 0;
482        m_npWindow.y = 0;
483        m_npWindow.clipRect.top = m_clipRect.y();
484        m_npWindow.clipRect.left = m_clipRect.x();
485    } else {
486        IntPoint pluginLocationInRootViewCoordinates = convertToRootView(IntPoint());
487        IntPoint clipRectInRootViewCoordinates = convertToRootView(m_clipRect.location());
488
489        m_npWindow.x = pluginLocationInRootViewCoordinates.x();
490        m_npWindow.y = pluginLocationInRootViewCoordinates.y();
491        m_npWindow.clipRect.top = clipRectInRootViewCoordinates.y();
492        m_npWindow.clipRect.left = clipRectInRootViewCoordinates.x();
493    }
494
495    m_npWindow.width = m_pluginSize.width();
496    m_npWindow.height = m_pluginSize.height();
497    m_npWindow.clipRect.right = m_npWindow.clipRect.left + m_clipRect.width();
498    m_npWindow.clipRect.bottom = m_npWindow.clipRect.top + m_clipRect.height();
499
500    NPP_SetWindow(&m_npWindow);
501    m_hasCalledSetWindow = true;
502}
503
504void NetscapePlugin::callSetWindowInvisible()
505{
506    NPWindow invisibleWindow = m_npWindow;
507
508    invisibleWindow.window = 0;
509    invisibleWindow.clipRect.top = 0;
510    invisibleWindow.clipRect.left = 0;
511    invisibleWindow.clipRect.bottom = 0;
512    invisibleWindow.clipRect.right = 0;
513
514    NPP_SetWindow(&invisibleWindow);
515    m_hasCalledSetWindow = true;
516}
517
518bool NetscapePlugin::shouldLoadSrcURL()
519{
520    // Check if we should cancel the load
521    NPBool cancelSrcStream = false;
522
523    if (NPP_GetValue(NPPVpluginCancelSrcStream, &cancelSrcStream) != NPERR_NO_ERROR)
524        return true;
525
526    return !cancelSrcStream;
527}
528
529NetscapePluginStream* NetscapePlugin::streamFromID(uint64_t streamID)
530{
531    return m_streams.get(streamID);
532}
533
534void NetscapePlugin::stopAllStreams()
535{
536    Vector<RefPtr<NetscapePluginStream>> streams;
537    copyValuesToVector(m_streams, streams);
538
539    for (size_t i = 0; i < streams.size(); ++i)
540        streams[i]->stop(NPRES_USER_BREAK);
541}
542
543bool NetscapePlugin::allowPopups() const
544{
545    if (m_pluginModule->pluginFuncs().version >= NPVERS_HAS_POPUPS_ENABLED_STATE) {
546        if (!m_popupEnabledStates.isEmpty())
547            return m_popupEnabledStates.last();
548    }
549
550    // FIXME: Check if the current event is a user gesture.
551    // Really old versions of Flash required this for popups to work, but all newer versions
552    // support NPN_PushPopupEnabledState/NPN_PopPopupEnabledState.
553    return false;
554}
555
556#if PLUGIN_ARCHITECTURE(MAC)
557static bool isTransparentSilverlightBackgroundValue(const String& lowercaseBackgroundValue)
558{
559    // This checks if the background color value is transparent, according to
560    // the format documented at http://msdn.microsoft.com/en-us/library/cc838148(VS.95).aspx
561    if (lowercaseBackgroundValue.startsWith('#')) {
562        if (lowercaseBackgroundValue.length() == 5 && lowercaseBackgroundValue[1] != 'f') {
563            // An 8-bit RGB value with alpha transparency, in the form #ARGB.
564            return true;
565        }
566
567        if (lowercaseBackgroundValue.length() == 9 && !(lowercaseBackgroundValue[1] == 'f' && lowercaseBackgroundValue[2] == 'f')) {
568            // A 16-bit RGB value with alpha transparency, in the form #AARRGGBB.
569            return true;
570        }
571    } else if (lowercaseBackgroundValue.startsWith("sc#")) {
572        Vector<String> components;
573        lowercaseBackgroundValue.substring(3).split(",", components);
574
575        // An ScRGB value with alpha transparency, in the form sc#A,R,G,B.
576        if (components.size() == 4) {
577            if (components[0].toDouble() < 1)
578                return true;
579        }
580    } else if (lowercaseBackgroundValue == "transparent")
581        return true;
582
583    // This is an opaque color.
584    return false;
585}
586#endif
587
588bool NetscapePlugin::initialize(const Parameters& parameters)
589{
590    uint16_t mode = parameters.isFullFramePlugin ? NP_FULL : NP_EMBED;
591
592    m_shouldUseManualLoader = parameters.shouldUseManualLoader;
593
594    CString mimeTypeCString = parameters.mimeType.utf8();
595
596    ASSERT(parameters.names.size() == parameters.values.size());
597
598    Vector<CString> paramNames;
599    Vector<CString> paramValues;
600    for (size_t i = 0; i < parameters.names.size(); ++i) {
601        String parameterName = parameters.names[i];
602
603#if PLUGIN_ARCHITECTURE(MAC)
604        if (m_pluginModule->pluginQuirks().contains(PluginQuirks::WantsLowercaseParameterNames))
605            parameterName = parameterName.lower();
606#endif
607
608        paramNames.append(parameterName.utf8());
609        paramValues.append(parameters.values[i].utf8());
610    }
611
612    // The strings that these pointers point to are kept alive by paramNames and paramValues.
613    Vector<const char*> names;
614    Vector<const char*> values;
615    for (size_t i = 0; i < paramNames.size(); ++i) {
616        names.append(paramNames[i].data());
617        values.append(paramValues[i].data());
618    }
619
620#if PLUGIN_ARCHITECTURE(MAC)
621    if (m_pluginModule->pluginQuirks().contains(PluginQuirks::MakeOpaqueUnlessTransparentSilverlightBackgroundAttributeExists)) {
622        for (size_t i = 0; i < parameters.names.size(); ++i) {
623            if (equalIgnoringCase(parameters.names[i], "background")) {
624                setIsTransparent(isTransparentSilverlightBackgroundValue(parameters.values[i].lower()));
625                break;
626            }
627        }
628    }
629
630    m_layerHostingMode = parameters.layerHostingMode;
631#endif
632
633    platformPreInitialize();
634
635    NetscapePlugin* previousNPPNewPlugin = currentNPPNewPlugin;
636
637    m_inNPPNew = true;
638    currentNPPNewPlugin = this;
639
640    NPError error = NPP_New(const_cast<char*>(mimeTypeCString.data()), mode, names.size(),
641                            const_cast<char**>(names.data()), const_cast<char**>(values.data()), 0);
642
643    m_inNPPNew = false;
644    currentNPPNewPlugin = previousNPPNewPlugin;
645
646    if (error != NPERR_NO_ERROR)
647        return false;
648
649    m_isStarted = true;
650
651    // FIXME: This is not correct in all cases.
652    m_npWindow.type = NPWindowTypeDrawable;
653
654    if (!platformPostInitialize()) {
655        destroy();
656        return false;
657    }
658
659    // Load the src URL if needed.
660    if (!parameters.shouldUseManualLoader && !parameters.url.isEmpty() && shouldLoadSrcURL())
661        loadURL("GET", parameters.url.string(), String(), HTTPHeaderMap(), Vector<uint8_t>(), false, 0);
662
663    return true;
664}
665
666void NetscapePlugin::destroy()
667{
668    ASSERT(m_isStarted);
669
670    // Stop all streams.
671    stopAllStreams();
672
673#if !PLUGIN_ARCHITECTURE(MAC) && !PLUGIN_ARCHITECTURE(X11)
674    m_npWindow.window = 0;
675    callSetWindow();
676#endif
677
678    NPP_Destroy(0);
679
680    m_isStarted = false;
681
682    platformDestroy();
683
684    m_timers.clear();
685}
686
687void NetscapePlugin::paint(GraphicsContext* context, const IntRect& dirtyRect)
688{
689    ASSERT(m_isStarted);
690
691    platformPaint(context, dirtyRect);
692}
693
694PassRefPtr<ShareableBitmap> NetscapePlugin::snapshot()
695{
696    if (!supportsSnapshotting() || m_pluginSize.isEmpty())
697        return 0;
698
699    ASSERT(m_isStarted);
700
701    IntSize backingStoreSize = m_pluginSize;
702    backingStoreSize.scale(contentsScaleFactor());
703
704    RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(backingStoreSize, ShareableBitmap::SupportsAlpha);
705    OwnPtr<GraphicsContext> context = bitmap->createGraphicsContext();
706
707    // FIXME: We should really call applyDeviceScaleFactor instead of scale, but that ends up calling into WKSI
708    // which we currently don't have initiated in the plug-in process.
709    context->scale(FloatSize(contentsScaleFactor(), contentsScaleFactor()));
710
711    platformPaint(context.get(), IntRect(IntPoint(), m_pluginSize), true);
712
713    return bitmap.release();
714}
715
716bool NetscapePlugin::isTransparent()
717{
718    return m_isTransparent;
719}
720
721bool NetscapePlugin::wantsWheelEvents()
722{
723    return m_pluginModule->pluginQuirks().contains(PluginQuirks::WantsWheelEvents);
724}
725
726void NetscapePlugin::geometryDidChange(const IntSize& pluginSize, const IntRect& clipRect, const AffineTransform& pluginToRootViewTransform)
727{
728    ASSERT(m_isStarted);
729
730    if (pluginSize == m_pluginSize && m_clipRect == clipRect && m_pluginToRootViewTransform == pluginToRootViewTransform) {
731        // Nothing to do.
732        return;
733    }
734
735    bool shouldCallSetWindow = true;
736
737    // If the plug-in doesn't want window relative coordinates, we don't need to call setWindow unless its size or clip rect changes.
738    if (m_hasCalledSetWindow && wantsPluginRelativeNPWindowCoordinates() && m_pluginSize == pluginSize && m_clipRect == clipRect)
739        shouldCallSetWindow = false;
740
741    m_pluginSize = pluginSize;
742    m_clipRect = clipRect;
743    m_pluginToRootViewTransform = pluginToRootViewTransform;
744
745    IntPoint frameRectLocationInWindowCoordinates = m_pluginToRootViewTransform.mapPoint(IntPoint());
746    m_frameRectInWindowCoordinates = IntRect(frameRectLocationInWindowCoordinates, m_pluginSize);
747
748    platformGeometryDidChange();
749
750    if (!shouldCallSetWindow)
751        return;
752
753    callSetWindow();
754}
755
756void NetscapePlugin::visibilityDidChange()
757{
758    ASSERT(m_isStarted);
759
760    platformVisibilityDidChange();
761}
762
763void NetscapePlugin::frameDidFinishLoading(uint64_t requestID)
764{
765    ASSERT(m_isStarted);
766
767    PendingURLNotifyMap::iterator it = m_pendingURLNotifications.find(requestID);
768    if (it == m_pendingURLNotifications.end())
769        return;
770
771    String url = it->value.first;
772    void* notificationData = it->value.second;
773
774    m_pendingURLNotifications.remove(it);
775
776    NPP_URLNotify(url.utf8().data(), NPRES_DONE, notificationData);
777}
778
779void NetscapePlugin::frameDidFail(uint64_t requestID, bool wasCancelled)
780{
781    ASSERT(m_isStarted);
782
783    PendingURLNotifyMap::iterator it = m_pendingURLNotifications.find(requestID);
784    if (it == m_pendingURLNotifications.end())
785        return;
786
787    String url = it->value.first;
788    void* notificationData = it->value.second;
789
790    m_pendingURLNotifications.remove(it);
791
792    NPP_URLNotify(url.utf8().data(), wasCancelled ? NPRES_USER_BREAK : NPRES_NETWORK_ERR, notificationData);
793}
794
795void NetscapePlugin::didEvaluateJavaScript(uint64_t requestID, const String& result)
796{
797    ASSERT(m_isStarted);
798
799    if (NetscapePluginStream* pluginStream = streamFromID(requestID))
800        pluginStream->sendJavaScriptStream(result);
801}
802
803void NetscapePlugin::streamDidReceiveResponse(uint64_t streamID, const KURL& responseURL, uint32_t streamLength,
804                                              uint32_t lastModifiedTime, const String& mimeType, const String& headers, const String& /* suggestedFileName */)
805{
806    ASSERT(m_isStarted);
807
808    if (NetscapePluginStream* pluginStream = streamFromID(streamID))
809        pluginStream->didReceiveResponse(responseURL, streamLength, lastModifiedTime, mimeType, headers);
810}
811
812void NetscapePlugin::streamDidReceiveData(uint64_t streamID, const char* bytes, int length)
813{
814    ASSERT(m_isStarted);
815
816    if (NetscapePluginStream* pluginStream = streamFromID(streamID))
817        pluginStream->didReceiveData(bytes, length);
818}
819
820void NetscapePlugin::streamDidFinishLoading(uint64_t streamID)
821{
822    ASSERT(m_isStarted);
823
824    if (NetscapePluginStream* pluginStream = streamFromID(streamID))
825        pluginStream->didFinishLoading();
826}
827
828void NetscapePlugin::streamDidFail(uint64_t streamID, bool wasCancelled)
829{
830    ASSERT(m_isStarted);
831
832    if (NetscapePluginStream* pluginStream = streamFromID(streamID))
833        pluginStream->didFail(wasCancelled);
834}
835
836void NetscapePlugin::manualStreamDidReceiveResponse(const KURL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime,
837                                                    const String& mimeType, const String& headers, const String& /* suggestedFileName */)
838{
839    ASSERT(m_isStarted);
840    ASSERT(m_shouldUseManualLoader);
841    ASSERT(!m_manualStream);
842
843    m_manualStream = NetscapePluginStream::create(this, 0, responseURL.string(), false, 0);
844    m_manualStream->didReceiveResponse(responseURL, streamLength, lastModifiedTime, mimeType, headers);
845}
846
847void NetscapePlugin::manualStreamDidReceiveData(const char* bytes, int length)
848{
849    ASSERT(m_isStarted);
850    ASSERT(m_shouldUseManualLoader);
851    ASSERT(m_manualStream);
852
853    m_manualStream->didReceiveData(bytes, length);
854}
855
856void NetscapePlugin::manualStreamDidFinishLoading()
857{
858    ASSERT(m_isStarted);
859    ASSERT(m_shouldUseManualLoader);
860    ASSERT(m_manualStream);
861
862    m_manualStream->didFinishLoading();
863}
864
865void NetscapePlugin::manualStreamDidFail(bool wasCancelled)
866{
867    ASSERT(m_isStarted);
868    ASSERT(m_shouldUseManualLoader);
869
870    if (!m_manualStream)
871        return;
872    m_manualStream->didFail(wasCancelled);
873}
874
875bool NetscapePlugin::handleMouseEvent(const WebMouseEvent& mouseEvent)
876{
877    ASSERT(m_isStarted);
878
879    return platformHandleMouseEvent(mouseEvent);
880}
881
882bool NetscapePlugin::handleWheelEvent(const WebWheelEvent& wheelEvent)
883{
884    ASSERT(m_isStarted);
885
886    return platformHandleWheelEvent(wheelEvent);
887}
888
889bool NetscapePlugin::handleMouseEnterEvent(const WebMouseEvent& mouseEvent)
890{
891    ASSERT(m_isStarted);
892
893    return platformHandleMouseEnterEvent(mouseEvent);
894}
895
896bool NetscapePlugin::handleMouseLeaveEvent(const WebMouseEvent& mouseEvent)
897{
898    ASSERT(m_isStarted);
899
900    return platformHandleMouseLeaveEvent(mouseEvent);
901}
902
903bool NetscapePlugin::handleContextMenuEvent(const WebMouseEvent&)
904{
905    // We don't know if the plug-in has handled mousedown event by displaying a context menu, so we never want WebKit to show a default one.
906    return true;
907}
908
909bool NetscapePlugin::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
910{
911    ASSERT(m_isStarted);
912
913    return platformHandleKeyboardEvent(keyboardEvent);
914}
915
916bool NetscapePlugin::handleEditingCommand(const String& /* commandName */, const String& /* argument */)
917{
918    return false;
919}
920
921bool NetscapePlugin::isEditingCommandEnabled(const String& /* commandName */)
922{
923    return false;
924}
925
926bool NetscapePlugin::shouldAllowScripting()
927{
928    return true;
929}
930
931bool NetscapePlugin::shouldAllowNavigationFromDrags()
932{
933    return false;
934}
935
936bool NetscapePlugin::handlesPageScaleFactor()
937{
938    return false;
939}
940
941void NetscapePlugin::setFocus(bool hasFocus)
942{
943    ASSERT(m_isStarted);
944
945    platformSetFocus(hasFocus);
946}
947
948NPObject* NetscapePlugin::pluginScriptableNPObject()
949{
950    ASSERT(m_isStarted);
951    NPObject* scriptableNPObject = 0;
952
953    if (NPP_GetValue(NPPVpluginScriptableNPObject, &scriptableNPObject) != NPERR_NO_ERROR)
954        return 0;
955
956#if PLUGIN_ARCHITECTURE(MAC)
957    if (m_pluginModule->pluginQuirks().contains(PluginQuirks::ReturnsNonRetainedScriptableNPObject))
958        retainNPObject(scriptableNPObject);
959#endif
960
961    return scriptableNPObject;
962}
963
964unsigned NetscapePlugin::countFindMatches(const String&, WebCore::FindOptions, unsigned)
965{
966    return 0;
967}
968
969bool NetscapePlugin::findString(const String&, WebCore::FindOptions, unsigned)
970{
971    return false;
972}
973
974void NetscapePlugin::contentsScaleFactorChanged(float scaleFactor)
975{
976    ASSERT(m_isStarted);
977
978#if PLUGIN_ARCHITECTURE(MAC)
979    double contentsScaleFactor = scaleFactor;
980    NPP_SetValue(NPNVcontentsScaleFactor, &contentsScaleFactor);
981#else
982    UNUSED_PARAM(scaleFactor);
983#endif
984}
985
986void NetscapePlugin::storageBlockingStateChanged(bool storageBlockingEnabled)
987{
988    if (m_storageBlockingState != storageBlockingEnabled) {
989        m_storageBlockingState = storageBlockingEnabled;
990        updateNPNPrivateMode();
991    }
992}
993
994void NetscapePlugin::privateBrowsingStateChanged(bool privateBrowsingEnabled)
995{
996    if (m_privateBrowsingState != privateBrowsingEnabled) {
997        m_privateBrowsingState = privateBrowsingEnabled;
998        updateNPNPrivateMode();
999    }
1000}
1001
1002void NetscapePlugin::updateNPNPrivateMode()
1003{
1004    ASSERT(m_isStarted);
1005
1006    // From https://wiki.mozilla.org/Plugins:PrivateMode
1007    //   When the browser turns private mode on or off it will call NPP_SetValue for "NPNVprivateModeBool"
1008    //   (assigned enum value 18) with a pointer to an NPBool value on all applicable instances.
1009    //   Plugins should check the boolean value pointed to, not the pointer itself.
1010    //   The value will be true when private mode is on.
1011    NPBool value = m_privateBrowsingState || m_storageBlockingState;
1012    NPP_SetValue(NPNVprivateModeBool, &value);
1013}
1014
1015bool NetscapePlugin::getFormValue(String& formValue)
1016{
1017    ASSERT(m_isStarted);
1018
1019    char* formValueString = 0;
1020    if (NPP_GetValue(NPPVformValue, &formValueString) != NPERR_NO_ERROR)
1021        return false;
1022
1023    formValue = String::fromUTF8(formValueString);
1024
1025    // The plug-in allocates the form value string with NPN_MemAlloc so it needs to be freed with NPN_MemFree.
1026    npnMemFree(formValueString);
1027    return true;
1028}
1029
1030bool NetscapePlugin::handleScroll(ScrollDirection, ScrollGranularity)
1031{
1032    return false;
1033}
1034
1035Scrollbar* NetscapePlugin::horizontalScrollbar()
1036{
1037    return 0;
1038}
1039
1040Scrollbar* NetscapePlugin::verticalScrollbar()
1041{
1042    return 0;
1043}
1044
1045bool NetscapePlugin::supportsSnapshotting() const
1046{
1047#if PLATFORM(MAC)
1048    return m_pluginModule && m_pluginModule->pluginQuirks().contains(PluginQuirks::SupportsSnapshotting);
1049#endif
1050    return false;
1051}
1052
1053PassRefPtr<WebCore::SharedBuffer> NetscapePlugin::liveResourceData() const
1054{
1055    return 0;
1056}
1057
1058IntPoint NetscapePlugin::convertToRootView(const IntPoint& pointInPluginCoordinates) const
1059{
1060    return m_pluginToRootViewTransform.mapPoint(pointInPluginCoordinates);
1061}
1062
1063bool NetscapePlugin::convertFromRootView(const IntPoint& pointInRootViewCoordinates, IntPoint& pointInPluginCoordinates)
1064{
1065    if (!m_pluginToRootViewTransform.isInvertible())
1066        return false;
1067
1068    pointInPluginCoordinates = m_pluginToRootViewTransform.inverse().mapPoint(pointInRootViewCoordinates);
1069    return true;
1070}
1071
1072} // namespace WebKit
1073
1074#endif // ENABLE(NETSCAPE_PLUGIN_API)
1075