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 | #endif |
93 | { |
94 | m_npp.ndata = this; |
95 | m_npp.pdata = 0; |
96 | |
97 | m_pluginModule->incrementLoadCount(); |
98 | } |
99 | |
100 | NetscapePlugin::~NetscapePlugin() |
101 | { |
102 | ASSERT(!m_isStarted); |
103 | ASSERT(m_timers.isEmpty()); |
104 | |
105 | m_pluginModule->decrementLoadCount(); |
106 | } |
107 | |
108 | RefPtr<NetscapePlugin> NetscapePlugin::fromNPP(NPP npp) |
109 | { |
110 | if (!npp) |
111 | return nullptr; |
112 | |
113 | return static_cast<NetscapePlugin*>(npp->ndata); |
114 | } |
115 | |
116 | void 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 | |
131 | const 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 | |
142 | const 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 | |
158 | void NetscapePlugin::(const String& method, const String& urlString, const String& target, const HTTPHeaderMap& , 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 | |
182 | NPError 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 | |
199 | void 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 | |
210 | void NetscapePlugin::setIsTransparent(bool isTransparent) |
211 | { |
212 | m_isTransparent = isTransparent; |
213 | } |
214 | |
215 | void NetscapePlugin::setStatusbarText(const String& statusbarText) |
216 | { |
217 | controller()->setStatusbarText(statusbarText); |
218 | } |
219 | |
220 | static void (*setExceptionFunction)(const String&); |
221 | |
222 | void NetscapePlugin::setSetExceptionFunction(void (*function)(const String&)) |
223 | { |
224 | ASSERT(!setExceptionFunction || setExceptionFunction == function); |
225 | setExceptionFunction = function; |
226 | } |
227 | |
228 | void NetscapePlugin::setException(const String& exceptionString) |
229 | { |
230 | ASSERT(setExceptionFunction); |
231 | setExceptionFunction(exceptionString); |
232 | } |
233 | |
234 | bool NetscapePlugin::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result) |
235 | { |
236 | return controller()->evaluate(npObject, scriptString, result, allowPopups()); |
237 | } |
238 | |
239 | bool NetscapePlugin::isPrivateBrowsingEnabled() |
240 | { |
241 | return controller()->isPrivateBrowsingEnabled(); |
242 | } |
243 | |
244 | bool NetscapePlugin::isMuted() const |
245 | { |
246 | return controller()->isMuted(); |
247 | } |
248 | |
249 | NPObject* NetscapePlugin::windowScriptNPObject() |
250 | { |
251 | return controller()->windowScriptNPObject(); |
252 | } |
253 | |
254 | NPObject* NetscapePlugin::pluginElementNPObject() |
255 | { |
256 | return controller()->pluginElementNPObject(); |
257 | } |
258 | |
259 | void 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 | |
270 | void 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 | |
281 | bool NetscapePlugin::isAcceleratedCompositingEnabled() |
282 | { |
283 | return controller()->isAcceleratedCompositingEnabled(); |
284 | } |
285 | |
286 | void NetscapePlugin::(bool state) |
287 | { |
288 | m_popupEnabledStates.append(state); |
289 | } |
290 | |
291 | void NetscapePlugin::() |
292 | { |
293 | ASSERT(!m_popupEnabledStates.isEmpty()); |
294 | |
295 | m_popupEnabledStates.removeLast(); |
296 | } |
297 | |
298 | void 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 | |
308 | NetscapePlugin::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 | |
318 | NetscapePlugin::Timer::~Timer() |
319 | { |
320 | } |
321 | |
322 | void 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 | |
332 | void NetscapePlugin::Timer::stop() |
333 | { |
334 | m_timer.stop(); |
335 | } |
336 | |
337 | void 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 | |
345 | uint32_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 | |
362 | void NetscapePlugin::unscheduleTimer(unsigned timerID) |
363 | { |
364 | if (auto timer = m_timers.take(timerID)) |
365 | timer->stop(); |
366 | } |
367 | |
368 | double NetscapePlugin::contentsScaleFactor() |
369 | { |
370 | return controller()->contentsScaleFactor(); |
371 | } |
372 | |
373 | String NetscapePlugin::proxiesForURL(const String& urlString) |
374 | { |
375 | return controller()->proxiesForURL(urlString); |
376 | } |
377 | |
378 | String NetscapePlugin::cookiesForURL(const String& urlString) |
379 | { |
380 | return controller()->cookiesForURL(urlString); |
381 | } |
382 | |
383 | void NetscapePlugin::setCookiesForURL(const String& urlString, const String& cookieString) |
384 | { |
385 | controller()->setCookiesForURL(urlString, cookieString); |
386 | } |
387 | |
388 | bool NetscapePlugin::getAuthenticationInfo(const ProtectionSpace& protectionSpace, String& username, String& password) |
389 | { |
390 | return controller()->getAuthenticationInfo(protectionSpace, username, password); |
391 | } |
392 | |
393 | void 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 | |
403 | void 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 | |
422 | void NetscapePlugin::setIsPlayingAudio(bool isPlayingAudio) |
423 | { |
424 | controller()->setPluginIsPlayingAudio(isPlayingAudio); |
425 | } |
426 | |
427 | NPError 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 | |
432 | NPError NetscapePlugin::NPP_Destroy(NPSavedData** savedData) |
433 | { |
434 | return m_pluginModule->pluginFuncs().destroy(&m_npp, savedData); |
435 | } |
436 | |
437 | NPError NetscapePlugin::NPP_SetWindow(NPWindow* npWindow) |
438 | { |
439 | return m_pluginModule->pluginFuncs().setwindow(&m_npp, npWindow); |
440 | } |
441 | |
442 | NPError 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 | |
447 | NPError NetscapePlugin::NPP_DestroyStream(NPStream* stream, NPReason reason) |
448 | { |
449 | return m_pluginModule->pluginFuncs().destroystream(&m_npp, stream, reason); |
450 | } |
451 | |
452 | void NetscapePlugin::NPP_StreamAsFile(NPStream* stream, const char* filename) |
453 | { |
454 | return m_pluginModule->pluginFuncs().asfile(&m_npp, stream, filename); |
455 | } |
456 | |
457 | int32_t NetscapePlugin::NPP_WriteReady(NPStream* stream) |
458 | { |
459 | return m_pluginModule->pluginFuncs().writeready(&m_npp, stream); |
460 | } |
461 | |
462 | int32_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 | |
467 | int16_t NetscapePlugin::NPP_HandleEvent(void* event) |
468 | { |
469 | return m_pluginModule->pluginFuncs().event(&m_npp, event); |
470 | } |
471 | |
472 | void NetscapePlugin::NPP_URLNotify(const char* url, NPReason reason, void* notifyData) |
473 | { |
474 | m_pluginModule->pluginFuncs().urlnotify(&m_npp, url, reason, notifyData); |
475 | } |
476 | |
477 | bool 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 | |
486 | NPError 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 | |
494 | NPError 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 | |
502 | void 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 | |
528 | void 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 | |
542 | bool 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 | |
559 | NetscapePluginStream* NetscapePlugin::streamFromID(uint64_t streamID) |
560 | { |
561 | return m_streams.get(streamID); |
562 | } |
563 | |
564 | void NetscapePlugin::stopAllStreams() |
565 | { |
566 | for (auto& stream : copyToVector(m_streams.values())) |
567 | stream->stop(NPRES_USER_BREAK); |
568 | } |
569 | |
570 | bool NetscapePlugin::() 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) |
584 | static 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 | |
614 | bool 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 | |
712 | void 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 | |
733 | void NetscapePlugin::paint(GraphicsContext& context, const IntRect& dirtyRect) |
734 | { |
735 | ASSERT(m_isStarted); |
736 | |
737 | platformPaint(context, dirtyRect); |
738 | } |
739 | |
740 | RefPtr<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 | |
762 | bool NetscapePlugin::isTransparent() |
763 | { |
764 | return m_isTransparent; |
765 | } |
766 | |
767 | bool NetscapePlugin::wantsWheelEvents() |
768 | { |
769 | return m_pluginModule->pluginQuirks().contains(PluginQuirks::WantsWheelEvents); |
770 | } |
771 | |
772 | void 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 | |
804 | void 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 | |
815 | void 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 | |
826 | void 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 | |
837 | void 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 | |
845 | void 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 | |
853 | void NetscapePlugin::streamDidReceiveResponse(uint64_t streamID, const URL& responseURL, uint32_t streamLength, |
854 | uint32_t lastModifiedTime, const String& mimeType, const String& , 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 | |
862 | void 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 | |
870 | void NetscapePlugin::streamDidFinishLoading(uint64_t streamID) |
871 | { |
872 | ASSERT(m_isStarted); |
873 | |
874 | if (NetscapePluginStream* pluginStream = streamFromID(streamID)) |
875 | pluginStream->didFinishLoading(); |
876 | } |
877 | |
878 | void 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 | |
886 | void NetscapePlugin::manualStreamDidReceiveResponse(const URL& responseURL, uint32_t streamLength, uint32_t lastModifiedTime, |
887 | const String& mimeType, const String& , 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 | |
897 | void 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 | |
906 | void NetscapePlugin::manualStreamDidFinishLoading() |
907 | { |
908 | ASSERT(m_isStarted); |
909 | ASSERT(m_shouldUseManualLoader); |
910 | ASSERT(m_manualStream); |
911 | |
912 | m_manualStream->didFinishLoading(); |
913 | } |
914 | |
915 | void 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 | |
925 | bool NetscapePlugin::handleMouseEvent(const WebMouseEvent& mouseEvent) |
926 | { |
927 | ASSERT(m_isStarted); |
928 | |
929 | return platformHandleMouseEvent(mouseEvent); |
930 | } |
931 | |
932 | bool NetscapePlugin::handleWheelEvent(const WebWheelEvent& wheelEvent) |
933 | { |
934 | ASSERT(m_isStarted); |
935 | |
936 | return platformHandleWheelEvent(wheelEvent); |
937 | } |
938 | |
939 | bool NetscapePlugin::handleMouseEnterEvent(const WebMouseEvent& mouseEvent) |
940 | { |
941 | ASSERT(m_isStarted); |
942 | |
943 | return platformHandleMouseEnterEvent(mouseEvent); |
944 | } |
945 | |
946 | bool NetscapePlugin::handleMouseLeaveEvent(const WebMouseEvent& mouseEvent) |
947 | { |
948 | ASSERT(m_isStarted); |
949 | |
950 | return platformHandleMouseLeaveEvent(mouseEvent); |
951 | } |
952 | |
953 | bool 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 | |
959 | bool NetscapePlugin::handleKeyboardEvent(const WebKeyboardEvent& keyboardEvent) |
960 | { |
961 | ASSERT(m_isStarted); |
962 | |
963 | return platformHandleKeyboardEvent(keyboardEvent); |
964 | } |
965 | |
966 | bool NetscapePlugin::handleEditingCommand(const String& /* commandName */, const String& /* argument */) |
967 | { |
968 | return false; |
969 | } |
970 | |
971 | bool NetscapePlugin::isEditingCommandEnabled(const String& /* commandName */) |
972 | { |
973 | return false; |
974 | } |
975 | |
976 | bool NetscapePlugin::shouldAllowScripting() |
977 | { |
978 | return true; |
979 | } |
980 | |
981 | bool NetscapePlugin::shouldAllowNavigationFromDrags() |
982 | { |
983 | return false; |
984 | } |
985 | |
986 | bool NetscapePlugin::handlesPageScaleFactor() const |
987 | { |
988 | return false; |
989 | } |
990 | |
991 | void NetscapePlugin::setFocus(bool hasFocus) |
992 | { |
993 | ASSERT(m_isStarted); |
994 | |
995 | platformSetFocus(hasFocus); |
996 | } |
997 | |
998 | NPObject* 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 | |
1014 | unsigned NetscapePlugin::countFindMatches(const String&, WebCore::FindOptions, unsigned) |
1015 | { |
1016 | return 0; |
1017 | } |
1018 | |
1019 | bool NetscapePlugin::findString(const String&, WebCore::FindOptions, unsigned) |
1020 | { |
1021 | return false; |
1022 | } |
1023 | |
1024 | void 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 | |
1036 | void NetscapePlugin::storageBlockingStateChanged(bool storageBlockingEnabled) |
1037 | { |
1038 | if (m_storageBlockingState != storageBlockingEnabled) { |
1039 | m_storageBlockingState = storageBlockingEnabled; |
1040 | updateNPNPrivateMode(); |
1041 | } |
1042 | } |
1043 | |
1044 | void NetscapePlugin::privateBrowsingStateChanged(bool privateBrowsingEnabled) |
1045 | { |
1046 | if (m_privateBrowsingState != privateBrowsingEnabled) { |
1047 | m_privateBrowsingState = privateBrowsingEnabled; |
1048 | updateNPNPrivateMode(); |
1049 | } |
1050 | } |
1051 | |
1052 | void 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 | |
1065 | bool 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 | |
1080 | bool NetscapePlugin::handleScroll(ScrollDirection, ScrollGranularity) |
1081 | { |
1082 | return false; |
1083 | } |
1084 | |
1085 | Scrollbar* NetscapePlugin::horizontalScrollbar() |
1086 | { |
1087 | return 0; |
1088 | } |
1089 | |
1090 | Scrollbar* NetscapePlugin::verticalScrollbar() |
1091 | { |
1092 | return 0; |
1093 | } |
1094 | |
1095 | bool NetscapePlugin::supportsSnapshotting() const |
1096 | { |
1097 | #if PLATFORM(COCOA) |
1098 | return m_pluginModule->pluginQuirks().contains(PluginQuirks::SupportsSnapshotting); |
1099 | #endif |
1100 | return false; |
1101 | } |
1102 | |
1103 | RefPtr<WebCore::SharedBuffer> NetscapePlugin::liveResourceData() const |
1104 | { |
1105 | return nullptr; |
1106 | } |
1107 | |
1108 | IntPoint NetscapePlugin::convertToRootView(const IntPoint& pointInPluginCoordinates) const |
1109 | { |
1110 | return m_pluginToRootViewTransform.mapPoint(pointInPluginCoordinates); |
1111 | } |
1112 | |
1113 | bool 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 | |
1122 | void NetscapePlugin::mutedStateChanged(bool muted) |
1123 | { |
1124 | NPBool value = muted; |
1125 | NPP_SetValue(NPNVmuteAudioBool, &value); |
1126 | } |
1127 | |
1128 | #if !PLATFORM(COCOA) |
1129 | |
1130 | void NetscapePlugin::windowFocusChanged(bool) |
1131 | { |
1132 | } |
1133 | |
1134 | void NetscapePlugin::windowVisibilityChanged(bool) |
1135 | { |
1136 | } |
1137 | |
1138 | #endif |
1139 | |
1140 | } // namespace WebKit |
1141 | |
1142 | #endif // ENABLE(NETSCAPE_PLUGIN_API) |
1143 | |