/* * 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 #import #import #import #import #import #import #import #import #import #import #import 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)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 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 layerBounds = NSRectToCGRect(boundsInWindow); 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 . [[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([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); } else if (_snapshotting && [self supportsSnapshotting]) { _proxy->snapshot(reinterpret_cast([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); } return; } } - (PassRefPtr)createPluginBindingsInstance:(PassRefPtr)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([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)