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