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