Home | History | Annotate | Download | only in Hosted
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
     27 
     28 #import "WebHostedNetscapePluginView.h"
     29 
     30 #import "HostedNetscapePluginStream.h"
     31 #import "NetscapePluginInstanceProxy.h"
     32 #import "NetscapePluginHostManager.h"
     33 #import "NetscapePluginHostProxy.h"
     34 #import "WebTextInputWindowController.h"
     35 #import "WebFrameInternal.h"
     36 #import "WebView.h"
     37 #import "WebViewInternal.h"
     38 #import "WebUIDelegate.h"
     39 
     40 #import <CoreFoundation/CoreFoundation.h>
     41 #import <WebCore/BridgeJSC.h>
     42 #import <WebCore/Frame.h>
     43 #import <WebCore/FrameLoaderTypes.h>
     44 #import <WebCore/FrameView.h>
     45 #import <WebCore/HTMLPlugInElement.h>
     46 #import <WebCore/RenderEmbeddedObject.h>
     47 #import <WebCore/WebCoreObjCExtras.h>
     48 #import <WebCore/runtime_root.h>
     49 #import <runtime/InitializeThreading.h>
     50 #import <wtf/Assertions.h>
     51 #import <wtf/Threading.h>
     52 
     53 using namespace WebCore;
     54 using namespace WebKit;
     55 
     56 extern "C" {
     57 #include "WebKitPluginClientServer.h"
     58 #include "WebKitPluginHost.h"
     59 }
     60 
     61 @implementation WebHostedNetscapePluginView
     62 
     63 + (void)initialize
     64 {
     65     JSC::initializeThreading();
     66     WTF::initializeMainThreadToProcessMainThread();
     67 #ifndef BUILDING_ON_TIGER
     68     WebCoreObjCFinalizeOnMainThread(self);
     69 #endif
     70     WKSendUserChangeNotifications();
     71 }
     72 
     73 - (id)initWithFrame:(NSRect)frame
     74       pluginPackage:(WebNetscapePluginPackage *)pluginPackage
     75                 URL:(NSURL *)URL
     76             baseURL:(NSURL *)baseURL
     77            MIMEType:(NSString *)MIME
     78       attributeKeys:(NSArray *)keys
     79     attributeValues:(NSArray *)values
     80        loadManually:(BOOL)loadManually
     81             element:(PassRefPtr<WebCore::HTMLPlugInElement>)element
     82 {
     83     self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element];
     84     if (!self)
     85         return nil;
     86 
     87     return self;
     88 }
     89 
     90 - (void)handleMouseMoved:(NSEvent *)event
     91 {
     92     if (_isStarted && _proxy)
     93         _proxy->mouseEvent(self, event, NPCocoaEventMouseMoved);
     94 }
     95 
     96 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values
     97 {
     98     ASSERT(!_attributeKeys);
     99     ASSERT(!_attributeValues);
    100 
    101     _attributeKeys.adoptNS([keys copy]);
    102     _attributeValues.adoptNS([values copy]);
    103 }
    104 
    105 - (BOOL)createPlugin
    106 {
    107     ASSERT(!_proxy);
    108 
    109     NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()];
    110     BOOL accleratedCompositingEnabled = false;
    111 #if USE(ACCELERATED_COMPOSITING)
    112     accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled];
    113 #endif
    114 
    115     _proxy = NetscapePluginHostManager::shared().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(),
    116                                                                    _mode == NP_FULL, _isPrivateBrowsingEnabled, accleratedCompositingEnabled);
    117     if (!_proxy)
    118         return NO;
    119 
    120     if (_proxy->rendererType() == UseSoftwareRenderer)
    121         _softwareRenderer = WKSoftwareCARendererCreate(_proxy->renderContextID());
    122     else {
    123         _pluginLayer = WKMakeRenderLayer(_proxy->renderContextID());
    124 
    125         if (accleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) {
    126             // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView.
    127 #ifndef BUILDING_ON_LEOPARD
    128             // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry
    129             // in order to get the coordinate system right.
    130             RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef());
    131 
    132             _pluginLayer.adoptNS([[CALayer alloc] init]);
    133             _pluginLayer.get().bounds = realPluginLayer.get().bounds;
    134             _pluginLayer.get().geometryFlipped = YES;
    135 
    136             realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
    137             [_pluginLayer.get() addSublayer:realPluginLayer.get()];
    138 #endif
    139 
    140             // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc()
    141             // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033
    142             core([self webFrame])->view()->enterCompositingMode();
    143             [self element]->setNeedsStyleRecalc(SyntheticStyleChange);
    144         } else
    145             self.wantsLayer = YES;
    146     }
    147 
    148     // Update the window frame.
    149     _proxy->windowFrameChanged([[self window] frame]);
    150 
    151     return YES;
    152 }
    153 
    154 // FIXME: This method is an ideal candidate to move up to the base class
    155 - (CALayer *)pluginLayer
    156 {
    157     return _pluginLayer.get();
    158 }
    159 
    160 - (void)setLayer:(CALayer *)newLayer
    161 {
    162     // FIXME: This should use the same implementation as WebNetscapePluginView (and move to the base class).
    163     [super setLayer:newLayer];
    164 
    165     if (_pluginLayer)
    166         [newLayer addSublayer:_pluginLayer.get()];
    167 }
    168 
    169 - (void)privateBrowsingModeDidChange
    170 {
    171     if (_proxy)
    172         _proxy->privateBrowsingModeDidChange(_isPrivateBrowsingEnabled);
    173 }
    174 
    175 - (void)loadStream
    176 {
    177 }
    178 
    179 - (void)updateAndSetWindow
    180 {
    181     if (!_proxy)
    182         return;
    183 
    184     // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor
    185     // of 1. For non-1.0 scale factors this assumption is false.
    186     NSView *windowContentView = [[self window] contentView];
    187     NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView];
    188 
    189     NSRect visibleRectInWindow;
    190 
    191     // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when
    192     // moved to a background tab. We don't do this for Core Graphics plug-ins as
    193     // older versions of Flash have historical WebKit-specific code that isn't
    194     // compatible with this behavior.
    195     BOOL shouldClipOutPlugin = _pluginLayer && [self shouldClipOutPlugin];
    196     if (!shouldClipOutPlugin)
    197         visibleRectInWindow = [self actualVisibleRectInWindow];
    198     else
    199         visibleRectInWindow = NSZeroRect;
    200 
    201     // Flip Y to convert NSWindow coordinates to top-left-based window coordinates.
    202     float borderViewHeight = [[self currentWindow] frame].size.height;
    203     boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow);
    204 
    205     if (!shouldClipOutPlugin)
    206         visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow);
    207 
    208     _previousSize = boundsInWindow.size;
    209 
    210     _proxy->resize(boundsInWindow, visibleRectInWindow);
    211 
    212     CGRect bounds = NSRectToCGRect([self bounds]);
    213     CGRect frame = NSRectToCGRect([self frame]);
    214 
    215     // We're not scaled, or in a subframe
    216     CATransform3D scaleTransform = CATransform3DIdentity;
    217     if (CGSizeEqualToSize(bounds.size, frame.size)) {
    218         // We're in a subframe. Backing store is boundsInWindow.size.
    219         if (boundsInWindow.size.width && boundsInWindow.size.height)
    220             scaleTransform = CATransform3DMakeScale(frame.size.width / boundsInWindow.size.width, frame.size.height / boundsInWindow.size.height, 1);
    221     } else {
    222         // We're in the main frame with scaling. Need to mimic the frame/bounds scaling on Widgets.
    223         if (frame.size.width && frame.size.height)
    224             scaleTransform = CATransform3DMakeScale(bounds.size.width / frame.size.width, bounds.size.height / frame.size.height, 1);
    225     }
    226 
    227     _pluginLayer.get().sublayerTransform = scaleTransform;
    228 }
    229 
    230 - (void)windowFocusChanged:(BOOL)hasFocus
    231 {
    232     if (_proxy)
    233         _proxy->windowFocusChanged(hasFocus);
    234 }
    235 
    236 - (BOOL)shouldStop
    237 {
    238     if (!_proxy)
    239         return YES;
    240 
    241     return _proxy->shouldStop();
    242 }
    243 
    244 - (void)destroyPlugin
    245 {
    246     if (_proxy) {
    247         if (_softwareRenderer) {
    248             WKSoftwareCARendererDestroy(_softwareRenderer);
    249             _softwareRenderer = 0;
    250         }
    251 
    252         _proxy->destroy();
    253         _proxy = 0;
    254     }
    255 
    256     _pluginLayer = 0;
    257 }
    258 
    259 - (void)startTimers
    260 {
    261     if (_proxy)
    262         _proxy->startTimers(_isCompletelyObscured);
    263 }
    264 
    265 - (void)stopTimers
    266 {
    267     if (_proxy)
    268         _proxy->stopTimers();
    269 }
    270 
    271 - (void)focusChanged
    272 {
    273     if (_proxy)
    274         _proxy->focusChanged(_hasFocus);
    275 }
    276 
    277 - (void)windowFrameDidChange:(NSNotification *)notification
    278 {
    279     if (_proxy && [self window])
    280         _proxy->windowFrameChanged([[self window] frame]);
    281 }
    282 
    283 - (void)addWindowObservers
    284 {
    285     [super addWindowObservers];
    286 
    287     ASSERT([self window]);
    288 
    289     NSWindow *window = [self window];
    290 
    291     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    292     [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:)
    293                                name:NSWindowDidMoveNotification object:window];
    294     [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:)
    295                                name:NSWindowDidResizeNotification object:window];
    296 
    297     if (_proxy)
    298         _proxy->windowFrameChanged([window frame]);
    299     [self updateAndSetWindow];
    300 }
    301 
    302 - (void)removeWindowObservers
    303 {
    304     [super removeWindowObservers];
    305 
    306     NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    307     [notificationCenter removeObserver:self name:NSWindowDidMoveNotification object:nil];
    308     [notificationCenter removeObserver:self name:NSWindowDidResizeNotification object:nil];
    309 }
    310 
    311 - (void)mouseDown:(NSEvent *)event
    312 {
    313     if (_isStarted && _proxy)
    314         _proxy->mouseEvent(self, event, NPCocoaEventMouseDown);
    315 }
    316 
    317 - (void)mouseUp:(NSEvent *)event
    318 {
    319     if (_isStarted && _proxy)
    320         _proxy->mouseEvent(self, event, NPCocoaEventMouseUp);
    321 }
    322 
    323 - (void)mouseDragged:(NSEvent *)event
    324 {
    325     if (_isStarted && _proxy)
    326         _proxy->mouseEvent(self, event, NPCocoaEventMouseDragged);
    327 }
    328 
    329 - (void)handleMouseEntered:(NSEvent *)event
    330 {
    331     // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one.
    332     [[NSCursor arrowCursor] set];
    333 
    334     if (_isStarted && _proxy)
    335         _proxy->mouseEvent(self, event, NPCocoaEventMouseEntered);
    336 }
    337 
    338 - (void)handleMouseExited:(NSEvent *)event
    339 {
    340     if (_isStarted && _proxy)
    341         _proxy->mouseEvent(self, event, NPCocoaEventMouseExited);
    342 
    343     // 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
    344     // current cursor is otherwise.  Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin.
    345     // FIXME: This should be job of plugin host, see <rdar://problem/7654434>.
    346     [[NSCursor arrowCursor] set];
    347 }
    348 
    349 - (void)scrollWheel:(NSEvent *)event
    350 {
    351     bool processedEvent = false;
    352 
    353     if (_isStarted && _proxy)
    354         processedEvent = _proxy->wheelEvent(self, event);
    355 
    356     if (!processedEvent)
    357         [super scrollWheel:event];
    358 }
    359 
    360 - (NSTextInputContext *)inputContext
    361 {
    362     return [[WebTextInputWindowController sharedTextInputWindowController] inputContext];
    363 }
    364 
    365 - (void)keyDown:(NSEvent *)event
    366 {
    367     if (!_isStarted || !_proxy)
    368         return;
    369 
    370     NSString *string = nil;
    371     if ([[WebTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event string:&string]) {
    372         if (string)
    373             _proxy->insertText(string);
    374         return;
    375     }
    376 
    377     _proxy->keyEvent(self, event, NPCocoaEventKeyDown);
    378 }
    379 
    380 - (void)keyUp:(NSEvent *)event
    381 {
    382     if (_isStarted && _proxy)
    383         _proxy->keyEvent(self, event, NPCocoaEventKeyUp);
    384 }
    385 
    386 - (void)flagsChanged:(NSEvent *)event
    387 {
    388     if (_isStarted && _proxy)
    389         _proxy->flagsChanged(event);
    390 }
    391 
    392 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character
    393 {
    394     if (_isStarted && _proxy)
    395         _proxy->syntheticKeyDownWithCommandModifier(keyCode, character);
    396 }
    397 
    398 - (void)pluginHostDied
    399 {
    400     if (_element->renderer() && _element->renderer()->isEmbeddedObject()) {
    401         // FIXME: The renderer could also be a RenderApplet, we should handle that.
    402         RenderEmbeddedObject* renderer = toRenderEmbeddedObject(_element->renderer());
    403         renderer->setShowsCrashedPluginIndicator();
    404     }
    405 
    406     _pluginLayer = nil;
    407     _proxy = 0;
    408 
    409     // No need for us to be layer backed anymore
    410     self.wantsLayer = NO;
    411 
    412     [self invalidatePluginContentRect:[self bounds]];
    413 }
    414 
    415 - (void)visibleRectDidChange
    416 {
    417     [super visibleRectDidChange];
    418     WKSyncSurfaceToView(self);
    419 }
    420 
    421 - (void)drawRect:(NSRect)rect
    422 {
    423     if (_cachedSnapshot) {
    424         NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] };
    425         [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1];
    426         return;
    427     }
    428 
    429     if (_proxy) {
    430         if (_softwareRenderer) {
    431             if ([NSGraphicsContext currentContextDrawingToScreen]) {
    432                 WKSoftwareCARendererRender(_softwareRenderer, (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(rect));
    433                 _proxy->didDraw();
    434             } else
    435                 _proxy->print(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
    436         } else if (_snapshotting && [self supportsSnapshotting]) {
    437             _proxy->snapshot(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height);
    438         }
    439 
    440         return;
    441     }
    442 }
    443 
    444 - (PassRefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(PassRefPtr<JSC::Bindings::RootObject>)rootObject
    445 {
    446     if (!_proxy)
    447         return 0;
    448 
    449     return _proxy->createBindingsInstance(rootObject);
    450 }
    451 
    452 - (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response
    453 {
    454     ASSERT(_loadManually);
    455     if (!_proxy)
    456         return;
    457 
    458     ASSERT(!_proxy->manualStream());
    459 
    460     _proxy->setManualStream(HostedNetscapePluginStream::create(_proxy.get(), core([self webFrame])->loader()));
    461     _proxy->manualStream()->startStreamWithResponse(response);
    462 }
    463 
    464 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data
    465 {
    466     ASSERT(_loadManually);
    467     if (!_proxy)
    468         return;
    469 
    470     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
    471         manualStream->didReceiveData(0, static_cast<const char*>([data bytes]), [data length]);
    472 }
    473 
    474 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error
    475 {
    476     ASSERT(_loadManually);
    477     if (!_proxy)
    478         return;
    479 
    480     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
    481         manualStream->didFail(0, error);
    482 }
    483 
    484 - (void)pluginViewFinishedLoading:(NSView *)pluginView
    485 {
    486     ASSERT(_loadManually);
    487     if (!_proxy)
    488         return;
    489 
    490     if (HostedNetscapePluginStream* manualStream = _proxy->manualStream())
    491         manualStream->didFinishLoading(0);
    492 }
    493 
    494 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck
    495 {
    496     ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]);
    497 
    498     id contextInfo = [webPluginContainerCheck contextInfo];
    499     ASSERT([contextInfo isKindOfClass:[NSNumber class]]);
    500 
    501     if (!_proxy)
    502         return;
    503 
    504     uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue];
    505     _proxy->cancelCheckIfAllowedToLoadURL(checkID);
    506 }
    507 
    508 - (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo
    509 {
    510     ASSERT([contextInfo isKindOfClass:[NSNumber class]]);
    511     if (!_proxy)
    512         return;
    513 
    514     uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue];
    515     _proxy->checkIfAllowedToLoadURLResult(checkID, (policy == PolicyUse));
    516 }
    517 
    518 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason
    519 {
    520     if (_isStarted && _proxy)
    521         _proxy->webFrameDidFinishLoadWithReason(webFrame, reason);
    522 }
    523 
    524 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error
    525 {
    526     NPReason reason = NPRES_DONE;
    527     if (error)
    528         reason = HostedNetscapePluginStream::reasonForError(error);
    529     [self webFrame:webFrame didFinishLoadWithReason:reason];
    530 }
    531 
    532 @end
    533 
    534 #endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
    535