| /* |
| * 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 "NetscapePluginHostManager.h" |
| #import "NetscapePluginHostProxy.h" |
| #import "NetscapePluginInstanceProxy.h" |
| #import "WebFrameInternal.h" |
| #import "WebTextInputWindowController.h" |
| #import "WebUIDelegate.h" |
| #import "WebView.h" |
| #import "WebViewInternal.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/WebCoreJITOperations.h> |
| #import <WebCore/runtime_root.h> |
| #import <pal/spi/cocoa/QuartzCoreSPI.h> |
| #import <wtf/Assertions.h> |
| #import <wtf/MainThread.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" { |
| #import "WebKitPluginClientServer.h" |
| #import "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::initialize(); |
| WTF::initializeMainThread(); |
| WebCore::populateJITOperations(); |
| #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) { |
| if (_softwareRenderer) { |
| if ([NSGraphicsContext currentContextDrawingToScreen]) { |
| _softwareRenderer->render([[NSGraphicsContext currentContext] CGContext], NSRectToCGRect(rect)); |
| _proxy->didDraw(); |
| } else |
| _proxy->print([[NSGraphicsContext currentContext] CGContext], [self bounds].size.width, [self bounds].size.height); |
| } else if (_snapshotting && [self supportsSnapshotting]) { |
| _proxy->snapshot([[NSGraphicsContext currentContext] CGContext], [self bounds].size.width, [self bounds].size.height); |
| } |
| |
| 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 uint8_t*>([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) |