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