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 53using namespace WebCore; 54using namespace WebKit; 55 56extern "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