| /* |
| * Copyright (C) 2008 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 <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/WebCoreObjCExtras.h> |
| #import <WebCore/runtime_root.h> |
| #import <runtime/InitializeThreading.h> |
| #import <wtf/Assertions.h> |
| #import <wtf/Threading.h> |
| |
| using namespace WebCore; |
| using namespace WebKit; |
| |
| extern "C" { |
| #include "WebKitPluginClientServer.h" |
| #include "WebKitPluginHost.h" |
| } |
| |
| @implementation WebHostedNetscapePluginView |
| |
| + (void)initialize |
| { |
| JSC::initializeThreading(); |
| WTF::initializeMainThreadToProcessMainThread(); |
| #ifndef BUILDING_ON_TIGER |
| WebCoreObjCFinalizeOnMainThread(self); |
| #endif |
| WKSendUserChangeNotifications(); |
| } |
| |
| - (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:(PassRefPtr<WebCore::HTMLPlugInElement>)element |
| { |
| self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element: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)createPlugin |
| { |
| ASSERT(!_proxy); |
| |
| NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; |
| BOOL accleratedCompositingEnabled = false; |
| #if USE(ACCELERATED_COMPOSITING) |
| accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; |
| #endif |
| |
| _proxy = NetscapePluginHostManager::shared().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(), |
| _mode == NP_FULL, _isPrivateBrowsingEnabled, accleratedCompositingEnabled); |
| if (!_proxy) |
| return NO; |
| |
| if (_proxy->rendererType() == UseSoftwareRenderer) |
| _softwareRenderer = WKSoftwareCARendererCreate(_proxy->renderContextID()); |
| else { |
| _pluginLayer = WKMakeRenderLayer(_proxy->renderContextID()); |
| |
| if (accleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) { |
| // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. |
| #ifndef BUILDING_ON_LEOPARD |
| // 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(AdoptNS, _pluginLayer.releaseRef()); |
| |
| _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()]; |
| #endif |
| |
| // 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]->setNeedsStyleRecalc(SyntheticStyleChange); |
| } else |
| self.wantsLayer = YES; |
| } |
| |
| // Update the window frame. |
| _proxy->windowFrameChanged([[self window] frame]); |
| |
| return YES; |
| } |
| |
| // FIXME: This method is an ideal candidate to move up to the base class |
| - (CALayer *)pluginLayer |
| { |
| return _pluginLayer.get(); |
| } |
| |
| - (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); |
| |
| CGRect bounds = NSRectToCGRect([self bounds]); |
| CGRect frame = NSRectToCGRect([self frame]); |
| |
| // We're not scaled, or in a subframe |
| CATransform3D scaleTransform = CATransform3DIdentity; |
| if (CGSizeEqualToSize(bounds.size, frame.size)) { |
| // We're in a subframe. Backing store is boundsInWindow.size. |
| if (boundsInWindow.size.width && boundsInWindow.size.height) |
| scaleTransform = CATransform3DMakeScale(frame.size.width / boundsInWindow.size.width, frame.size.height / boundsInWindow.size.height, 1); |
| } else { |
| // We're in the main frame with scaling. Need to mimic the frame/bounds scaling on Widgets. |
| if (frame.size.width && frame.size.height) |
| scaleTransform = CATransform3DMakeScale(bounds.size.width / frame.size.width, bounds.size.height / frame.size.height, 1); |
| } |
| |
| _pluginLayer.get().sublayerTransform = scaleTransform; |
| } |
| |
| - (void)windowFocusChanged:(BOOL)hasFocus |
| { |
| if (_proxy) |
| _proxy->windowFocusChanged(hasFocus); |
| } |
| |
| - (BOOL)shouldStop |
| { |
| if (!_proxy) |
| return YES; |
| |
| return _proxy->shouldStop(); |
| } |
| |
| - (void)destroyPlugin |
| { |
| if (_proxy) { |
| if (_softwareRenderer) { |
| WKSoftwareCARendererDestroy(_softwareRenderer); |
| _softwareRenderer = 0; |
| } |
| |
| _proxy->destroy(); |
| _proxy = 0; |
| } |
| |
| _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 (_element->renderer() && _element->renderer()->isEmbeddedObject()) { |
| // FIXME: The renderer could also be a RenderApplet, we should handle that. |
| RenderEmbeddedObject* renderer = toRenderEmbeddedObject(_element->renderer()); |
| renderer->setShowsCrashedPluginIndicator(); |
| } |
| |
| _pluginLayer = nil; |
| _proxy = 0; |
| |
| // No need for us to be layer backed anymore |
| self.wantsLayer = NO; |
| |
| [self invalidatePluginContentRect:[self bounds]]; |
| } |
| |
| - (void)visibleRectDidChange |
| { |
| [super visibleRectDidChange]; |
| WKSyncSurfaceToView(self); |
| } |
| |
| - (void)drawRect:(NSRect)rect |
| { |
| if (_cachedSnapshot) { |
| NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; |
| [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; |
| return; |
| } |
| |
| if (_proxy) { |
| if (_softwareRenderer) { |
| if ([NSGraphicsContext currentContextDrawingToScreen]) { |
| WKSoftwareCARendererRender(_softwareRenderer, (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); |
| } |
| |
| return; |
| } |
| } |
| |
| - (PassRefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(PassRefPtr<JSC::Bindings::RootObject>)rootObject |
| { |
| if (!_proxy) |
| return 0; |
| |
| return _proxy->createBindingsInstance(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)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck |
| { |
| ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); |
| |
| id contextInfo = [webPluginContainerCheck contextInfo]; |
| ASSERT([contextInfo isKindOfClass:[NSNumber class]]); |
| |
| if (!_proxy) |
| return; |
| |
| uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; |
| _proxy->cancelCheckIfAllowedToLoadURL(checkID); |
| } |
| |
| - (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo |
| { |
| ASSERT([contextInfo isKindOfClass:[NSNumber class]]); |
| if (!_proxy) |
| return; |
| |
| uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; |
| _proxy->checkIfAllowedToLoadURLResult(checkID, (policy == PolicyUse)); |
| } |
| |
| - (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) |