NetscapePluginMac.mm [plain text]
/*
* Copyright (C) 2010-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "NetscapePlugin.h"
#if ENABLE(NETSCAPE_PLUGIN_API)
#import "LayerTreeContext.h"
#import "NetscapeBrowserFuncs.h"
#import "PluginController.h"
#import "WebKeyboardEvent.h"
#import "WebMouseEvent.h"
#import "WebWheelEvent.h"
#import <Carbon/Carbon.h>
#import <WebCore/GraphicsContext.h>
#import <WebCore/NotImplemented.h>
#import <objc/runtime.h>
#import <pal/spi/mac/HIToolboxSPI.h>
#import <pal/spi/mac/NSMenuSPI.h>
#import <wtf/MachSendRight.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/text/StringView.h>
namespace WebKit {
using namespace WebCore;
NPError NetscapePlugin::setDrawingModel(NPDrawingModel drawingModel)
{
// The drawing model can only be set from NPP_New.
if (!m_inNPPNew)
return NPERR_GENERIC_ERROR;
switch (drawingModel) {
case NPDrawingModelCoreGraphics:
case NPDrawingModelCoreAnimation:
m_drawingModel = drawingModel;
break;
default:
return NPERR_GENERIC_ERROR;
}
return NPERR_NO_ERROR;
}
NPError NetscapePlugin::setEventModel(NPEventModel eventModel)
{
// The event model can only be set from NPP_New.
if (!m_inNPPNew)
return NPERR_GENERIC_ERROR;
switch (eventModel) {
case NPEventModelCocoa:
m_eventModel = eventModel;
break;
default:
return NPERR_GENERIC_ERROR;
}
return NPERR_NO_ERROR;
}
bool NetscapePlugin::getScreenTransform(NPCoordinateSpace sourceSpace, AffineTransform& transform)
{
ASSERT(transform.isIdentity());
switch (sourceSpace) {
case NPCoordinateSpacePlugin: {
transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y());
transform.translate(m_viewFrameInWindowCoordinates.x(), m_viewFrameInWindowCoordinates.height() + m_viewFrameInWindowCoordinates.y());
transform.flipY();
transform *= m_pluginToRootViewTransform;
return true;
}
case NPCoordinateSpaceWindow: {
transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.y());
return true;
}
case NPCoordinateSpaceFlippedWindow: {
transform.translate(m_windowFrameInScreenCoordinates.x(), m_windowFrameInScreenCoordinates.height() + m_windowFrameInScreenCoordinates.y());
transform.flipY();
return true;
}
case NPCoordinateSpaceScreen: {
// Nothing to do.
return true;
}
case NPCoordinateSpaceFlippedScreen: {
double screenHeight = [(NSScreen *)[[NSScreen screens] objectAtIndex:0] frame].size.height;
transform.translate(0, screenHeight);
transform.flipY();
return true;
}
default:
return false;
}
}
NPBool NetscapePlugin::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double& destX, double& destY, NPCoordinateSpace destSpace)
{
AffineTransform sourceTransform;
if (!getScreenTransform(sourceSpace, sourceTransform))
return false;
AffineTransform destTransform;
if (!getScreenTransform(destSpace, destTransform))
return false;
if (auto inverse = destTransform.inverse()) {
AffineTransform transform = inverse.value() * sourceTransform;
FloatPoint destinationPoint = transform.mapPoint(FloatPoint(sourceX, sourceY));
destX = destinationPoint.x();
destY = destinationPoint.y();
return true;
}
return false;
}
NPError NetscapePlugin::popUpContextMenu(NPMenu* npMenu)
{
if (!m_currentMouseEvent)
return NPERR_GENERIC_ERROR;
double screenX, screenY;
if (!convertPoint(m_currentMouseEvent->data.mouse.pluginX, m_currentMouseEvent->data.mouse.pluginY, NPCoordinateSpacePlugin, screenX, screenY, NPCoordinateSpaceScreen))
ASSERT_NOT_REACHED();
_NSPopUpCarbonMenu3((__bridge NSMenu *)npMenu, nil, nil, NSMakePoint(screenX, screenY), -1, nil, 0, nil, NSPopUpMenuTypeContext, nil);
return NPERR_NO_ERROR;
}
const MachSendRight& NetscapePlugin::compositingRenderServerPort()
{
#if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
if (m_layerHostingMode == LayerHostingMode::OutOfProcess) {
static NeverDestroyed<MachSendRight> sendRight;
return sendRight;
}
#endif
return controller()->compositingRenderServerPort();
}
static void NSException_release(id, SEL)
{
// Do nothing.
}
void NetscapePlugin::platformPreInitialize()
{
if (m_pluginModule->pluginQuirks().contains(PluginQuirks::LeakAllThrownNSExceptions)) {
// Patch -[NSException release] to not release the object.
static dispatch_once_t once;
dispatch_once(&once, ^{
auto exceptionClass = [NSException class];
auto releaseSelector = sel_registerName("release");
auto exceptionReleaseMethod = class_getInstanceMethod(exceptionClass, releaseSelector);
class_replaceMethod(exceptionClass, releaseSelector, reinterpret_cast<IMP>(NSException_release), method_getTypeEncoding(exceptionReleaseMethod));
});
}
}
bool NetscapePlugin::platformPostInitialize()
{
if (m_drawingModel == static_cast<NPDrawingModel>(-1))
m_drawingModel = NPDrawingModelCoreGraphics;
if (m_eventModel == static_cast<NPEventModel>(-1))
m_eventModel = NPEventModelCocoa;
updatePluginLayer();
return true;
}
void NetscapePlugin::platformDestroy()
{
}
bool NetscapePlugin::platformInvalidate(const IntRect&)
{
// NPN_InvalidateRect is just a no-op in the Core Animation drawing model.
if (m_drawingModel == NPDrawingModelCoreAnimation)
return true;
return false;
}
void NetscapePlugin::platformGeometryDidChange()
{
switch (m_eventModel) {
case NPEventModelCocoa:
// Nothing to do
break;
default:
ASSERT_NOT_REACHED();
}
}
void NetscapePlugin::platformVisibilityDidChange()
{
// FIXME: Implement this. <http://webkit.org/b/44368>.
notImplemented();
}
static inline NPCocoaEvent initializeEvent(NPCocoaEventType type)
{
NPCocoaEvent event;
event.type = type;
event.version = 0;
return event;
}
void NetscapePlugin::platformPaint(GraphicsContext& context, const IntRect& dirtyRect, bool isSnapshot)
{
CGContextRef platformContext = context.platformContext();
switch (m_eventModel) {
case NPEventModelCocoa: {
// Don't send draw events when we're using the Core Animation drawing model.
if (!isSnapshot && m_drawingModel == NPDrawingModelCoreAnimation)
return;
NPCocoaEvent event = initializeEvent(NPCocoaEventDrawRect);
event.data.draw.context = platformContext;
event.data.draw.x = dirtyRect.x();
event.data.draw.y = dirtyRect.y();
event.data.draw.width = dirtyRect.width();
event.data.draw.height = dirtyRect.height();
NPP_HandleEvent(&event);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
static uint32_t modifierFlags(const WebEvent& event)
{
uint32_t modifiers = 0;
if (event.shiftKey())
modifiers |= NSEventModifierFlagShift;
if (event.controlKey())
modifiers |= NSEventModifierFlagControl;
if (event.altKey())
modifiers |= NSEventModifierFlagOption;
if (event.metaKey())
modifiers |= NSEventModifierFlagCommand;
return modifiers;
}
static int32_t buttonNumber(WebMouseEvent::Button button)
{
switch (button) {
case WebMouseEvent::NoButton:
case WebMouseEvent::LeftButton:
return 0;
case WebMouseEvent::RightButton:
return 1;
case WebMouseEvent::MiddleButton:
return 2;
}
ASSERT_NOT_REACHED();
return -1;
}
static void fillInCocoaEventFromMouseEvent(NPCocoaEvent& event, const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates)
{
event.data.mouse.modifierFlags = modifierFlags(mouseEvent);
event.data.mouse.pluginX = eventPositionInPluginCoordinates.x();
event.data.mouse.pluginY = eventPositionInPluginCoordinates.y();
event.data.mouse.buttonNumber = buttonNumber(mouseEvent.button());
event.data.mouse.clickCount = mouseEvent.clickCount();
event.data.mouse.deltaX = mouseEvent.deltaX();
event.data.mouse.deltaY = mouseEvent.deltaY();
event.data.mouse.deltaZ = mouseEvent.deltaZ();
}
static NPCocoaEvent initializeMouseEvent(const WebMouseEvent& mouseEvent, const WebCore::IntPoint& eventPositionInPluginCoordinates)
{
NPCocoaEventType eventType;
switch (mouseEvent.type()) {
case WebEvent::MouseDown:
eventType = NPCocoaEventMouseDown;
break;
case WebEvent::MouseUp:
eventType = NPCocoaEventMouseUp;
break;
case WebEvent::MouseMove:
if (mouseEvent.button() == WebMouseEvent::NoButton)
eventType = NPCocoaEventMouseMoved;
else
eventType = NPCocoaEventMouseDragged;
break;
default:
ASSERT_NOT_REACHED();
return NPCocoaEvent();
}
NPCocoaEvent event = initializeEvent(eventType);
fillInCocoaEventFromMouseEvent(event, mouseEvent, eventPositionInPluginCoordinates);
return event;
}
bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& mouseEvent)
{
IntPoint eventPositionInPluginCoordinates;
if (!convertFromRootView(mouseEvent.position(), eventPositionInPluginCoordinates))
return true;
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeMouseEvent(mouseEvent, eventPositionInPluginCoordinates);
NPCocoaEvent* previousMouseEvent = m_currentMouseEvent;
m_currentMouseEvent = &event;
// Protect against NPP_HandleEvent causing the plug-in to be destroyed, since we
// access m_currentMouseEvent afterwards.
Ref<NetscapePlugin> protect(*this);
NPP_HandleEvent(&event);
m_currentMouseEvent = previousMouseEvent;
// Some plug-ins return false even if the mouse event has been handled.
// This leads to bugs such as <rdar://problem/9167611>. Work around this
// by always returning true.
return true;
}
default:
ASSERT_NOT_REACHED();
}
return false;
}
bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent& wheelEvent)
{
switch (m_eventModel) {
case NPEventModelCocoa: {
IntPoint eventPositionInPluginCoordinates;
if (!convertFromRootView(wheelEvent.position(), eventPositionInPluginCoordinates))
return true;
NPCocoaEvent event = initializeEvent(NPCocoaEventScrollWheel);
event.data.mouse.modifierFlags = modifierFlags(wheelEvent);
event.data.mouse.pluginX = eventPositionInPluginCoordinates.x();
event.data.mouse.pluginY = eventPositionInPluginCoordinates.y();
event.data.mouse.buttonNumber = 0;
event.data.mouse.clickCount = 0;
event.data.mouse.deltaX = wheelEvent.delta().width();
event.data.mouse.deltaY = wheelEvent.delta().height();
event.data.mouse.deltaZ = 0;
return NPP_HandleEvent(&event);
}
default:
ASSERT_NOT_REACHED();
}
return false;
}
bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& mouseEvent)
{
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeEvent(NPCocoaEventMouseEntered);
fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint());
return NPP_HandleEvent(&event);
}
default:
ASSERT_NOT_REACHED();
}
return false;
}
bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& mouseEvent)
{
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeEvent(NPCocoaEventMouseExited);
fillInCocoaEventFromMouseEvent(event, mouseEvent, IntPoint());
return NPP_HandleEvent(&event);
}
default:
ASSERT_NOT_REACHED();
}
return false;
}
static unsigned modifierFlags(const WebKeyboardEvent& keyboardEvent)
{
unsigned modifierFlags = 0;
if (keyboardEvent.capsLockKey())
modifierFlags |= NSEventModifierFlagCapsLock;
if (keyboardEvent.shiftKey())
modifierFlags |= NSEventModifierFlagShift;
if (keyboardEvent.controlKey())
modifierFlags |= NSEventModifierFlagControl;
if (keyboardEvent.altKey())
modifierFlags |= NSEventModifierFlagOption;
if (keyboardEvent.metaKey())
modifierFlags |= NSEventModifierFlagCommand;
return modifierFlags;
}
static bool isFlagsChangedEvent(const WebKeyboardEvent& keyboardEvent)
{
switch (keyboardEvent.nativeVirtualKeyCode()) {
case 54: // Right Command
case 55: // Left Command
case 57: // Capslock
case 56: // Left Shift
case 60: // Right Shift
case 58: // Left Alt
case 61: // Right Alt
case 59: // Left Ctrl
case 62: // Right Ctrl
return true;
}
return false;
}
static NPNSString *convertToNPNNString(const String& string)
{
CFTypeRef releasedString = string.createCFString().autorelease();
return static_cast<NPNSString*>(const_cast<void*>(releasedString));
}
static NPCocoaEvent initializeKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
{
NPCocoaEventType eventType;
if (isFlagsChangedEvent(keyboardEvent))
eventType = NPCocoaEventFlagsChanged;
else {
switch (keyboardEvent.type()) {
case WebEvent::KeyDown:
eventType = NPCocoaEventKeyDown;
break;
case WebEvent::KeyUp:
eventType = NPCocoaEventKeyUp;
break;
default:
ASSERT_NOT_REACHED();
return NPCocoaEvent();
}
}
NPCocoaEvent event = initializeEvent(eventType);
event.data.key.modifierFlags = modifierFlags(keyboardEvent);
event.data.key.characters = convertToNPNNString(keyboardEvent.text());
event.data.key.charactersIgnoringModifiers = convertToNPNNString(keyboardEvent.unmodifiedText());
event.data.key.isARepeat = keyboardEvent.isAutoRepeat();
event.data.key.keyCode = keyboardEvent.nativeVirtualKeyCode();
return event;
}
bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent& keyboardEvent)
{
bool handled = false;
switch (m_eventModel) {
case NPEventModelCocoa: {
if (keyboardEvent.type() == WebEvent::KeyDown) {
m_hasHandledAKeyDownEvent = true;
if (!m_pluginWantsLegacyCocoaTextInput && m_isComplexTextInputEnabled && !keyboardEvent.isAutoRepeat()) {
// When complex text is enabled in the new model, the plug-in should never
// receive any key down or key up events until the composition is complete.
m_ignoreNextKeyUpEventCounter++;
return true;
}
} else if (keyboardEvent.type() == WebEvent::KeyUp && m_ignoreNextKeyUpEventCounter) {
m_ignoreNextKeyUpEventCounter--;
return true;
}
NPCocoaEvent event = initializeKeyboardEvent(keyboardEvent);
int16_t returnValue = NPP_HandleEvent(&event);
handled = returnValue;
if (!m_pluginWantsLegacyCocoaTextInput) {
if (event.type == NPCocoaEventKeyDown && returnValue == kNPEventStartIME) {
if (!keyboardEvent.isAutoRepeat())
m_ignoreNextKeyUpEventCounter++;
setComplexTextInputEnabled(true);
}
}
break;
}
default:
ASSERT_NOT_REACHED();
}
// Most plug-ins simply return true for all keyboard events, even those that aren't handled.
// This leads to bugs such as <rdar://problem/8740926>. We work around this by returning false
// if the keyboard event has the command modifier pressed.
// However, for command-A (the shortcurt for 'Select All' we will always return true, since we don't
// want the entire page to be selected if the focus is in a plug-in text field (see <rdar://problem/9309903>).
if (keyboardEvent.metaKey()) {
if (keyboardEvent.text() == "a")
return true;
return false;
}
return handled;
}
void NetscapePlugin::platformSetFocus(bool hasFocus)
{
m_pluginHasFocus = hasFocus;
pluginFocusOrWindowFocusChanged();
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeEvent(NPCocoaEventFocusChanged);
event.data.focus.hasFocus = hasFocus;
NPP_HandleEvent(&event);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
bool NetscapePlugin::wantsPluginRelativeNPWindowCoordinates()
{
return true;
}
void NetscapePlugin::windowFocusChanged(bool hasFocus)
{
m_windowHasFocus = hasFocus;
pluginFocusOrWindowFocusChanged();
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeEvent(NPCocoaEventWindowFocusChanged);
event.data.focus.hasFocus = hasFocus;
NPP_HandleEvent(&event);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
void NetscapePlugin::windowAndViewFramesChanged(const IntRect& windowFrameInScreenCoordinates, const IntRect& viewFrameInWindowCoordinates)
{
m_windowFrameInScreenCoordinates = windowFrameInScreenCoordinates;
m_viewFrameInWindowCoordinates = viewFrameInWindowCoordinates;
switch (m_eventModel) {
case NPEventModelCocoa:
// Nothing to do.
break;
default:
ASSERT_NOT_REACHED();
}
}
void NetscapePlugin::windowVisibilityChanged(bool visible)
{
if (visible)
callSetWindow();
else
callSetWindowInvisible();
}
uint64_t NetscapePlugin::pluginComplexTextInputIdentifier() const
{
// Just return a dummy value; this is only called for in-process plug-ins, which we don't support on Mac.
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
}
void NetscapePlugin::sendComplexTextInput(const String& textInput)
{
if (!m_pluginWantsLegacyCocoaTextInput) {
// In the updated Cocoa text input spec, text input is disabled when the text input string has been sent
// by the UI process. Since the UI process has also updated its state, we can just reset the variable here
// instead of calling setComplexTextInputEnabled.
m_isComplexTextInputEnabled = false;
// The UI process can also disable text input by sending an empty input string. In this case, we don't want
// to send it to the plug-in.
if (textInput.isNull())
return;
}
switch (m_eventModel) {
case NPEventModelCocoa: {
NPCocoaEvent event = initializeEvent(NPCocoaEventTextInput);
event.data.text.text = convertToNPNNString(textInput);
NPP_HandleEvent(&event);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
void NetscapePlugin::setLayerHostingMode(LayerHostingMode layerHostingMode)
{
m_layerHostingMode = layerHostingMode;
// Tell the plug-in about the new compositing render server port. If it returns OK we'll ask it again for a new layer.
mach_port_t port = NetscapePlugin::compositingRenderServerPort().sendRight();
if (NPP_SetValue(static_cast<NPNVariable>(WKNVCALayerRenderServerPort), &port) != NPERR_NO_ERROR)
return;
m_pluginLayer = nullptr;
updatePluginLayer();
}
void NetscapePlugin::pluginFocusOrWindowFocusChanged()
{
bool pluginHasFocusAndWindowHasFocus = m_pluginHasFocus && m_windowHasFocus;
controller()->pluginFocusOrWindowFocusChanged(pluginHasFocusAndWindowHasFocus);
// In the updated Cocoa text input spec, the plug-in will enable complex text input
// by returning kNPEventStartIME from it's NPCocoaEventKeyDown handler.
if (!m_pluginWantsLegacyCocoaTextInput)
return;
// In the old model, if the plug-in is focused, enable complex text input.
setComplexTextInputEnabled(pluginHasFocusAndWindowHasFocus);
}
void NetscapePlugin::setComplexTextInputEnabled(bool complexTextInputEnabled)
{
if (m_isComplexTextInputEnabled == complexTextInputEnabled)
return;
m_isComplexTextInputEnabled = complexTextInputEnabled;
PluginComplexTextInputState complexTextInputState = PluginComplexTextInputDisabled;
if (m_isComplexTextInputEnabled)
complexTextInputState = m_pluginWantsLegacyCocoaTextInput ? PluginComplexTextInputEnabledLegacy : PluginComplexTextInputEnabled;
controller()->setComplexTextInputState(complexTextInputState);
}
PlatformLayer* NetscapePlugin::pluginLayer()
{
return static_cast<PlatformLayer*>(m_pluginLayer.get());
}
static void makeCGLPresentLayerOpaque(CALayer *pluginRootLayer)
{
// We look for a layer that's the only sublayer of the root layer that is an instance
// of the CGLPresentLayer class which in turn is a subclass of CAOpenGLLayer and make
// it opaque if all these conditions hold.
NSArray *sublayers = [pluginRootLayer sublayers];
if ([sublayers count] != 1)
return;
Class cglPresentLayerClass = NSClassFromString(@"CGLPresentLayer");
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (![cglPresentLayerClass isSubclassOfClass:[CAOpenGLLayer class]])
return;
ALLOW_DEPRECATED_DECLARATIONS_END
CALayer *layer = [sublayers objectAtIndex:0];
if (![layer isKindOfClass:cglPresentLayerClass])
return;
[layer setOpaque:YES];
}
void NetscapePlugin::updatePluginLayer()
{
if (m_drawingModel != NPDrawingModelCoreAnimation)
return;
void* value = 0;
// Get the Core Animation layer.
if (NPP_GetValue(NPPVpluginCoreAnimationLayer, &value) != NPERR_NO_ERROR)
return;
if (!value)
return;
ASSERT(!m_pluginLayer);
// The original Core Animation drawing model required that plug-ins pass a retained layer
// to the browser, which the browser would then adopt. However, the final spec changed this
// (See https://wiki.mozilla.org/NPAPI:CoreAnimationDrawingModel for more information)
// after a version of WebKit1 with the original implementation had shipped, but that now means
// that any plug-ins that expect the WebKit1 behavior would leak the CALayer.
// For plug-ins that we know return retained layers, we have the ReturnsRetainedCoreAnimationLayer
// plug-in quirk. Plug-ins can also check for whether the browser expects a non-retained layer to
// be returned by using NPN_GetValue and pass the WKNVExpectsNonretainedLayer parameter.
// https://bugs.webkit.org/show_bug.cgi?id=58282 describes the bug where WebKit expects retained layers.
if (m_pluginReturnsNonretainedLayer)
m_pluginLayer = (__bridge CALayer *)value;
else
m_pluginLayer = adoptNS((__bridge CALayer *)value);
if (m_pluginModule->pluginQuirks().contains(PluginQuirks::MakeOpaqueUnlessTransparentSilverlightBackgroundAttributeExists) &&
!m_isTransparent)
makeCGLPresentLayerOpaque(m_pluginLayer.get());
}
} // namespace WebKit
#endif // ENABLE(NETSCAPE_PLUGIN_API)