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/ResourceError.h> 48#import <WebCore/WebCoreObjCExtras.h> 49#import <WebCore/RunLoop.h> 50#import <WebCore/runtime_root.h> 51#import <runtime/InitializeThreading.h> 52#import <wtf/Assertions.h> 53#import <wtf/MainThread.h> 54#import <wtf/ObjcRuntimeExtras.h> 55 56using namespace WebCore; 57using namespace WebKit; 58 59extern "C" { 60#include "WebKitPluginClientServer.h" 61#include "WebKitPluginHost.h" 62} 63 64#if HAVE(LAYER_HOSTING_IN_WINDOW_SERVER) 65@interface NSWindow (Details) 66- (BOOL)_hostsLayersInWindowServer; 67@end 68#endif 69 70@implementation WebHostedNetscapePluginView 71 72+ (void)initialize 73{ 74 JSC::initializeThreading(); 75 WTF::initializeMainThreadToProcessMainThread(); 76 WebCore::RunLoop::initializeMainRunLoop(); 77 WebCoreObjCFinalizeOnMainThread(self); 78 WKSendUserChangeNotifications(); 79} 80 81- (id)initWithFrame:(NSRect)frame 82 pluginPackage:(WebNetscapePluginPackage *)pluginPackage 83 URL:(NSURL *)URL 84 baseURL:(NSURL *)baseURL 85 MIMEType:(NSString *)MIME 86 attributeKeys:(NSArray *)keys 87 attributeValues:(NSArray *)values 88 loadManually:(BOOL)loadManually 89 element:(PassRefPtr<WebCore::HTMLPlugInElement>)element 90{ 91 self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element]; 92 if (!self) 93 return nil; 94 95 return self; 96} 97 98- (void)handleMouseMoved:(NSEvent *)event 99{ 100 if (_isStarted && _proxy) 101 _proxy->mouseEvent(self, event, NPCocoaEventMouseMoved); 102} 103 104- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values 105{ 106 ASSERT(!_attributeKeys); 107 ASSERT(!_attributeValues); 108 109 _attributeKeys = adoptNS([keys copy]); 110 _attributeValues = adoptNS([values copy]); 111} 112 113- (BOOL)windowHostsLayersInWindowServer 114{ 115#if HAVE(LAYER_HOSTING_IN_WINDOW_SERVER) 116 return [[[self webView] window] _hostsLayersInWindowServer]; 117#else 118 return false; 119#endif 120} 121 122- (BOOL)createPlugin 123{ 124 ASSERT(!_proxy); 125 126 NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; 127 BOOL acceleratedCompositingEnabled = false; 128#if USE(ACCELERATED_COMPOSITING) 129 acceleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; 130#endif 131 _hostsLayersInWindowServer = [self windowHostsLayersInWindowServer]; 132 133 _proxy = NetscapePluginHostManager::shared().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(), 134 _mode == NP_FULL, _isPrivateBrowsingEnabled, acceleratedCompositingEnabled, _hostsLayersInWindowServer); 135 if (!_proxy) 136 return NO; 137 138 if (_proxy->rendererType() == UseSoftwareRenderer) 139 _softwareRenderer = WKSoftwareCARendererCreate(_proxy->renderContextID()); 140 else 141 [self createPluginLayer]; 142 143 // Update the window frame. 144 _proxy->windowFrameChanged([[self window] frame]); 145 146 return YES; 147} 148 149- (void)createPluginLayer 150{ 151 BOOL acceleratedCompositingEnabled = false; 152#if USE(ACCELERATED_COMPOSITING) 153 acceleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; 154#endif 155 156 _pluginLayer = WKMakeRenderLayer(_proxy->renderContextID()); 157 158 if (acceleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) { 159 // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. 160 // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry 161 // in order to get the coordinate system right. 162 RetainPtr<CALayer> realPluginLayer = adoptNS(_pluginLayer.leakRef()); 163 164 _pluginLayer = adoptNS([[CALayer alloc] init]); 165 _pluginLayer.get().bounds = realPluginLayer.get().bounds; 166 _pluginLayer.get().geometryFlipped = YES; 167 168 _pluginLayer.get().backgroundColor = adoptCF(CGColorCreateGenericRGB(1, 0, 1, 1)).get(); 169 170 realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 171 [_pluginLayer.get() addSublayer:realPluginLayer.get()]; 172 173 // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc() 174 // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033 175 core([self webFrame])->view()->enterCompositingMode(); 176 [self element]->setNeedsStyleRecalc(SyntheticStyleChange); 177 } else 178 self.wantsLayer = YES; 179} 180 181- (void)setHostsLayersInWindowServer:(bool)hostsLayersInWindowServer 182{ 183 if (_proxy->rendererType() == UseSoftwareRenderer) 184 return; 185 186 RetainPtr<CALayer> currentSuperlayer = [_pluginLayer superlayer]; 187 188 _hostsLayersInWindowServer = hostsLayersInWindowServer; 189 190 [self createPluginLayer]; 191 [self setLayer:currentSuperlayer.get()]; 192} 193 194// FIXME: This method is an ideal candidate to move up to the base class 195- (CALayer *)pluginLayer 196{ 197 return _pluginLayer.get(); 198} 199 200- (BOOL)getFormValue:(NSString **)value 201{ 202 // FIXME: We cannot implement this method for now because NPP_GetValue 203 // is not currently exposed by the plugin host. Once we have an IPC routine 204 // which allows us to invoke NPP_GetValue, we could implement this method. 205 return false; 206} 207 208- (void)setLayer:(CALayer *)newLayer 209{ 210 // FIXME: This should use the same implementation as WebNetscapePluginView (and move to the base class). 211 [super setLayer:newLayer]; 212 213 if (_pluginLayer) 214 [newLayer addSublayer:_pluginLayer.get()]; 215} 216 217- (void)privateBrowsingModeDidChange 218{ 219 if (_proxy) 220 _proxy->privateBrowsingModeDidChange(_isPrivateBrowsingEnabled); 221} 222 223- (void)loadStream 224{ 225} 226 227- (void)updateAndSetWindow 228{ 229 if (!_proxy) 230 return; 231 232 // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor 233 // of 1. For non-1.0 scale factors this assumption is false. 234 NSView *windowContentView = [[self window] contentView]; 235 NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView]; 236 237 NSRect visibleRectInWindow; 238 239 // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when 240 // moved to a background tab. We don't do this for Core Graphics plug-ins as 241 // older versions of Flash have historical WebKit-specific code that isn't 242 // compatible with this behavior. 243 BOOL shouldClipOutPlugin = _pluginLayer && [self shouldClipOutPlugin]; 244 if (!shouldClipOutPlugin) 245 visibleRectInWindow = [self actualVisibleRectInWindow]; 246 else 247 visibleRectInWindow = NSZeroRect; 248 249 // Flip Y to convert NSWindow coordinates to top-left-based window coordinates. 250 float borderViewHeight = [[self currentWindow] frame].size.height; 251 boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow); 252 253 if (!shouldClipOutPlugin) 254 visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow); 255 256 _previousSize = boundsInWindow.size; 257 258 _proxy->resize(boundsInWindow, visibleRectInWindow); 259 260 bool shouldHostLayersInWindowServer = [self windowHostsLayersInWindowServer]; 261 if (_hostsLayersInWindowServer != shouldHostLayersInWindowServer) 262 _proxy->setShouldHostLayersInWindowServer(shouldHostLayersInWindowServer); 263} 264 265- (void)windowFocusChanged:(BOOL)hasFocus 266{ 267 if (_proxy) 268 _proxy->windowFocusChanged(hasFocus); 269} 270 271- (BOOL)shouldStop 272{ 273 if (!_proxy) 274 return YES; 275 276 return _proxy->shouldStop(); 277} 278 279- (void)destroyPlugin 280{ 281 if (_proxy) { 282 if (_softwareRenderer) { 283 WKSoftwareCARendererDestroy(_softwareRenderer); 284 _softwareRenderer = 0; 285 } 286 287 _proxy->destroy(); 288 _proxy = 0; 289 } 290 291 _pluginLayer = 0; 292} 293 294- (void)startTimers 295{ 296 if (_proxy) 297 _proxy->startTimers(_isCompletelyObscured); 298} 299 300- (void)stopTimers 301{ 302 if (_proxy) 303 _proxy->stopTimers(); 304} 305 306- (void)focusChanged 307{ 308 if (_proxy) 309 _proxy->focusChanged(_hasFocus); 310} 311 312- (void)windowFrameDidChange:(NSNotification *)notification 313{ 314 if (_proxy && [self window]) 315 _proxy->windowFrameChanged([[self window] frame]); 316} 317 318- (void)addWindowObservers 319{ 320 [super addWindowObservers]; 321 322 ASSERT([self window]); 323 324 NSWindow *window = [self window]; 325 326 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 327 [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:) 328 name:NSWindowDidMoveNotification object:window]; 329 [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:) 330 name:NSWindowDidResizeNotification object:window]; 331 332 if (_proxy) 333 _proxy->windowFrameChanged([window frame]); 334 [self updateAndSetWindow]; 335} 336 337- (void)removeWindowObservers 338{ 339 [super removeWindowObservers]; 340 341 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 342 [notificationCenter removeObserver:self name:NSWindowDidMoveNotification object:nil]; 343 [notificationCenter removeObserver:self name:NSWindowDidResizeNotification object:nil]; 344} 345 346- (void)mouseDown:(NSEvent *)event 347{ 348 if (_isStarted && _proxy) 349 _proxy->mouseEvent(self, event, NPCocoaEventMouseDown); 350} 351 352- (void)mouseUp:(NSEvent *)event 353{ 354 if (_isStarted && _proxy) 355 _proxy->mouseEvent(self, event, NPCocoaEventMouseUp); 356} 357 358- (void)mouseDragged:(NSEvent *)event 359{ 360 if (_isStarted && _proxy) 361 _proxy->mouseEvent(self, event, NPCocoaEventMouseDragged); 362} 363 364- (void)handleMouseEntered:(NSEvent *)event 365{ 366 // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one. 367 [[NSCursor arrowCursor] set]; 368 369 if (_isStarted && _proxy) 370 _proxy->mouseEvent(self, event, NPCocoaEventMouseEntered); 371} 372 373- (void)handleMouseExited:(NSEvent *)event 374{ 375 if (_isStarted && _proxy) 376 _proxy->mouseEvent(self, event, NPCocoaEventMouseExited); 377 378 // 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 379 // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin. 380 // FIXME: This should be job of plugin host, see <rdar://problem/7654434>. 381 [[NSCursor arrowCursor] set]; 382} 383 384- (void)scrollWheel:(NSEvent *)event 385{ 386 bool processedEvent = false; 387 388 if (_isStarted && _proxy) 389 processedEvent = _proxy->wheelEvent(self, event); 390 391 if (!processedEvent) 392 [super scrollWheel:event]; 393} 394 395- (NSTextInputContext *)inputContext 396{ 397 return [[WebTextInputWindowController sharedTextInputWindowController] inputContext]; 398} 399 400- (void)keyDown:(NSEvent *)event 401{ 402 if (!_isStarted || !_proxy) 403 return; 404 405 NSString *string = nil; 406 if ([[WebTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event string:&string]) { 407 if (string) 408 _proxy->insertText(string); 409 return; 410 } 411 412 _proxy->keyEvent(self, event, NPCocoaEventKeyDown); 413} 414 415- (void)keyUp:(NSEvent *)event 416{ 417 if (_isStarted && _proxy) 418 _proxy->keyEvent(self, event, NPCocoaEventKeyUp); 419} 420 421- (void)flagsChanged:(NSEvent *)event 422{ 423 if (_isStarted && _proxy) 424 _proxy->flagsChanged(event); 425} 426 427- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character 428{ 429 if (_isStarted && _proxy) 430 _proxy->syntheticKeyDownWithCommandModifier(keyCode, character); 431} 432 433- (void)pluginHostDied 434{ 435 if (_element->renderer() && _element->renderer()->isEmbeddedObject()) { 436 // FIXME: The renderer could also be a RenderApplet, we should handle that. 437 RenderEmbeddedObject* renderer = toRenderEmbeddedObject(_element->renderer()); 438 renderer->setPluginUnavailabilityReason(RenderEmbeddedObject::PluginCrashed); 439 } 440 441 _pluginLayer = nil; 442 _proxy = 0; 443 444 // No need for us to be layer backed anymore 445 self.wantsLayer = NO; 446 447 [self invalidatePluginContentRect:[self bounds]]; 448} 449 450- (void)visibleRectDidChange 451{ 452 [super visibleRectDidChange]; 453 WKSyncSurfaceToView(self); 454} 455 456- (void)drawRect:(NSRect)rect 457{ 458 if (_cachedSnapshot) { 459 NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; 460 [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; 461 return; 462 } 463 464 if (_proxy) { 465 if (_softwareRenderer) { 466 if ([NSGraphicsContext currentContextDrawingToScreen]) { 467 WKSoftwareCARendererRender(_softwareRenderer, (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(rect)); 468 _proxy->didDraw(); 469 } else 470 _proxy->print(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); 471 } else if (_snapshotting && [self supportsSnapshotting]) { 472 _proxy->snapshot(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); 473 } 474 475 return; 476 } 477} 478 479- (PassRefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(PassRefPtr<JSC::Bindings::RootObject>)rootObject 480{ 481 if (!_proxy) 482 return 0; 483 484 return _proxy->createBindingsInstance(rootObject); 485} 486 487- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response 488{ 489 ASSERT(_loadManually); 490 if (!_proxy) 491 return; 492 493 ASSERT(!_proxy->manualStream()); 494 495 _proxy->setManualStream(HostedNetscapePluginStream::create(_proxy.get(), core([self webFrame])->loader())); 496 _proxy->manualStream()->startStreamWithResponse(response); 497} 498 499- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data 500{ 501 ASSERT(_loadManually); 502 if (!_proxy) 503 return; 504 505 if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) 506 manualStream->didReceiveData(0, static_cast<const char*>([data bytes]), [data length]); 507} 508 509- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error 510{ 511 ASSERT(_loadManually); 512 if (!_proxy) 513 return; 514 515 if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) 516 manualStream->didFail(0, error); 517} 518 519- (void)pluginViewFinishedLoading:(NSView *)pluginView 520{ 521 ASSERT(_loadManually); 522 if (!_proxy) 523 return; 524 525 if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) 526 manualStream->didFinishLoading(0); 527} 528 529- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck 530{ 531 ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); 532 533 id contextInfo = [webPluginContainerCheck contextInfo]; 534 ASSERT([contextInfo isKindOfClass:[NSNumber class]]); 535 536 if (!_proxy) 537 return; 538 539 uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; 540 _proxy->cancelCheckIfAllowedToLoadURL(checkID); 541} 542 543- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo 544{ 545 ASSERT([contextInfo isKindOfClass:[NSNumber class]]); 546 if (!_proxy) 547 return; 548 549 uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; 550 _proxy->checkIfAllowedToLoadURLResult(checkID, (policy == PolicyUse)); 551} 552 553- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason 554{ 555 if (_isStarted && _proxy) 556 _proxy->webFrameDidFinishLoadWithReason(webFrame, reason); 557} 558 559- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error 560{ 561 NPReason reason = NPRES_DONE; 562 if (error) 563 reason = HostedNetscapePluginStream::reasonForError(error); 564 [self webFrame:webFrame didFinishLoadWithReason:reason]; 565} 566 567@end 568 569#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) 570