blob: 63801e1cabacbaaf101a038ace50526f03f1afc9 [file] [log] [blame]
/*
* Copyright (C) 2008-2017 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. ``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
* 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.
*/
#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
#import "WebHostedNetscapePluginView.h"
#import "HostedNetscapePluginStream.h"
#import "NetscapePluginInstanceProxy.h"
#import "NetscapePluginHostManager.h"
#import "NetscapePluginHostProxy.h"
#import "WebTextInputWindowController.h"
#import "WebFrameInternal.h"
#import "WebView.h"
#import "WebViewInternal.h"
#import "WebUIDelegate.h"
#import <CoreFoundation/CoreFoundation.h>
#import <JavaScriptCore/InitializeThreading.h>
#import <WebCore/BridgeJSC.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoaderTypes.h>
#import <WebCore/FrameView.h>
#import <WebCore/HTMLPlugInElement.h>
#import <WebCore/RenderEmbeddedObject.h>
#import <WebCore/ResourceError.h>
#import <WebCore/WebCoreCALayerExtras.h>
#import <WebCore/runtime_root.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <wtf/Assertions.h>
#import <wtf/MainThread.h>
#import <wtf/ObjCRuntimeExtras.h>
#import <wtf/RunLoop.h>
using namespace WebCore;
using namespace WebKit;
namespace WebKit {
class SoftwareCARenderer {
WTF_MAKE_FAST_ALLOCATED;
public:
explicit SoftwareCARenderer(uint32_t contextID)
: m_context { [CAContext localContext] }
, m_renderContext { CARenderCGNew(0) }
{
[m_context setLayer:[CALayer _web_renderLayerWithContextID:contextID]];
}
~SoftwareCARenderer()
{
CARenderCGDestroy(m_renderContext);
}
void render(CGContextRef context, CGRect rect)
{
CARenderUpdate* update = CARenderUpdateBegin(nullptr, 0, CACurrentMediaTime(), nullptr, 0, &rect);
CARenderUpdateAddContext(update, [m_context renderContext]);
CARenderUpdateAddRect(update, &rect);
CARenderCGRender(m_renderContext, update, context);
CARenderUpdateFinish(update);
}
private:
RetainPtr<CAContext> m_context;
CARenderCGContext* m_renderContext;
};
}
extern "C" {
#include "WebKitPluginClientServer.h"
#include "WebKitPluginHost.h"
}
#if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
@interface NSWindow (Details)
- (BOOL)_hostsLayersInWindowServer;
@end
#endif
@implementation WebHostedNetscapePluginView
+ (void)initialize
{
#if !PLATFORM(IOS_FAMILY)
JSC::initializeThreading();
RunLoop::initializeMainRunLoop();
#endif
sendUserChangeNotifications();
}
- (id)initWithFrame:(NSRect)frame
pluginPackage:(WebNetscapePluginPackage *)pluginPackage
URL:(NSURL *)URL
baseURL:(NSURL *)baseURL
MIMEType:(NSString *)MIME
attributeKeys:(NSArray *)keys
attributeValues:(NSArray *)values
loadManually:(BOOL)loadManually
element:(RefPtr<WebCore::HTMLPlugInElement>&&)element
{
self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:WTFMove(element)];
if (!self)
return nil;
return self;
}
- (void)handleMouseMoved:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseMoved);
}
- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values
{
ASSERT(!_attributeKeys);
ASSERT(!_attributeValues);
_attributeKeys = adoptNS([keys copy]);
_attributeValues = adoptNS([values copy]);
}
- (BOOL)windowHostsLayersInWindowServer
{
#if HAVE(OUT_OF_PROCESS_LAYER_HOSTING)
return [[[self webView] window] _hostsLayersInWindowServer];
#else
return false;
#endif
}
- (BOOL)createPlugin
{
ASSERT(!_proxy);
NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()];
BOOL acceleratedCompositingEnabled = false;
acceleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
_hostsLayersInWindowServer = [self windowHostsLayersInWindowServer];
_proxy = NetscapePluginHostManager::singleton().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(),
_mode == NP_FULL, _isPrivateBrowsingEnabled, acceleratedCompositingEnabled, _hostsLayersInWindowServer);
if (!_proxy)
return NO;
if (_proxy->rendererType() == UseSoftwareRenderer)
_softwareRenderer = makeUnique<SoftwareCARenderer>(_proxy->renderContextID());
else
[self createPluginLayer];
// Update the window frame.
_proxy->windowFrameChanged([[self window] frame]);
return YES;
}
- (void)createPluginLayer
{
BOOL acceleratedCompositingEnabled = false;
acceleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
_pluginLayer = [CALayer _web_renderLayerWithContextID:_proxy->renderContextID()];
if (acceleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) {
// FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView.
// Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry
// in order to get the coordinate system right.
RetainPtr<CALayer> realPluginLayer = WTFMove(_pluginLayer);
_pluginLayer = adoptNS([[CALayer alloc] init]);
_pluginLayer.get().bounds = realPluginLayer.get().bounds;
_pluginLayer.get().geometryFlipped = YES;
realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
[_pluginLayer.get() addSublayer:realPluginLayer.get()];
// Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc()
// for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033
core([self webFrame])->view()->enterCompositingMode();
[self element]->invalidateStyleAndLayerComposition();
} else
self.wantsLayer = YES;
}
- (void)setHostsLayersInWindowServer:(bool)hostsLayersInWindowServer
{
if (_proxy->rendererType() == UseSoftwareRenderer)
return;
RetainPtr<CALayer> currentSuperlayer = [_pluginLayer superlayer];
_hostsLayersInWindowServer = hostsLayersInWindowServer;
[self createPluginLayer];
[self setLayer:currentSuperlayer.get()];
}
// FIXME: This method is an ideal candidate to move up to the base class
- (CALayer *)pluginLayer
{
return _pluginLayer.get();
}
- (BOOL)getFormValue:(NSString **)value
{
// FIXME: We cannot implement this method for now because NPP_GetValue
// is not currently exposed by the plugin host. Once we have an IPC routine
// which allows us to invoke NPP_GetValue, we could implement this method.
return false;
}
- (void)setLayer:(CALayer *)newLayer
{
// FIXME: This should use the same implementation as WebNetscapePluginView (and move to the base class).
[super setLayer:newLayer];
if (_pluginLayer)
[newLayer addSublayer:_pluginLayer.get()];
}
- (void)privateBrowsingModeDidChange
{
if (_proxy)
_proxy->privateBrowsingModeDidChange(_isPrivateBrowsingEnabled);
}
- (void)loadStream
{
}
- (void)updateAndSetWindow
{
if (!_proxy)
return;
// The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor
// of 1. For non-1.0 scale factors this assumption is false.
NSView *windowContentView = [[self window] contentView];
NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView];
NSRect visibleRectInWindow;
// Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when
// moved to a background tab. We don't do this for Core Graphics plug-ins as
// older versions of Flash have historical WebKit-specific code that isn't
// compatible with this behavior.
BOOL shouldClipOutPlugin = _pluginLayer && [self shouldClipOutPlugin];
if (!shouldClipOutPlugin)
visibleRectInWindow = [self actualVisibleRectInWindow];
else
visibleRectInWindow = NSZeroRect;
// Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
float borderViewHeight = [[self currentWindow] frame].size.height;
boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
if (!shouldClipOutPlugin)
visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
_previousSize = boundsInWindow.size;
_proxy->resize(boundsInWindow, visibleRectInWindow);
bool shouldHostLayersInWindowServer = [self windowHostsLayersInWindowServer];
if (_hostsLayersInWindowServer != shouldHostLayersInWindowServer)
_proxy->setShouldHostLayersInWindowServer(shouldHostLayersInWindowServer);
}
- (void)windowFocusChanged:(BOOL)hasFocus
{
if (_proxy)
_proxy->windowFocusChanged(hasFocus);
}
- (BOOL)shouldStop
{
if (!_proxy)
return YES;
return _proxy->shouldStop();
}
- (void)destroyPlugin
{
if (_proxy) {
_softwareRenderer = nullptr;
_proxy->destroy();
_proxy = nullptr;
}
_pluginLayer = 0;
}
- (void)startTimers
{
if (_proxy)
_proxy->startTimers(_isCompletelyObscured);
}
- (void)stopTimers
{
if (_proxy)
_proxy->stopTimers();
}
- (void)focusChanged
{
if (_proxy)
_proxy->focusChanged(_hasFocus);
}
- (void)windowFrameDidChange:(NSNotification *)notification
{
if (_proxy && [self window])
_proxy->windowFrameChanged([[self window] frame]);
}
- (void)addWindowObservers
{
[super addWindowObservers];
ASSERT([self window]);
NSWindow *window = [self window];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(windowFrameDidChange:)
name:NSWindowDidMoveNotification object:window];
[notificationCenter addObserver:self selector:@selector(windowFrameDidChange:)
name:NSWindowDidResizeNotification object:window];
if (_proxy)
_proxy->windowFrameChanged([window frame]);
[self updateAndSetWindow];
}
- (void)removeWindowObservers
{
[super removeWindowObservers];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:NSWindowDidMoveNotification object:nil];
[notificationCenter removeObserver:self name:NSWindowDidResizeNotification object:nil];
}
- (void)mouseDown:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseDown);
}
- (void)mouseUp:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseUp);
}
- (void)mouseDragged:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseDragged);
}
- (void)handleMouseEntered:(NSEvent *)event
{
// Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one.
[[NSCursor arrowCursor] set];
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseEntered);
}
- (void)handleMouseExited:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->mouseEvent(self, event, NPCocoaEventMouseExited);
// Set cursor back to arrow cursor. Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the
// current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
// FIXME: This should be job of plugin host, see <rdar://problem/7654434>.
[[NSCursor arrowCursor] set];
}
- (void)scrollWheel:(NSEvent *)event
{
bool processedEvent = false;
if (_isStarted && _proxy)
processedEvent = _proxy->wheelEvent(self, event);
if (!processedEvent)
[super scrollWheel:event];
}
- (NSTextInputContext *)inputContext
{
return [[WebTextInputWindowController sharedTextInputWindowController] inputContext];
}
- (void)keyDown:(NSEvent *)event
{
if (!_isStarted || !_proxy)
return;
NSString *string = nil;
if ([[WebTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event string:&string]) {
if (string)
_proxy->insertText(string);
return;
}
_proxy->keyEvent(self, event, NPCocoaEventKeyDown);
}
- (void)keyUp:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->keyEvent(self, event, NPCocoaEventKeyUp);
}
- (void)flagsChanged:(NSEvent *)event
{
if (_isStarted && _proxy)
_proxy->flagsChanged(event);
}
- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
{
if (_isStarted && _proxy)
_proxy->syntheticKeyDownWithCommandModifier(keyCode, character);
}
- (void)pluginHostDied
{
if (is<RenderEmbeddedObject>(_element->renderer()))
downcast<RenderEmbeddedObject>(*_element->renderer()).setPluginUnavailabilityReason(RenderEmbeddedObject::PluginCrashed);
_pluginLayer = nil;
_proxy = nullptr;
// No need for us to be layer backed anymore
self.wantsLayer = NO;
[self invalidatePluginContentRect:[self bounds]];
}
- (void)drawRect:(NSRect)rect
{
if (_cachedSnapshot) {
NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] };
[_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositingOperationSourceOver fraction:1];
return;
}
if (_proxy) {
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (_softwareRenderer) {
if ([NSGraphicsContext currentContextDrawingToScreen]) {
_softwareRenderer->render((CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(rect));
_proxy->didDraw();
} else
_proxy->print(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
} else if (_snapshotting && [self supportsSnapshotting]) {
_proxy->snapshot(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
}
ALLOW_DEPRECATED_DECLARATIONS_END
return;
}
}
- (RefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(Ref<JSC::Bindings::RootObject>&&)rootObject
{
if (!_proxy)
return nullptr;
return _proxy->createBindingsInstance(WTFMove(rootObject));
}
- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
{
ASSERT(_loadManually);
if (!_proxy)
return;
ASSERT(!_proxy->manualStream());
_proxy->setManualStream(HostedNetscapePluginStream::create(_proxy.get(), &core([self webFrame])->loader()));
_proxy->manualStream()->startStreamWithResponse(response);
}
- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
{
ASSERT(_loadManually);
if (!_proxy)
return;
if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
manualStream->didReceiveData(0, static_cast<const char*>([data bytes]), [data length]);
}
- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
{
ASSERT(_loadManually);
if (!_proxy)
return;
if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
manualStream->didFail(0, error);
}
- (void)pluginViewFinishedLoading:(NSView *)pluginView
{
ASSERT(_loadManually);
if (!_proxy)
return;
if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
manualStream->didFinishLoading(0);
}
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
{
if (_isStarted && _proxy)
_proxy->webFrameDidFinishLoadWithReason(webFrame, reason);
}
- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
{
NPReason reason = NPRES_DONE;
if (error)
reason = HostedNetscapePluginStream::reasonForError(error);
[self webFrame:webFrame didFinishLoadWithReason:reason];
}
@end
#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)