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