1 /* 2 * Copyright (C) 2005, 2006, 2007 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #if ENABLE(NETSCAPE_PLUGIN_API) 30 31 #import "WebNetscapePluginView.h" 32 33 #import "QuickDrawCompatibility.h" 34 #import "WebDataSourceInternal.h" 35 #import "WebDefaultUIDelegate.h" 36 #import "WebFrameInternal.h" 37 #import "WebFrameView.h" 38 #import "WebKitErrorsPrivate.h" 39 #import "WebKitLogging.h" 40 #import "WebKitNSStringExtras.h" 41 #import "WebKitSystemInterface.h" 42 #import "WebNSDataExtras.h" 43 #import "WebNSDictionaryExtras.h" 44 #import "WebNSObjectExtras.h" 45 #import "WebNSURLExtras.h" 46 #import "WebNSURLRequestExtras.h" 47 #import "WebNSViewExtras.h" 48 #import "WebNetscapeContainerCheckContextInfo.h" 49 #import "WebNetscapeContainerCheckPrivate.h" 50 #import "WebNetscapePluginEventHandler.h" 51 #import "WebNetscapePluginPackage.h" 52 #import "WebNetscapePluginStream.h" 53 #import "WebPluginContainerCheck.h" 54 #import "WebPluginRequest.h" 55 #import "WebPreferences.h" 56 #import "WebUIDelegatePrivate.h" 57 #import "WebViewInternal.h" 58 #import <Carbon/Carbon.h> 59 #import <WebCore/CookieJar.h> 60 #import <WebCore/DocumentLoader.h> 61 #import <WebCore/Element.h> 62 #import <WebCore/Frame.h> 63 #import <WebCore/FrameLoader.h> 64 #import <WebCore/FrameTree.h> 65 #import <WebCore/FrameView.h> 66 #import <WebCore/HTMLPlugInElement.h> 67 #import <WebCore/Page.h> 68 #import <WebCore/PluginMainThreadScheduler.h> 69 #import <WebCore/ProxyServer.h> 70 #import <WebCore/ScriptController.h> 71 #import <WebCore/SecurityOrigin.h> 72 #import <WebCore/SoftLinking.h> 73 #import <WebCore/WebCoreObjCExtras.h> 74 #import <WebCore/WebCoreURLResponse.h> 75 #import <WebCore/npruntime_impl.h> 76 #import <WebKit/DOMPrivate.h> 77 #import <WebKit/WebUIDelegate.h> 78 #import <objc/objc-runtime.h> 79 #import <runtime/InitializeThreading.h> 80 #import <runtime/JSLock.h> 81 #import <wtf/Assertions.h> 82 #import <wtf/Threading.h> 83 #import <wtf/text/CString.h> 84 85 #define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification" 86 #define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification" 87 #define WKNVSupportsCompositingCoreAnimationPluginsBool 74656 /* TRUE if the browser supports hardware compositing of Core Animation plug-ins */ 88 static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying bug in <rdar://problem/7288546> */ 89 90 using namespace WebCore; 91 using namespace WebKit; 92 using namespace std; 93 94 static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel) 95 { 96 #ifndef NP_NO_QUICKDRAW 97 return drawingModel == NPDrawingModelQuickDraw; 98 #else 99 return false; 100 #endif 101 }; 102 103 @interface WebNetscapePluginView (Internal) 104 - (NPError)_createPlugin; 105 - (void)_destroyPlugin; 106 - (NSBitmapImageRep *)_printedPluginBitmap; 107 - (void)_redeliverStream; 108 - (BOOL)_shouldCancelSrcStream; 109 @end 110 111 static WebNetscapePluginView *currentPluginView = nil; 112 113 typedef struct OpaquePortState* PortState; 114 115 static const double ThrottledTimerInterval = 0.25; 116 117 class PluginTimer : public TimerBase { 118 public: 119 typedef void (*TimerFunc)(NPP npp, uint32_t timerID); 120 121 PluginTimer(NPP npp, uint32_t timerID, uint32_t interval, NPBool repeat, TimerFunc timerFunc) 122 : m_npp(npp) 123 , m_timerID(timerID) 124 , m_interval(interval) 125 , m_repeat(repeat) 126 , m_timerFunc(timerFunc) 127 { 128 } 129 130 void start(bool throttle) 131 { 132 ASSERT(!isActive()); 133 134 double timeInterval = m_interval / 1000.0; 135 136 if (throttle) 137 timeInterval = max(timeInterval, ThrottledTimerInterval); 138 139 if (m_repeat) 140 startRepeating(timeInterval); 141 else 142 startOneShot(timeInterval); 143 } 144 145 private: 146 virtual void fired() 147 { 148 m_timerFunc(m_npp, m_timerID); 149 if (!m_repeat) 150 delete this; 151 } 152 153 NPP m_npp; 154 uint32_t m_timerID; 155 uint32_t m_interval; 156 NPBool m_repeat; 157 TimerFunc m_timerFunc; 158 }; 159 160 #ifndef NP_NO_QUICKDRAW 161 162 // QuickDraw is not available in 64-bit 163 164 typedef struct { 165 GrafPtr oldPort; 166 GDHandle oldDevice; 167 Point oldOrigin; 168 RgnHandle oldClipRegion; 169 RgnHandle oldVisibleRegion; 170 RgnHandle clipRegion; 171 BOOL forUpdate; 172 } PortState_QD; 173 174 #endif /* NP_NO_QUICKDRAW */ 175 176 typedef struct { 177 CGContextRef context; 178 } PortState_CG; 179 180 @class NSTextInputContext; 181 @interface NSResponder (AppKitDetails) 182 - (NSTextInputContext *)inputContext; 183 @end 184 185 @interface WebNetscapePluginView (ForwardDeclarations) 186 - (void)setWindowIfNecessary; 187 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification; 188 @end 189 190 @implementation WebNetscapePluginView 191 192 + (void)initialize 193 { 194 JSC::initializeThreading(); 195 WTF::initializeMainThreadToProcessMainThread(); 196 #ifndef BUILDING_ON_TIGER 197 WebCoreObjCFinalizeOnMainThread(self); 198 #endif 199 WKSendUserChangeNotifications(); 200 } 201 202 // MARK: EVENTS 203 204 // The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers 205 // the entire window frame (or structure region to use the Carbon term) rather then just the window content. 206 // We can remove this when <rdar://problem/4201099> is fixed. 207 - (void)fixWindowPort 208 { 209 #ifndef NP_NO_QUICKDRAW 210 ASSERT(isDrawingModelQuickDraw(drawingModel)); 211 212 NSWindow *currentWindow = [self currentWindow]; 213 if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")]) 214 return; 215 216 float windowHeight = [currentWindow frame].size.height; 217 NSView *contentView = [currentWindow contentView]; 218 NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates 219 220 CGrafPtr oldPort; 221 GetPort(&oldPort); 222 SetPort(GetWindowPort((WindowRef)[currentWindow windowRef])); 223 224 MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect))); 225 PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height)); 226 227 SetPort(oldPort); 228 #endif 229 } 230 231 #ifndef NP_NO_QUICKDRAW 232 static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context) 233 { 234 UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask; 235 if (byteOrder == kCGBitmapByteOrderDefault) 236 switch (CGBitmapContextGetBitsPerPixel(context)) { 237 case 16: 238 byteOrder = kCGBitmapByteOrder16Host; 239 break; 240 case 32: 241 byteOrder = kCGBitmapByteOrder32Host; 242 break; 243 } 244 switch (byteOrder) { 245 case kCGBitmapByteOrder16Little: 246 return k16LE555PixelFormat; 247 case kCGBitmapByteOrder32Little: 248 return k32BGRAPixelFormat; 249 case kCGBitmapByteOrder16Big: 250 return k16BE555PixelFormat; 251 case kCGBitmapByteOrder32Big: 252 return k32ARGBPixelFormat; 253 } 254 ASSERT_NOT_REACHED(); 255 return 0; 256 } 257 258 static inline void getNPRect(const CGRect& cgr, NPRect& npr) 259 { 260 npr.top = static_cast<uint16_t>(cgr.origin.y); 261 npr.left = static_cast<uint16_t>(cgr.origin.x); 262 npr.bottom = static_cast<uint16_t>(CGRectGetMaxY(cgr)); 263 npr.right = static_cast<uint16_t>(CGRectGetMaxX(cgr)); 264 } 265 266 #endif 267 268 static inline void getNPRect(const NSRect& nr, NPRect& npr) 269 { 270 npr.top = static_cast<uint16_t>(nr.origin.y); 271 npr.left = static_cast<uint16_t>(nr.origin.x); 272 npr.bottom = static_cast<uint16_t>(NSMaxY(nr)); 273 npr.right = static_cast<uint16_t>(NSMaxX(nr)); 274 } 275 276 - (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate 277 { 278 ASSERT([self currentWindow] != nil); 279 280 // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor 281 // of 1. For non-1.0 scale factors this assumption is false. 282 NSView *windowContentView = [[self window] contentView]; 283 NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView]; 284 NSRect visibleRectInWindow = [self actualVisibleRectInWindow]; 285 286 // Flip Y to convert -[NSWindow contentView] coordinates to top-left-based window coordinates. 287 float borderViewHeight = [[self currentWindow] frame].size.height; 288 boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow); 289 visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow); 290 291 #ifndef NP_NO_QUICKDRAW 292 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef]; 293 ASSERT(windowRef); 294 295 // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates. 296 if (isDrawingModelQuickDraw(drawingModel)) { 297 // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's 298 // content view. This makes it easier to convert between AppKit view and QuickDraw port coordinates. 299 [self fixWindowPort]; 300 301 ::Rect portBounds; 302 CGrafPtr port = GetWindowPort(windowRef); 303 GetPortBounds(port, &portBounds); 304 305 PixMap *pix = *GetPortPixMap(port); 306 boundsInWindow.origin.x += pix->bounds.left - portBounds.left; 307 boundsInWindow.origin.y += pix->bounds.top - portBounds.top; 308 visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left; 309 visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top; 310 } 311 #endif 312 313 window.type = NPWindowTypeWindow; 314 window.x = (int32_t)boundsInWindow.origin.x; 315 window.y = (int32_t)boundsInWindow.origin.y; 316 window.width = static_cast<uint32_t>(NSWidth(boundsInWindow)); 317 window.height = static_cast<uint32_t>(NSHeight(boundsInWindow)); 318 319 // "Clip-out" the plug-in when: 320 // 1) it's not really in a window or off-screen or has no height or width. 321 // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets. 322 // 3) the window is miniaturized or the app is hidden 323 // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil 324 // superviews and nil windows and results from convertRect:toView: are incorrect. 325 if (window.width <= 0 || window.height <= 0 || window.x < -100000 || [self shouldClipOutPlugin]) { 326 327 // The following code tries to give plug-ins the same size they will eventually have. 328 // The specifiedWidth and specifiedHeight variables are used to predict the size that 329 // WebCore will eventually resize us to. 330 331 // The QuickTime plug-in has problems if you give it a width or height of 0. 332 // Since other plug-ins also might have the same sort of trouble, we make sure 333 // to always give plug-ins a size other than 0,0. 334 335 if (window.width <= 0) 336 window.width = specifiedWidth > 0 ? specifiedWidth : 100; 337 if (window.height <= 0) 338 window.height = specifiedHeight > 0 ? specifiedHeight : 100; 339 340 window.clipRect.bottom = window.clipRect.top; 341 window.clipRect.left = window.clipRect.right; 342 343 // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when 344 // moved to a background tab. We don't do this for Core Graphics plug-ins as 345 // older versions of Flash have historical WebKit-specific code that isn't 346 // compatible with this behavior. 347 if (drawingModel == NPDrawingModelCoreAnimation) 348 getNPRect(NSZeroRect, window.clipRect); 349 } else { 350 getNPRect(visibleRectInWindow, window.clipRect); 351 } 352 353 // Save the port state, set up the port for entry into the plugin 354 PortState portState; 355 switch (drawingModel) { 356 #ifndef NP_NO_QUICKDRAW 357 case NPDrawingModelQuickDraw: { 358 // Set up NS_Port. 359 ::Rect portBounds; 360 CGrafPtr port = GetWindowPort(windowRef); 361 GetPortBounds(port, &portBounds); 362 nPort.qdPort.port = port; 363 nPort.qdPort.portx = (int32_t)-boundsInWindow.origin.x; 364 nPort.qdPort.porty = (int32_t)-boundsInWindow.origin.y; 365 window.window = &nPort; 366 367 PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD)); 368 portState = (PortState)qdPortState; 369 370 GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice); 371 372 qdPortState->oldOrigin.h = portBounds.left; 373 qdPortState->oldOrigin.v = portBounds.top; 374 375 qdPortState->oldClipRegion = NewRgn(); 376 GetPortClipRegion(port, qdPortState->oldClipRegion); 377 378 qdPortState->oldVisibleRegion = NewRgn(); 379 GetPortVisibleRegion(port, qdPortState->oldVisibleRegion); 380 381 RgnHandle clipRegion = NewRgn(); 382 qdPortState->clipRegion = clipRegion; 383 384 CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 385 if (currentContext && WKCGContextIsBitmapContext(currentContext)) { 386 // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData 387 // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext 388 // returns true, it still might not be a context we need to create a GWorld for; for example 389 // transparency layers will return true, but return 0 for CGBitmapContextGetData. 390 void* offscreenData = CGBitmapContextGetData(currentContext); 391 if (offscreenData) { 392 // If the current context is an offscreen bitmap, then create a GWorld for it. 393 ::Rect offscreenBounds; 394 offscreenBounds.top = 0; 395 offscreenBounds.left = 0; 396 offscreenBounds.right = CGBitmapContextGetWidth(currentContext); 397 offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext); 398 GWorldPtr newOffscreenGWorld; 399 QDErr err = NewGWorldFromPtr(&newOffscreenGWorld, 400 getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0, 401 static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext)); 402 ASSERT(newOffscreenGWorld); 403 ASSERT(!err); 404 if (!err) { 405 if (offscreenGWorld) 406 DisposeGWorld(offscreenGWorld); 407 offscreenGWorld = newOffscreenGWorld; 408 409 SetGWorld(offscreenGWorld, NULL); 410 411 port = offscreenGWorld; 412 413 nPort.qdPort.port = port; 414 boundsInWindow = [self bounds]; 415 416 // Generate a QD origin based on the current affine transform for currentContext. 417 CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext); 418 CGPoint origin = {0,0}; 419 CGPoint axisFlip = {1,1}; 420 origin = CGPointApplyAffineTransform(origin, offscreenMatrix); 421 axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix); 422 423 // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that. 424 origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x); 425 origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y); 426 427 nPort.qdPort.portx = static_cast<int32_t>(-boundsInWindow.origin.x + origin.x); 428 nPort.qdPort.porty = static_cast<int32_t>(-boundsInWindow.origin.y - origin.y); 429 window.x = 0; 430 window.y = 0; 431 window.window = &nPort; 432 433 // Use the clip bounds from the context instead of the bounds we created 434 // from the window above. 435 getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect); 436 } 437 } 438 } 439 440 MacSetRectRgn(clipRegion, 441 window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty, 442 window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty); 443 444 // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip. 445 if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) { 446 // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are 447 // not going to be redrawn this update. This forces plug-ins to play nice with z-index ordering. 448 if (forUpdate) { 449 RgnHandle viewClipRegion = NewRgn(); 450 451 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and 452 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView 453 // knows about the true set of dirty rects. 454 NSView *opaqueAncestor = [self opaqueAncestor]; 455 const NSRect *dirtyRects; 456 NSInteger dirtyRectCount, dirtyRectIndex; 457 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount]; 458 459 for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) { 460 NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor]; 461 if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) { 462 // Create a region for this dirty rect 463 RgnHandle dirtyRectRegion = NewRgn(); 464 SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect))); 465 466 // Union this dirty rect with the rest of the dirty rects 467 UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion); 468 DisposeRgn(dirtyRectRegion); 469 } 470 } 471 472 // Intersect the dirty region with the clip region, so that we only draw over dirty parts 473 SectRgn(clipRegion, viewClipRegion, clipRegion); 474 DisposeRgn(viewClipRegion); 475 } 476 } 477 478 // Switch to the port and set it up. 479 SetPort(port); 480 PenNormal(); 481 ForeColor(blackColor); 482 BackColor(whiteColor); 483 SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty); 484 SetPortClipRegion(nPort.qdPort.port, clipRegion); 485 486 if (forUpdate) { 487 // AppKit may have tried to help us by doing a BeginUpdate. 488 // But the invalid region at that level didn't include AppKit's notion of what was not valid. 489 // We reset the port's visible region to counteract what BeginUpdate did. 490 SetPortVisibleRegion(nPort.qdPort.port, clipRegion); 491 InvalWindowRgn(windowRef, clipRegion); 492 } 493 494 qdPortState->forUpdate = forUpdate; 495 break; 496 } 497 #endif /* NP_NO_QUICKDRAW */ 498 499 case NPDrawingModelCoreGraphics: { 500 if (![self canDraw]) { 501 portState = NULL; 502 break; 503 } 504 505 ASSERT([NSView focusView] == self); 506 507 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 508 509 PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG)); 510 portState = (PortState)cgPortState; 511 cgPortState->context = context; 512 513 #ifndef NP_NO_CARBON 514 if (eventModel != NPEventModelCocoa) { 515 // Update the plugin's window/context 516 nPort.cgPort.window = windowRef; 517 nPort.cgPort.context = context; 518 window.window = &nPort.cgPort; 519 } 520 #endif /* NP_NO_CARBON */ 521 522 // Save current graphics context's state; will be restored by -restorePortState: 523 CGContextSaveGState(context); 524 525 // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip. 526 if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) { 527 // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and 528 // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView 529 // knows about the true set of dirty rects. 530 NSView *opaqueAncestor = [self opaqueAncestor]; 531 const NSRect *dirtyRects; 532 NSInteger count; 533 [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count]; 534 Vector<CGRect, 16> convertedDirtyRects; 535 convertedDirtyRects.resize(count); 536 for (int i = 0; i < count; ++i) 537 reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor]; 538 CGContextClipToRects(context, convertedDirtyRects.data(), count); 539 } 540 541 break; 542 } 543 544 case NPDrawingModelCoreAnimation: 545 // Just set the port state to a dummy value. 546 portState = (PortState)1; 547 break; 548 549 default: 550 ASSERT_NOT_REACHED(); 551 portState = NULL; 552 break; 553 } 554 555 return portState; 556 } 557 558 - (PortState)saveAndSetNewPortState 559 { 560 return [self saveAndSetNewPortStateForUpdate:NO]; 561 } 562 563 - (void)restorePortState:(PortState)portState 564 { 565 ASSERT([self currentWindow]); 566 ASSERT(portState); 567 568 switch (drawingModel) { 569 #ifndef NP_NO_QUICKDRAW 570 case NPDrawingModelQuickDraw: { 571 PortState_QD *qdPortState = (PortState_QD *)portState; 572 WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef]; 573 CGrafPtr port = GetWindowPort(windowRef); 574 575 SetPort(port); 576 577 if (qdPortState->forUpdate) 578 ValidWindowRgn(windowRef, qdPortState->clipRegion); 579 580 SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v); 581 582 SetPortClipRegion(port, qdPortState->oldClipRegion); 583 if (qdPortState->forUpdate) 584 SetPortVisibleRegion(port, qdPortState->oldVisibleRegion); 585 586 DisposeRgn(qdPortState->oldClipRegion); 587 DisposeRgn(qdPortState->oldVisibleRegion); 588 DisposeRgn(qdPortState->clipRegion); 589 590 SetGWorld(qdPortState->oldPort, qdPortState->oldDevice); 591 break; 592 } 593 #endif /* NP_NO_QUICKDRAW */ 594 595 case NPDrawingModelCoreGraphics: { 596 ASSERT([NSView focusView] == self); 597 598 CGContextRef context = ((PortState_CG *)portState)->context; 599 ASSERT(!nPort.cgPort.context || (context == nPort.cgPort.context)); 600 CGContextRestoreGState(context); 601 break; 602 } 603 604 case NPDrawingModelCoreAnimation: 605 ASSERT(portState == (PortState)1); 606 break; 607 default: 608 ASSERT_NOT_REACHED(); 609 break; 610 } 611 } 612 613 - (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect 614 { 615 if (![self window]) 616 return NO; 617 ASSERT(event); 618 619 if (!_isStarted) 620 return NO; 621 622 ASSERT([_pluginPackage.get() pluginFuncs]->event); 623 624 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow. 625 // We probably don't want more general reentrancy protection; we are really 626 // protecting only against this one case, which actually comes up when 627 // you first install the SVG viewer plug-in. 628 if (inSetWindow) 629 return NO; 630 631 Frame* frame = core([self webFrame]); 632 if (!frame) 633 return NO; 634 Page* page = frame->page(); 635 if (!page) 636 return NO; 637 638 // Can only send drawRect (updateEvt) to CoreGraphics plugins when actually drawing 639 ASSERT((drawingModel != NPDrawingModelCoreGraphics) || !eventIsDrawRect || [NSView focusView] == self); 640 641 PortState portState = NULL; 642 643 if (isDrawingModelQuickDraw(drawingModel) || (drawingModel != NPDrawingModelCoreAnimation && eventIsDrawRect)) { 644 // In CoreGraphics mode, the port state only needs to be saved/set when redrawing the plug-in view. 645 // The plug-in is not allowed to draw at any other time. 646 portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect]; 647 // We may have changed the window, so inform the plug-in. 648 [self setWindowIfNecessary]; 649 } 650 651 #if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW) 652 // Draw green to help debug. 653 // If we see any green we know something's wrong. 654 // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined. 655 if (isDrawingModelQuickDraw(drawingModel) && eventIsDrawRect) { 656 ForeColor(greenColor); 657 const ::Rect bigRect = { -10000, -10000, 10000, 10000 }; 658 PaintRect(&bigRect); 659 ForeColor(blackColor); 660 } 661 #endif 662 663 // Temporarily retain self in case the plug-in view is released while sending an event. 664 [[self retain] autorelease]; 665 666 BOOL acceptedEvent; 667 [self willCallPlugInFunction]; 668 // Set the pluginAllowPopup flag. 669 ASSERT(_eventHandler); 670 bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); 671 frame->script()->setAllowPopupsFromPlugin(_eventHandler->currentEventIsUserGesture()); 672 { 673 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 674 acceptedEvent = [_pluginPackage.get() pluginFuncs]->event(plugin, event); 675 } 676 // Restore the old pluginAllowPopup flag. 677 frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); 678 [self didCallPlugInFunction]; 679 680 if (portState) { 681 if ([self currentWindow]) 682 [self restorePortState:portState]; 683 if (portState != (PortState)1) 684 free(portState); 685 } 686 687 return acceptedEvent; 688 } 689 690 - (void)windowFocusChanged:(BOOL)hasFocus 691 { 692 _eventHandler->windowFocusChanged(hasFocus); 693 } 694 695 - (void)sendDrawRectEvent:(NSRect)rect 696 { 697 ASSERT(_eventHandler); 698 699 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 700 _eventHandler->drawRect(context, rect); 701 } 702 703 - (void)stopTimers 704 { 705 [super stopTimers]; 706 707 if (_eventHandler) 708 _eventHandler->stopTimers(); 709 710 if (!timers) 711 return; 712 713 HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end(); 714 for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { 715 PluginTimer* timer = it->second; 716 timer->stop(); 717 } 718 } 719 720 - (void)startTimers 721 { 722 [super startTimers]; 723 724 // If the plugin is completely obscured (scrolled out of view, for example), then we will 725 // send null events at a reduced rate. 726 _eventHandler->startTimers(_isCompletelyObscured); 727 728 if (!timers) 729 return; 730 731 HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end(); 732 for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { 733 PluginTimer* timer = it->second; 734 ASSERT(!timer->isActive()); 735 timer->start(_isCompletelyObscured); 736 } 737 } 738 739 - (void)focusChanged 740 { 741 // We need to null check the event handler here because 742 // the plug-in view can resign focus after it's been stopped 743 // and the event handler has been deleted. 744 if (_eventHandler) 745 _eventHandler->focusChanged(_hasFocus); 746 } 747 748 - (void)mouseDown:(NSEvent *)theEvent 749 { 750 if (!_isStarted) 751 return; 752 753 _eventHandler->mouseDown(theEvent); 754 } 755 756 - (void)mouseUp:(NSEvent *)theEvent 757 { 758 if (!_isStarted) 759 return; 760 761 _eventHandler->mouseUp(theEvent); 762 } 763 764 - (void)handleMouseEntered:(NSEvent *)theEvent 765 { 766 if (!_isStarted) 767 return; 768 769 // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one. 770 [[NSCursor arrowCursor] set]; 771 772 _eventHandler->mouseEntered(theEvent); 773 } 774 775 - (void)handleMouseExited:(NSEvent *)theEvent 776 { 777 if (!_isStarted) 778 return; 779 780 _eventHandler->mouseExited(theEvent); 781 782 // 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 783 // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin. 784 [[NSCursor arrowCursor] set]; 785 } 786 787 - (void)handleMouseMoved:(NSEvent *)theEvent 788 { 789 if (!_isStarted) 790 return; 791 792 _eventHandler->mouseMoved(theEvent); 793 } 794 795 - (void)mouseDragged:(NSEvent *)theEvent 796 { 797 if (!_isStarted) 798 return; 799 800 _eventHandler->mouseDragged(theEvent); 801 } 802 803 - (void)scrollWheel:(NSEvent *)theEvent 804 { 805 if (!_isStarted) { 806 [super scrollWheel:theEvent]; 807 return; 808 } 809 810 if (!_eventHandler->scrollWheel(theEvent)) 811 [super scrollWheel:theEvent]; 812 } 813 814 - (void)keyUp:(NSEvent *)theEvent 815 { 816 if (!_isStarted) 817 return; 818 819 _eventHandler->keyUp(theEvent); 820 } 821 822 - (void)keyDown:(NSEvent *)theEvent 823 { 824 if (!_isStarted) 825 return; 826 827 _eventHandler->keyDown(theEvent); 828 } 829 830 - (void)flagsChanged:(NSEvent *)theEvent 831 { 832 if (!_isStarted) 833 return; 834 835 _eventHandler->flagsChanged(theEvent); 836 } 837 838 - (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character 839 { 840 if (!_isStarted) 841 return; 842 843 _eventHandler->syntheticKeyDownWithCommandModifier(keyCode, character); 844 } 845 846 - (void)privateBrowsingModeDidChange 847 { 848 if (!_isStarted) 849 return; 850 851 NPBool value = _isPrivateBrowsingEnabled; 852 853 [self willCallPlugInFunction]; 854 { 855 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 856 if ([_pluginPackage.get() pluginFuncs]->setvalue) 857 [_pluginPackage.get() pluginFuncs]->setvalue(plugin, NPNVprivateModeBool, &value); 858 } 859 [self didCallPlugInFunction]; 860 } 861 862 // MARK: WEB_NETSCAPE_PLUGIN 863 864 - (BOOL)isNewWindowEqualToOldWindow 865 { 866 if (window.x != lastSetWindow.x) 867 return NO; 868 if (window.y != lastSetWindow.y) 869 return NO; 870 if (window.width != lastSetWindow.width) 871 return NO; 872 if (window.height != lastSetWindow.height) 873 return NO; 874 if (window.clipRect.top != lastSetWindow.clipRect.top) 875 return NO; 876 if (window.clipRect.left != lastSetWindow.clipRect.left) 877 return NO; 878 if (window.clipRect.bottom != lastSetWindow.clipRect.bottom) 879 return NO; 880 if (window.clipRect.right != lastSetWindow.clipRect.right) 881 return NO; 882 if (window.type != lastSetWindow.type) 883 return NO; 884 885 switch (drawingModel) { 886 #ifndef NP_NO_QUICKDRAW 887 case NPDrawingModelQuickDraw: 888 if (nPort.qdPort.portx != lastSetPort.qdPort.portx) 889 return NO; 890 if (nPort.qdPort.porty != lastSetPort.qdPort.porty) 891 return NO; 892 if (nPort.qdPort.port != lastSetPort.qdPort.port) 893 return NO; 894 break; 895 #endif /* NP_NO_QUICKDRAW */ 896 897 case NPDrawingModelCoreGraphics: 898 if (nPort.cgPort.window != lastSetPort.cgPort.window) 899 return NO; 900 if (nPort.cgPort.context != lastSetPort.cgPort.context) 901 return NO; 902 break; 903 904 case NPDrawingModelCoreAnimation: 905 if (window.window != lastSetWindow.window) 906 return NO; 907 break; 908 default: 909 ASSERT_NOT_REACHED(); 910 break; 911 } 912 913 return YES; 914 } 915 916 -(void)tellQuickTimeToChill 917 { 918 #ifndef NP_NO_QUICKDRAW 919 ASSERT(isDrawingModelQuickDraw(drawingModel)); 920 921 // Make a call to the secret QuickDraw API that makes QuickTime calm down. 922 WindowRef windowRef = (WindowRef)[[self window] windowRef]; 923 if (!windowRef) { 924 return; 925 } 926 CGrafPtr port = GetWindowPort(windowRef); 927 ::Rect bounds; 928 GetPortBounds(port, &bounds); 929 WKCallDrawingNotification(port, &bounds); 930 #endif /* NP_NO_QUICKDRAW */ 931 } 932 933 - (void)updateAndSetWindow 934 { 935 // A plug-in can only update if it's (1) already been started (2) isn't stopped 936 // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not 937 // be hidden and be attached to a window. There are two exceptions to this rule: 938 // 939 // Exception 1: QuickDraw plug-ins must be manually told when to stop writing 940 // bits to the window backing store, thus to do so requires a new call to 941 // NPP_SetWindow() with an empty NPWindow struct. 942 // 943 // Exception 2: CoreGraphics plug-ins expect to have their drawable area updated 944 // when they are moved to a background tab, via a NPP_SetWindow call. This is 945 // accomplished by allowing -saveAndSetNewPortStateForUpdate to "clip-out" the window's 946 // clipRect. Flash is curently an exception to this. See 6453738. 947 // 948 949 if (!_isStarted) 950 return; 951 952 #ifdef NP_NO_QUICKDRAW 953 if (![self canDraw]) 954 return; 955 #else 956 if (drawingModel == NPDrawingModelQuickDraw) 957 [self tellQuickTimeToChill]; 958 else if (drawingModel == NPDrawingModelCoreGraphics && ![self canDraw] && _isFlash) { 959 // The Flash plug-in does not expect an NPP_SetWindow call from WebKit in this case. 960 // See Exception 2 above. 961 return; 962 } 963 #endif // NP_NO_QUICKDRAW 964 965 BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw]; 966 967 PortState portState = [self saveAndSetNewPortState]; 968 if (portState) { 969 [self setWindowIfNecessary]; 970 [self restorePortState:portState]; 971 if (portState != (PortState)1) 972 free(portState); 973 } else if (drawingModel == NPDrawingModelCoreGraphics) 974 [self setWindowIfNecessary]; 975 976 if (didLockFocus) 977 [self unlockFocus]; 978 } 979 980 - (void)setWindowIfNecessary 981 { 982 if (!_isStarted) 983 return; 984 985 if (![self isNewWindowEqualToOldWindow]) { 986 // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow. 987 // We probably don't want more general reentrancy protection; we are really 988 // protecting only against this one case, which actually comes up when 989 // you first install the SVG viewer plug-in. 990 NPError npErr; 991 992 BOOL wasInSetWindow = inSetWindow; 993 inSetWindow = YES; 994 [self willCallPlugInFunction]; 995 { 996 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 997 npErr = [_pluginPackage.get() pluginFuncs]->setwindow(plugin, &window); 998 } 999 [self didCallPlugInFunction]; 1000 inSetWindow = wasInSetWindow; 1001 1002 #ifndef NDEBUG 1003 switch (drawingModel) { 1004 #ifndef NP_NO_QUICKDRAW 1005 case NPDrawingModelQuickDraw: 1006 LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d", 1007 npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height); 1008 break; 1009 #endif /* NP_NO_QUICKDRAW */ 1010 1011 case NPDrawingModelCoreGraphics: 1012 LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d window.clipRect size:%dx%d", 1013 npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height, 1014 window.clipRect.right - window.clipRect.left, window.clipRect.bottom - window.clipRect.top); 1015 break; 1016 1017 case NPDrawingModelCoreAnimation: 1018 LOG(Plugins, "NPP_SetWindow (CoreAnimation): %d, window=%p window.x:%d window.y:%d window.width:%d window.height:%d", 1019 npErr, window.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height); 1020 break; 1021 1022 default: 1023 ASSERT_NOT_REACHED(); 1024 break; 1025 } 1026 #endif /* !defined(NDEBUG) */ 1027 1028 lastSetWindow = window; 1029 lastSetPort = nPort; 1030 } 1031 } 1032 1033 + (void)setCurrentPluginView:(WebNetscapePluginView *)view 1034 { 1035 currentPluginView = view; 1036 } 1037 1038 + (WebNetscapePluginView *)currentPluginView 1039 { 1040 return currentPluginView; 1041 } 1042 1043 - (BOOL)createPlugin 1044 { 1045 // Open the plug-in package so it remains loaded while our plugin uses it 1046 [_pluginPackage.get() open]; 1047 1048 // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel 1049 drawingModel = (NPDrawingModel)-1; 1050 1051 // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model. 1052 eventModel = (NPEventModel)-1; 1053 1054 NPError npErr = [self _createPlugin]; 1055 if (npErr != NPERR_NO_ERROR) { 1056 LOG_ERROR("NPP_New failed with error: %d", npErr); 1057 [self _destroyPlugin]; 1058 [_pluginPackage.get() close]; 1059 return NO; 1060 } 1061 1062 if (drawingModel == (NPDrawingModel)-1) { 1063 #ifndef NP_NO_QUICKDRAW 1064 // Default to QuickDraw if the plugin did not specify a drawing model. 1065 drawingModel = NPDrawingModelQuickDraw; 1066 #else 1067 // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. 1068 drawingModel = NPDrawingModelCoreGraphics; 1069 #endif 1070 } 1071 1072 if (eventModel == (NPEventModel)-1) { 1073 // If the plug-in did not specify a drawing model we default to Carbon when it is available. 1074 #ifndef NP_NO_CARBON 1075 eventModel = NPEventModelCarbon; 1076 #else 1077 eventModel = NPEventModelCocoa; 1078 #endif // NP_NO_CARBON 1079 } 1080 1081 #ifndef NP_NO_CARBON 1082 if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) { 1083 LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get()); 1084 [self _destroyPlugin]; 1085 [_pluginPackage.get() close]; 1086 1087 return NO; 1088 } 1089 #endif // NP_NO_CARBON 1090 1091 #ifndef BUILDING_ON_TIGER 1092 if (drawingModel == NPDrawingModelCoreAnimation) { 1093 void *value = 0; 1094 if ([_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { 1095 1096 // The plug-in gives us a retained layer. 1097 _pluginLayer.adoptNS((CALayer *)value); 1098 1099 BOOL accleratedCompositingEnabled = false; 1100 #if USE(ACCELERATED_COMPOSITING) 1101 accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; 1102 #endif 1103 if (accleratedCompositingEnabled) { 1104 // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. 1105 #ifndef BUILDING_ON_LEOPARD 1106 // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry 1107 // in order to get the coordinate system right. 1108 RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef()); 1109 1110 _pluginLayer.adoptNS([[CALayer alloc] init]); 1111 _pluginLayer.get().bounds = realPluginLayer.get().bounds; 1112 _pluginLayer.get().geometryFlipped = YES; 1113 1114 realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 1115 [_pluginLayer.get() addSublayer:realPluginLayer.get()]; 1116 #endif 1117 // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc() 1118 // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033 1119 core([self webFrame])->view()->enterCompositingMode(); 1120 [self element]->setNeedsStyleRecalc(SyntheticStyleChange); 1121 } else 1122 [self setWantsLayer:YES]; 1123 1124 LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _pluginLayer.get()); 1125 } 1126 1127 ASSERT(_pluginLayer); 1128 } 1129 #endif 1130 1131 // Create the event handler 1132 _eventHandler.set(WebNetscapePluginEventHandler::create(self)); 1133 1134 return YES; 1135 } 1136 1137 #ifndef BUILDING_ON_TIGER 1138 // FIXME: This method is an ideal candidate to move up to the base class 1139 - (CALayer *)pluginLayer 1140 { 1141 return _pluginLayer.get(); 1142 } 1143 1144 - (void)setLayer:(CALayer *)newLayer 1145 { 1146 [super setLayer:newLayer]; 1147 1148 if (newLayer && _pluginLayer) { 1149 _pluginLayer.get().frame = [newLayer frame]; 1150 _pluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; 1151 [newLayer addSublayer:_pluginLayer.get()]; 1152 } 1153 } 1154 #endif 1155 1156 - (void)loadStream 1157 { 1158 if ([self _shouldCancelSrcStream]) 1159 return; 1160 1161 if (_loadManually) { 1162 [self _redeliverStream]; 1163 return; 1164 } 1165 1166 // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "". 1167 // Check for this and don't start a load in this case. 1168 if (_sourceURL && ![_sourceURL.get() _web_isEmpty]) { 1169 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_sourceURL.get()]; 1170 [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()]; 1171 [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO]; 1172 } 1173 } 1174 1175 - (BOOL)shouldStop 1176 { 1177 // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling 1178 // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said 1179 // plugin-function returns. 1180 // See <rdar://problem/4480737>. 1181 if (pluginFunctionCallDepth > 0) { 1182 shouldStopSoon = YES; 1183 return NO; 1184 } 1185 1186 return YES; 1187 } 1188 1189 - (void)destroyPlugin 1190 { 1191 // To stop active streams it's necessary to invoke stop() on a copy 1192 // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect 1193 // of removing a stream from this hash set. 1194 Vector<RefPtr<WebNetscapePluginStream> > streamsCopy; 1195 copyToVector(streams, streamsCopy); 1196 for (size_t i = 0; i < streamsCopy.size(); i++) 1197 streamsCopy[i]->stop(); 1198 1199 [[_pendingFrameLoads.get() allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil]; 1200 [NSObject cancelPreviousPerformRequestsWithTarget:self]; 1201 1202 // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted. 1203 lastSetWindow.type = (NPWindowType)0; 1204 1205 #ifndef BUILDING_ON_TIGER 1206 _pluginLayer = 0; 1207 #endif 1208 1209 [self _destroyPlugin]; 1210 [_pluginPackage.get() close]; 1211 1212 _eventHandler.clear(); 1213 } 1214 1215 - (NPEventModel)eventModel 1216 { 1217 return eventModel; 1218 } 1219 1220 - (NPP)plugin 1221 { 1222 return plugin; 1223 } 1224 1225 - (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values 1226 { 1227 ASSERT([keys count] == [values count]); 1228 1229 // Convert the attributes to 2 C string arrays. 1230 // These arrays are passed to NPP_New, but the strings need to be 1231 // modifiable and live the entire life of the plugin. 1232 1233 // The Java plug-in requires the first argument to be the base URL 1234 if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) { 1235 cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *)); 1236 cValues = (char **)malloc(([values count] + 1) * sizeof(char *)); 1237 cAttributes[0] = strdup("DOCBASE"); 1238 cValues[0] = strdup([_baseURL.get() _web_URLCString]); 1239 argsCount++; 1240 } else { 1241 cAttributes = (char **)malloc([keys count] * sizeof(char *)); 1242 cValues = (char **)malloc([values count] * sizeof(char *)); 1243 } 1244 1245 BOOL isWMP = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.WMP.defaultplugin"; 1246 1247 unsigned i; 1248 unsigned count = [keys count]; 1249 for (i = 0; i < count; i++) { 1250 NSString *key = [keys objectAtIndex:i]; 1251 NSString *value = [values objectAtIndex:i]; 1252 if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) { 1253 specifiedHeight = [value intValue]; 1254 } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) { 1255 specifiedWidth = [value intValue]; 1256 } 1257 // Avoid Window Media Player crash when these attributes are present. 1258 if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) { 1259 continue; 1260 } 1261 cAttributes[argsCount] = strdup([key UTF8String]); 1262 cValues[argsCount] = strdup([value UTF8String]); 1263 LOG(Plugins, "%@ = %@", key, value); 1264 argsCount++; 1265 } 1266 } 1267 1268 - (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString 1269 callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc 1270 context:(void*)context 1271 { 1272 if (!_containerChecksInProgress) 1273 _containerChecksInProgress = [[NSMutableDictionary alloc] init]; 1274 1275 NSString *frameName = frameNameCString ? [NSString stringWithCString:frameNameCString encoding:NSISOLatin1StringEncoding] : nil; 1276 1277 ++_currentContainerCheckRequestID; 1278 WebNetscapeContainerCheckContextInfo *contextInfo = [[WebNetscapeContainerCheckContextInfo alloc] initWithCheckRequestID:_currentContainerCheckRequestID 1279 callbackFunc:callbackFunc 1280 context:context]; 1281 1282 WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[self requestWithURLCString:urlCString] 1283 target:frameName 1284 resultObject:self 1285 selector:@selector(_containerCheckResult:contextInfo:) 1286 controller:self 1287 contextInfo:contextInfo]; 1288 1289 [contextInfo release]; 1290 [_containerChecksInProgress setObject:check forKey:[NSNumber numberWithInt:_currentContainerCheckRequestID]]; 1291 [check start]; 1292 1293 return _currentContainerCheckRequestID; 1294 } 1295 1296 - (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo 1297 { 1298 ASSERT([contextInfo isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); 1299 void (*pluginCallback)(NPP npp, uint32_t, NPBool, void*) = [contextInfo callback]; 1300 1301 if (!pluginCallback) { 1302 ASSERT_NOT_REACHED(); 1303 return; 1304 } 1305 1306 pluginCallback([self plugin], [contextInfo checkRequestID], (policy == PolicyUse), [contextInfo context]); 1307 } 1308 1309 - (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID 1310 { 1311 WebPluginContainerCheck *check = (WebPluginContainerCheck *)[_containerChecksInProgress objectForKey:[NSNumber numberWithInt:checkID]]; 1312 1313 if (!check) 1314 return; 1315 1316 [check cancel]; 1317 [_containerChecksInProgress removeObjectForKey:[NSNumber numberWithInt:checkID]]; 1318 } 1319 1320 // WebPluginContainerCheck automatically calls this method after invoking our _containerCheckResult: selector. 1321 // It works this way because calling -[WebPluginContainerCheck cancel] allows it to do it's teardown process. 1322 - (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck 1323 { 1324 ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); 1325 WebPluginContainerCheck *check = (WebPluginContainerCheck *)webPluginContainerCheck; 1326 ASSERT([[check contextInfo] isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); 1327 1328 [self cancelCheckIfAllowedToLoadURL:[[check contextInfo] checkRequestID]]; 1329 } 1330 1331 #ifdef BUILDING_ON_TIGER 1332 // The Tiger compiler requires these two methods be present. Otherwise it doesn't think WebNetscapePluginView 1333 // conforms to the WebPluginContainerCheckController protocol. 1334 - (WebView *)webView 1335 { 1336 return [super webView]; 1337 } 1338 1339 - (WebFrame *)webFrame 1340 { 1341 return [super webFrame]; 1342 } 1343 #endif 1344 1345 // MARK: NSVIEW 1346 1347 - (id)initWithFrame:(NSRect)frame 1348 pluginPackage:(WebNetscapePluginPackage *)pluginPackage 1349 URL:(NSURL *)URL 1350 baseURL:(NSURL *)baseURL 1351 MIMEType:(NSString *)MIME 1352 attributeKeys:(NSArray *)keys 1353 attributeValues:(NSArray *)values 1354 loadManually:(BOOL)loadManually 1355 element:(PassRefPtr<WebCore::HTMLPlugInElement>)element 1356 { 1357 self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element]; 1358 if (!self) 1359 return nil; 1360 1361 _pendingFrameLoads.adoptNS([[NSMutableDictionary alloc] init]); 1362 1363 // load the plug-in if it is not already loaded 1364 if (![pluginPackage load]) { 1365 [self release]; 1366 return nil; 1367 } 1368 1369 return self; 1370 } 1371 1372 - (id)initWithFrame:(NSRect)frame 1373 { 1374 ASSERT_NOT_REACHED(); 1375 return nil; 1376 } 1377 1378 - (void)fini 1379 { 1380 #ifndef NP_NO_QUICKDRAW 1381 if (offscreenGWorld) 1382 DisposeGWorld(offscreenGWorld); 1383 #endif 1384 1385 for (unsigned i = 0; i < argsCount; i++) { 1386 free(cAttributes[i]); 1387 free(cValues[i]); 1388 } 1389 free(cAttributes); 1390 free(cValues); 1391 1392 ASSERT(!_eventHandler); 1393 1394 if (timers) { 1395 deleteAllValues(*timers); 1396 delete timers; 1397 } 1398 1399 [_containerChecksInProgress release]; 1400 } 1401 1402 - (void)disconnectStream:(WebNetscapePluginStream*)stream 1403 { 1404 streams.remove(stream); 1405 } 1406 1407 - (void)dealloc 1408 { 1409 ASSERT(!_isStarted); 1410 ASSERT(!plugin); 1411 1412 [self fini]; 1413 1414 [super dealloc]; 1415 } 1416 1417 - (void)finalize 1418 { 1419 ASSERT_MAIN_THREAD(); 1420 ASSERT(!_isStarted); 1421 1422 [self fini]; 1423 1424 [super finalize]; 1425 } 1426 1427 - (void)drawRect:(NSRect)rect 1428 { 1429 if (_cachedSnapshot) { 1430 NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; 1431 [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; 1432 return; 1433 } 1434 1435 if (drawingModel == NPDrawingModelCoreAnimation && (!_snapshotting || ![self supportsSnapshotting])) 1436 return; 1437 1438 if (!_isStarted) 1439 return; 1440 1441 if ([NSGraphicsContext currentContextDrawingToScreen] || _isFlash) 1442 [self sendDrawRectEvent:rect]; 1443 else { 1444 NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap]; 1445 if (printedPluginBitmap) { 1446 // Flip the bitmap before drawing because the QuickDraw port is flipped relative 1447 // to this view. 1448 CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 1449 CGContextSaveGState(cgContext); 1450 NSRect bounds = [self bounds]; 1451 CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds)); 1452 CGContextScaleCTM(cgContext, 1.0f, -1.0f); 1453 [printedPluginBitmap drawInRect:bounds]; 1454 CGContextRestoreGState(cgContext); 1455 } 1456 } 1457 } 1458 1459 - (NPObject *)createPluginScriptableObject 1460 { 1461 if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted) 1462 return NULL; 1463 1464 NPObject *value = NULL; 1465 NPError error; 1466 [self willCallPlugInFunction]; 1467 { 1468 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 1469 error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginScriptableNPObject, &value); 1470 } 1471 [self didCallPlugInFunction]; 1472 if (error != NPERR_NO_ERROR) 1473 return NULL; 1474 1475 return value; 1476 } 1477 1478 - (void)willCallPlugInFunction 1479 { 1480 ASSERT(plugin); 1481 1482 // Could try to prevent infinite recursion here, but it's probably not worth the effort. 1483 pluginFunctionCallDepth++; 1484 } 1485 1486 - (void)didCallPlugInFunction 1487 { 1488 ASSERT(pluginFunctionCallDepth > 0); 1489 pluginFunctionCallDepth--; 1490 1491 // If -stop was called while we were calling into a plug-in function, and we're no longer 1492 // inside a plug-in function, stop now. 1493 if (pluginFunctionCallDepth == 0 && shouldStopSoon) { 1494 shouldStopSoon = NO; 1495 [self stop]; 1496 } 1497 } 1498 1499 -(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response 1500 { 1501 ASSERT(_loadManually); 1502 ASSERT(!_manualStream); 1503 1504 _manualStream = WebNetscapePluginStream::create(core([self webFrame])->loader()); 1505 } 1506 1507 - (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data 1508 { 1509 ASSERT(_loadManually); 1510 ASSERT(_manualStream); 1511 1512 _dataLengthReceived += [data length]; 1513 1514 if (!_isStarted) 1515 return; 1516 1517 if (!_manualStream->plugin()) { 1518 // Check if the load should be cancelled 1519 if ([self _shouldCancelSrcStream]) { 1520 NSURLResponse *response = [[self dataSource] response]; 1521 1522 NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad 1523 contentURL:[response URL] 1524 pluginPageURL:nil 1525 pluginName:nil // FIXME: Get this from somewhere 1526 MIMEType:[response MIMEType]]; 1527 [[self dataSource] _documentLoader]->cancelMainResourceLoad(error); 1528 [error release]; 1529 return; 1530 } 1531 1532 _manualStream->setRequestURL([[[self dataSource] request] URL]); 1533 _manualStream->setPlugin([self plugin]); 1534 ASSERT(_manualStream->plugin()); 1535 1536 _manualStream->startStreamWithResponse([[self dataSource] response]); 1537 } 1538 1539 if (_manualStream->plugin()) 1540 _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]); 1541 } 1542 1543 - (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error 1544 { 1545 ASSERT(_loadManually); 1546 1547 _error = error; 1548 1549 if (!_isStarted) { 1550 return; 1551 } 1552 1553 _manualStream->destroyStreamWithError(error); 1554 } 1555 1556 - (void)pluginViewFinishedLoading:(NSView *)pluginView 1557 { 1558 ASSERT(_loadManually); 1559 ASSERT(_manualStream); 1560 1561 if (_isStarted) 1562 _manualStream->didFinishLoading(0); 1563 } 1564 1565 - (NSTextInputContext *)inputContext 1566 { 1567 return nil; 1568 } 1569 1570 @end 1571 1572 @implementation WebNetscapePluginView (WebNPPCallbacks) 1573 1574 - (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest 1575 { 1576 // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called 1577 // if we are stopped since this method is called after a delay and we call 1578 // cancelPreviousPerformRequestsWithTarget inside of stop. 1579 if (!_isStarted) { 1580 return; 1581 } 1582 1583 NSURL *URL = [[JSPluginRequest request] URL]; 1584 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1585 ASSERT(JSString); 1586 1587 NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]]; 1588 1589 // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. 1590 if (!_isStarted) { 1591 return; 1592 } 1593 1594 if ([JSPluginRequest frameName] != nil) { 1595 // FIXME: If the result is a string, we probably want to put that string into the frame. 1596 if ([JSPluginRequest sendNotification]) { 1597 [self willCallPlugInFunction]; 1598 { 1599 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 1600 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]); 1601 } 1602 [self didCallPlugInFunction]; 1603 } 1604 } else if ([result length] > 0) { 1605 // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. 1606 NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; 1607 1608 RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]); 1609 1610 RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL 1611 MIMEType:@"text/plain" 1612 expectedContentLength:[JSData length] 1613 textEncodingName:nil]); 1614 1615 stream->startStreamWithResponse(response.get()); 1616 stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); 1617 stream->didFinishLoading(0); 1618 } 1619 } 1620 1621 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason 1622 { 1623 ASSERT(_isStarted); 1624 1625 WebPluginRequest *pluginRequest = [_pendingFrameLoads.get() objectForKey:webFrame]; 1626 ASSERT(pluginRequest != nil); 1627 ASSERT([pluginRequest sendNotification]); 1628 1629 [self willCallPlugInFunction]; 1630 { 1631 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 1632 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]); 1633 } 1634 [self didCallPlugInFunction]; 1635 1636 [_pendingFrameLoads.get() removeObjectForKey:webFrame]; 1637 [webFrame _setInternalLoadDelegate:nil]; 1638 } 1639 1640 - (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error 1641 { 1642 NPReason reason = NPRES_DONE; 1643 if (error != nil) 1644 reason = WebNetscapePluginStream::reasonForError(error); 1645 [self webFrame:webFrame didFinishLoadWithReason:reason]; 1646 } 1647 1648 - (void)loadPluginRequest:(WebPluginRequest *)pluginRequest 1649 { 1650 NSURLRequest *request = [pluginRequest request]; 1651 NSString *frameName = [pluginRequest frameName]; 1652 WebFrame *frame = nil; 1653 1654 NSURL *URL = [request URL]; 1655 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1656 1657 ASSERT(frameName || JSString); 1658 1659 if (frameName) { 1660 // FIXME - need to get rid of this window creation which 1661 // bypasses normal targeted link handling 1662 frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName)); 1663 if (frame == nil) { 1664 WebView *currentWebView = [self webView]; 1665 NSDictionary *features = [[NSDictionary alloc] init]; 1666 WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView 1667 createWebViewWithRequest:nil 1668 windowFeatures:features]; 1669 [features release]; 1670 1671 if (!newWebView) { 1672 if ([pluginRequest sendNotification]) { 1673 [self willCallPlugInFunction]; 1674 { 1675 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 1676 [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]); 1677 } 1678 [self didCallPlugInFunction]; 1679 } 1680 return; 1681 } 1682 1683 frame = [newWebView mainFrame]; 1684 core(frame)->tree()->setName(frameName); 1685 [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; 1686 } 1687 } 1688 1689 if (JSString) { 1690 ASSERT(frame == nil || [self webFrame] == frame); 1691 [self evaluateJavaScriptPluginRequest:pluginRequest]; 1692 } else { 1693 [frame loadRequest:request]; 1694 if ([pluginRequest sendNotification]) { 1695 // Check if another plug-in view or even this view is waiting for the frame to load. 1696 // If it is, tell it that the load was cancelled because it will be anyway. 1697 WebNetscapePluginView *view = [frame _internalLoadDelegate]; 1698 if (view != nil) { 1699 ASSERT([view isKindOfClass:[WebNetscapePluginView class]]); 1700 [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; 1701 } 1702 [_pendingFrameLoads.get() _webkit_setObject:pluginRequest forUncopiedKey:frame]; 1703 [frame _setInternalLoadDelegate:self]; 1704 } 1705 } 1706 } 1707 1708 - (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification 1709 { 1710 NSURL *URL = [request URL]; 1711 1712 if (!URL) 1713 return NPERR_INVALID_URL; 1714 1715 // Don't allow requests to be loaded when the document loader is stopping all loaders. 1716 if ([[self dataSource] _documentLoader]->isStopping()) 1717 return NPERR_GENERIC_ERROR; 1718 1719 NSString *target = nil; 1720 if (cTarget) { 1721 // Find the frame given the target string. 1722 target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; 1723 } 1724 WebFrame *frame = [self webFrame]; 1725 1726 // don't let a plugin start any loads if it is no longer part of a document that is being 1727 // displayed unless the loads are in the same frame as the plugin. 1728 if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() && 1729 (!cTarget || [frame findFrameNamed:target] != frame)) { 1730 return NPERR_GENERIC_ERROR; 1731 } 1732 1733 NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; 1734 if (JSString != nil) { 1735 if (![[[self webView] preferences] isJavaScriptEnabled]) { 1736 // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. 1737 return NPERR_GENERIC_ERROR; 1738 } else if (cTarget == NULL && _mode == NP_FULL) { 1739 // Don't allow a JavaScript request from a standalone plug-in that is self-targetted 1740 // because this can cause the user to be redirected to a blank page (3424039). 1741 return NPERR_INVALID_PARAM; 1742 } 1743 } else { 1744 if (!core([self webFrame])->document()->securityOrigin()->canDisplay(URL)) 1745 return NPERR_GENERIC_ERROR; 1746 } 1747 1748 if (cTarget || JSString) { 1749 // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't 1750 // want to potentially kill the plug-in inside of its URL request. 1751 1752 if (JSString && target && [frame findFrameNamed:target] != frame) { 1753 // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. 1754 return NPERR_INVALID_PARAM; 1755 } 1756 1757 bool currentEventIsUserGesture = false; 1758 if (_eventHandler) 1759 currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture(); 1760 1761 WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request 1762 frameName:target 1763 notifyData:notifyData 1764 sendNotification:sendNotification 1765 didStartFromUserGesture:currentEventIsUserGesture]; 1766 [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0]; 1767 [pluginRequest release]; 1768 } else { 1769 RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData); 1770 1771 streams.add(stream.get()); 1772 stream->start(); 1773 } 1774 1775 return NPERR_NO_ERROR; 1776 } 1777 1778 -(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData 1779 { 1780 LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget); 1781 1782 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1783 return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES]; 1784 } 1785 1786 -(NPError)getURL:(const char *)URLCString target:(const char *)cTarget 1787 { 1788 LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget); 1789 1790 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1791 return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO]; 1792 } 1793 1794 - (NPError)_postURL:(const char *)URLCString 1795 target:(const char *)target 1796 len:(UInt32)len 1797 buf:(const char *)buf 1798 file:(NPBool)file 1799 notifyData:(void *)notifyData 1800 sendNotification:(BOOL)sendNotification 1801 allowHeaders:(BOOL)allowHeaders 1802 { 1803 if (!URLCString || !len || !buf) { 1804 return NPERR_INVALID_PARAM; 1805 } 1806 1807 NSData *postData = nil; 1808 1809 if (file) { 1810 // If we're posting a file, buf is either a file URL or a path to the file. 1811 NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1); 1812 if (!bufString) { 1813 return NPERR_INVALID_PARAM; 1814 } 1815 NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString]; 1816 NSString *path; 1817 if ([fileURL isFileURL]) { 1818 path = [fileURL path]; 1819 } else { 1820 path = bufString; 1821 } 1822 postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; 1823 CFRelease(bufString); 1824 if (!postData) { 1825 return NPERR_FILE_NOT_FOUND; 1826 } 1827 } else { 1828 postData = [NSData dataWithBytes:buf length:len]; 1829 } 1830 1831 if ([postData length] == 0) { 1832 return NPERR_INVALID_PARAM; 1833 } 1834 1835 NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; 1836 [request setHTTPMethod:@"POST"]; 1837 1838 if (allowHeaders) { 1839 if ([postData _web_startsWithBlankLine]) { 1840 postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)]; 1841 } else { 1842 NSInteger location = [postData _web_locationAfterFirstBlankLine]; 1843 if (location != NSNotFound) { 1844 // If the blank line is somewhere in the middle of postData, everything before is the header. 1845 NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)]; 1846 NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; 1847 unsigned dataLength = [postData length] - location; 1848 1849 // Sometimes plugins like to set Content-Length themselves when they post, 1850 // but WebFoundation does not like that. So we will remove the header 1851 // and instead truncate the data to the requested length. 1852 NSString *contentLength = [header objectForKey:@"Content-Length"]; 1853 1854 if (contentLength != nil) 1855 dataLength = min<unsigned>([contentLength intValue], dataLength); 1856 [header removeObjectForKey:@"Content-Length"]; 1857 1858 if ([header count] > 0) { 1859 [request setAllHTTPHeaderFields:header]; 1860 } 1861 // Everything after the blank line is the actual content of the POST. 1862 postData = [postData subdataWithRange:NSMakeRange(location, dataLength)]; 1863 1864 } 1865 } 1866 if ([postData length] == 0) { 1867 return NPERR_INVALID_PARAM; 1868 } 1869 } 1870 1871 // Plug-ins expect to receive uncached data when doing a POST (3347134). 1872 [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; 1873 [request setHTTPBody:postData]; 1874 1875 return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification]; 1876 } 1877 1878 - (NPError)postURLNotify:(const char *)URLCString 1879 target:(const char *)target 1880 len:(UInt32)len 1881 buf:(const char *)buf 1882 file:(NPBool)file 1883 notifyData:(void *)notifyData 1884 { 1885 LOG(Plugins, "NPN_PostURLNotify: %s", URLCString); 1886 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES]; 1887 } 1888 1889 -(NPError)postURL:(const char *)URLCString 1890 target:(const char *)target 1891 len:(UInt32)len 1892 buf:(const char *)buf 1893 file:(NPBool)file 1894 { 1895 LOG(Plugins, "NPN_PostURL: %s", URLCString); 1896 // As documented, only allow headers to be specified via NPP_PostURL when using a file. 1897 return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file]; 1898 } 1899 1900 -(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream 1901 { 1902 LOG(Plugins, "NPN_NewStream"); 1903 return NPERR_GENERIC_ERROR; 1904 } 1905 1906 -(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer 1907 { 1908 LOG(Plugins, "NPN_Write"); 1909 return NPERR_GENERIC_ERROR; 1910 } 1911 1912 -(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason 1913 { 1914 LOG(Plugins, "NPN_DestroyStream"); 1915 // This function does a sanity check to ensure that the NPStream provided actually 1916 // belongs to the plug-in that provided it, which fixes a crash in the DivX 1917 // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203 1918 if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) { 1919 LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream); 1920 return NPERR_INVALID_INSTANCE_ERROR; 1921 } 1922 1923 WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata); 1924 browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason)); 1925 1926 return NPERR_NO_ERROR; 1927 } 1928 1929 - (const char *)userAgent 1930 { 1931 NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; 1932 1933 if (_isSilverlight) { 1934 // Silverlight has a workaround for a leak in Safari 2. This workaround is 1935 // applied when the user agent does not contain "Version/3" so we append it 1936 // at the end of the user agent. 1937 userAgent = [userAgent stringByAppendingString:@" Version/3.2.1"]; 1938 } 1939 1940 return [userAgent UTF8String]; 1941 } 1942 1943 -(void)status:(const char *)message 1944 { 1945 CFStringRef status = CFStringCreateWithCString(NULL, message ? message : "", kCFStringEncodingUTF8); 1946 if (!status) { 1947 LOG_ERROR("NPN_Status: the message was not valid UTF-8"); 1948 return; 1949 } 1950 1951 LOG(Plugins, "NPN_Status: %@", status); 1952 WebView *wv = [self webView]; 1953 [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status]; 1954 CFRelease(status); 1955 } 1956 1957 -(void)invalidateRect:(NPRect *)invalidRect 1958 { 1959 LOG(Plugins, "NPN_InvalidateRect"); 1960 [self invalidatePluginContentRect:NSMakeRect(invalidRect->left, invalidRect->top, 1961 (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)]; 1962 } 1963 1964 - (void)invalidateRegion:(NPRegion)invalidRegion 1965 { 1966 LOG(Plugins, "NPN_InvalidateRegion"); 1967 NSRect invalidRect = NSZeroRect; 1968 switch (drawingModel) { 1969 #ifndef NP_NO_QUICKDRAW 1970 case NPDrawingModelQuickDraw: 1971 { 1972 ::Rect qdRect; 1973 GetRegionBounds((NPQDRegion)invalidRegion, &qdRect); 1974 invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top); 1975 } 1976 break; 1977 #endif /* NP_NO_QUICKDRAW */ 1978 1979 case NPDrawingModelCoreGraphics: 1980 { 1981 CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion); 1982 invalidRect = *(NSRect*)&cgRect; 1983 break; 1984 } 1985 default: 1986 ASSERT_NOT_REACHED(); 1987 break; 1988 } 1989 1990 [self invalidatePluginContentRect:invalidRect]; 1991 } 1992 1993 -(void)forceRedraw 1994 { 1995 LOG(Plugins, "forceRedraw"); 1996 [self invalidatePluginContentRect:[self bounds]]; 1997 [[self window] displayIfNeeded]; 1998 } 1999 2000 - (NPError)getVariable:(NPNVariable)variable value:(void *)value 2001 { 2002 switch (variable) { 2003 case NPNVWindowNPObject: 2004 { 2005 Frame* frame = core([self webFrame]); 2006 NPObject* windowScriptObject = frame ? frame->script()->windowScriptNPObject() : 0; 2007 2008 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 2009 if (windowScriptObject) 2010 _NPN_RetainObject(windowScriptObject); 2011 2012 void **v = (void **)value; 2013 *v = windowScriptObject; 2014 2015 return NPERR_NO_ERROR; 2016 } 2017 2018 case NPNVPluginElementNPObject: 2019 { 2020 if (!_element) 2021 return NPERR_GENERIC_ERROR; 2022 2023 NPObject *plugInScriptObject = _element->getNPObject(); 2024 2025 // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> 2026 if (plugInScriptObject) 2027 _NPN_RetainObject(plugInScriptObject); 2028 2029 void **v = (void **)value; 2030 *v = plugInScriptObject; 2031 2032 return NPERR_NO_ERROR; 2033 } 2034 2035 case NPNVpluginDrawingModel: 2036 { 2037 *(NPDrawingModel *)value = drawingModel; 2038 return NPERR_NO_ERROR; 2039 } 2040 2041 #ifndef NP_NO_QUICKDRAW 2042 case NPNVsupportsQuickDrawBool: 2043 { 2044 *(NPBool *)value = TRUE; 2045 return NPERR_NO_ERROR; 2046 } 2047 #endif /* NP_NO_QUICKDRAW */ 2048 2049 case NPNVsupportsCoreGraphicsBool: 2050 { 2051 *(NPBool *)value = TRUE; 2052 return NPERR_NO_ERROR; 2053 } 2054 2055 case NPNVsupportsOpenGLBool: 2056 { 2057 *(NPBool *)value = FALSE; 2058 return NPERR_NO_ERROR; 2059 } 2060 2061 case NPNVsupportsCoreAnimationBool: 2062 { 2063 #ifdef BUILDING_ON_TIGER 2064 *(NPBool *)value = FALSE; 2065 #else 2066 *(NPBool *)value = TRUE; 2067 #endif 2068 return NPERR_NO_ERROR; 2069 } 2070 2071 #ifndef NP_NO_CARBON 2072 case NPNVsupportsCarbonBool: 2073 { 2074 *(NPBool *)value = TRUE; 2075 return NPERR_NO_ERROR; 2076 } 2077 #endif /* NP_NO_CARBON */ 2078 2079 case NPNVsupportsCocoaBool: 2080 { 2081 *(NPBool *)value = TRUE; 2082 return NPERR_NO_ERROR; 2083 } 2084 2085 case NPNVprivateModeBool: 2086 { 2087 *(NPBool *)value = _isPrivateBrowsingEnabled; 2088 return NPERR_NO_ERROR; 2089 } 2090 2091 case WKNVBrowserContainerCheckFuncs: 2092 { 2093 *(WKNBrowserContainerCheckFuncs **)value = browserContainerCheckFuncs(); 2094 return NPERR_NO_ERROR; 2095 } 2096 #if USE(ACCELERATED_COMPOSITING) 2097 case WKNVSupportsCompositingCoreAnimationPluginsBool: 2098 { 2099 *(NPBool *)value = [[[self webView] preferences] acceleratedCompositingEnabled]; 2100 return NPERR_NO_ERROR; 2101 } 2102 #endif 2103 default: 2104 break; 2105 } 2106 2107 return NPERR_GENERIC_ERROR; 2108 } 2109 2110 - (NPError)setVariable:(NPPVariable)variable value:(void *)value 2111 { 2112 switch (variable) { 2113 case NPPVpluginDrawingModel: 2114 { 2115 // Can only set drawing model inside NPP_New() 2116 if (self != [[self class] currentPluginView]) 2117 return NPERR_GENERIC_ERROR; 2118 2119 // Check for valid, supported drawing model 2120 NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value; 2121 switch (newDrawingModel) { 2122 // Supported drawing models: 2123 #ifndef NP_NO_QUICKDRAW 2124 case NPDrawingModelQuickDraw: 2125 #endif 2126 case NPDrawingModelCoreGraphics: 2127 #ifndef BUILDING_ON_TIGER 2128 case NPDrawingModelCoreAnimation: 2129 #endif 2130 drawingModel = newDrawingModel; 2131 return NPERR_NO_ERROR; 2132 2133 2134 // Unsupported (or unknown) drawing models: 2135 default: 2136 LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel); 2137 return NPERR_GENERIC_ERROR; 2138 } 2139 } 2140 2141 case NPPVpluginEventModel: 2142 { 2143 // Can only set event model inside NPP_New() 2144 if (self != [[self class] currentPluginView]) 2145 return NPERR_GENERIC_ERROR; 2146 2147 // Check for valid, supported event model 2148 NPEventModel newEventModel = (NPEventModel)(uintptr_t)value; 2149 switch (newEventModel) { 2150 // Supported event models: 2151 #ifndef NP_NO_CARBON 2152 case NPEventModelCarbon: 2153 #endif 2154 case NPEventModelCocoa: 2155 eventModel = newEventModel; 2156 return NPERR_NO_ERROR; 2157 2158 // Unsupported (or unknown) event models: 2159 default: 2160 LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel); 2161 return NPERR_GENERIC_ERROR; 2162 } 2163 } 2164 2165 default: 2166 return NPERR_GENERIC_ERROR; 2167 } 2168 } 2169 2170 - (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc 2171 { 2172 if (!timerFunc) 2173 return 0; 2174 2175 if (!timers) 2176 timers = new HashMap<uint32_t, PluginTimer*>; 2177 2178 uint32_t timerID; 2179 2180 do { 2181 timerID = ++currentTimerID; 2182 } while (timers->contains(timerID) || timerID == 0); 2183 2184 PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc); 2185 timers->set(timerID, timer); 2186 2187 if (_shouldFireTimers) 2188 timer->start(_isCompletelyObscured); 2189 2190 return timerID; 2191 } 2192 2193 - (void)unscheduleTimer:(uint32_t)timerID 2194 { 2195 if (!timers) 2196 return; 2197 2198 if (PluginTimer* timer = timers->take(timerID)) 2199 delete timer; 2200 } 2201 2202 - (NPError)popUpContextMenu:(NPMenu *)menu 2203 { 2204 NSEvent *currentEvent = [NSApp currentEvent]; 2205 2206 // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent. 2207 if (!currentEvent) 2208 return NPERR_GENERIC_ERROR; 2209 2210 [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self]; 2211 return NPERR_NO_ERROR; 2212 } 2213 2214 - (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length 2215 { 2216 switch (variable) { 2217 case NPNURLVCookie: { 2218 if (!value) 2219 break; 2220 2221 NSURL *URL = [self URLWithCString:url]; 2222 if (!URL) 2223 break; 2224 2225 if (Frame* frame = core([self webFrame])) { 2226 String cookieString = cookies(frame->document(), URL); 2227 CString cookieStringUTF8 = cookieString.utf8(); 2228 if (cookieStringUTF8.isNull()) 2229 return NPERR_GENERIC_ERROR; 2230 2231 *value = static_cast<char*>(NPN_MemAlloc(cookieStringUTF8.length())); 2232 memcpy(*value, cookieStringUTF8.data(), cookieStringUTF8.length()); 2233 2234 if (length) 2235 *length = cookieStringUTF8.length(); 2236 return NPERR_NO_ERROR; 2237 } 2238 break; 2239 } 2240 case NPNURLVProxy: { 2241 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 2242 if (!value) 2243 break; 2244 2245 NSURL *URL = [self URLWithCString:url]; 2246 if (!URL) 2247 break; 2248 2249 Vector<ProxyServer> proxyServers = proxyServersForURL(URL, 0); 2250 CString proxiesUTF8 = toString(proxyServers).utf8(); 2251 2252 *value = static_cast<char*>(NPN_MemAlloc(proxiesUTF8.length())); 2253 memcpy(*value, proxiesUTF8.data(), proxiesUTF8.length()); 2254 2255 if (length) 2256 *length = proxiesUTF8.length(); 2257 2258 return NPERR_NO_ERROR; 2259 #else 2260 break; 2261 #endif 2262 } 2263 } 2264 return NPERR_GENERIC_ERROR; 2265 } 2266 2267 - (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length 2268 { 2269 switch (variable) { 2270 case NPNURLVCookie: { 2271 NSURL *URL = [self URLWithCString:url]; 2272 if (!URL) 2273 break; 2274 2275 String cookieString = String::fromUTF8(value, length); 2276 if (!cookieString) 2277 break; 2278 2279 if (Frame* frame = core([self webFrame])) { 2280 setCookies(frame->document(), URL, cookieString); 2281 return NPERR_NO_ERROR; 2282 } 2283 2284 break; 2285 } 2286 case NPNURLVProxy: 2287 // Can't set the proxy for a URL. 2288 break; 2289 } 2290 return NPERR_GENERIC_ERROR; 2291 } 2292 2293 - (NPError)getAuthenticationInfoWithProtocol:(const char*)protocolStr host:(const char*)hostStr port:(int32_t)port scheme:(const char*)schemeStr realm:(const char*)realmStr 2294 username:(char**)usernameStr usernameLength:(uint32_t*)usernameLength 2295 password:(char**)passwordStr passwordLength:(uint32_t*)passwordLength 2296 { 2297 if (!protocolStr || !hostStr || !schemeStr || !realmStr || !usernameStr || !usernameLength || !passwordStr || !passwordLength) 2298 return NPERR_GENERIC_ERROR; 2299 2300 CString username; 2301 CString password; 2302 if (!getAuthenticationInfo(protocolStr, hostStr, port, schemeStr, realmStr, username, password)) 2303 return NPERR_GENERIC_ERROR; 2304 2305 *usernameLength = username.length(); 2306 *usernameStr = static_cast<char*>(NPN_MemAlloc(username.length())); 2307 memcpy(*usernameStr, username.data(), username.length()); 2308 2309 *passwordLength = password.length(); 2310 *passwordStr = static_cast<char*>(NPN_MemAlloc(password.length())); 2311 memcpy(*passwordStr, password.data(), password.length()); 2312 2313 return NPERR_NO_ERROR; 2314 } 2315 2316 - (char*)resolveURL:(const char*)url forTarget:(const char*)target 2317 { 2318 CString location = [self resolvedURLStringForURL:url target:target]; 2319 2320 if (location.isNull()) 2321 return 0; 2322 2323 // We use strdup here because the caller needs to free it with NPN_MemFree (which calls free). 2324 return strdup(location.data()); 2325 } 2326 2327 @end 2328 2329 @implementation WebNetscapePluginView (Internal) 2330 2331 - (BOOL)_shouldCancelSrcStream 2332 { 2333 ASSERT(_isStarted); 2334 2335 // Check if we should cancel the load 2336 NPBool cancelSrcStream = 0; 2337 if ([_pluginPackage.get() pluginFuncs]->getvalue && 2338 [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCancelSrcStream, &cancelSrcStream) == NPERR_NO_ERROR && cancelSrcStream) 2339 return YES; 2340 2341 return NO; 2342 } 2343 2344 // Work around Silverlight full screen performance issue by maintaining an accelerated GL pixel format. 2345 // We can safely remove it at some point in the future when both: 2346 // 1) Microsoft releases a genuine fix for 7288546. 2347 // 2) Enough Silverlight users update to the new Silverlight. 2348 // For now, we'll distinguish older broken versions of Silverlight by asking the plug-in if it resolved its full screen badness. 2349 - (void)_workaroundSilverlightFullscreenBug:(BOOL)initializedPlugin 2350 { 2351 #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 2352 ASSERT(_isSilverlight); 2353 NPBool isFullscreenPerformanceIssueFixed = 0; 2354 NPPluginFuncs *pluginFuncs = [_pluginPackage.get() pluginFuncs]; 2355 if (pluginFuncs->getvalue && pluginFuncs->getvalue(plugin, static_cast<NPPVariable>(WKNVSilverlightFullscreenPerformanceIssueFixed), &isFullscreenPerformanceIssueFixed) == NPERR_NO_ERROR && isFullscreenPerformanceIssueFixed) 2356 return; 2357 2358 static CGLPixelFormatObj pixelFormatObject = 0; 2359 static unsigned refCount = 0; 2360 2361 if (initializedPlugin) { 2362 refCount++; 2363 if (refCount == 1) { 2364 const CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, static_cast<CGLPixelFormatAttribute>(0) }; 2365 GLint npix; 2366 CGLChoosePixelFormat(attributes, &pixelFormatObject, &npix); 2367 } 2368 } else { 2369 ASSERT(pixelFormatObject); 2370 refCount--; 2371 if (!refCount) 2372 CGLReleasePixelFormat(pixelFormatObject); 2373 } 2374 #endif 2375 } 2376 2377 - (NPError)_createPlugin 2378 { 2379 plugin = (NPP)calloc(1, sizeof(NPP_t)); 2380 plugin->ndata = self; 2381 2382 ASSERT([_pluginPackage.get() pluginFuncs]->newp); 2383 2384 // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance. 2385 ASSERT(pluginFunctionCallDepth == 0); 2386 2387 PluginMainThreadScheduler::scheduler().registerPlugin(plugin); 2388 2389 _isFlash = [_pluginPackage.get() bundleIdentifier] == "com.macromedia.Flash Player.plugin"; 2390 _isSilverlight = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.SilverlightPlugin"; 2391 2392 [[self class] setCurrentPluginView:self]; 2393 NPError npErr = [_pluginPackage.get() pluginFuncs]->newp((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL); 2394 [[self class] setCurrentPluginView:nil]; 2395 if (_isSilverlight) 2396 [self _workaroundSilverlightFullscreenBug:YES]; 2397 LOG(Plugins, "NPP_New: %d", npErr); 2398 return npErr; 2399 } 2400 2401 - (void)_destroyPlugin 2402 { 2403 PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin); 2404 2405 if (_isSilverlight) 2406 [self _workaroundSilverlightFullscreenBug:NO]; 2407 2408 NPError npErr; 2409 npErr = ![_pluginPackage.get() pluginFuncs]->destroy(plugin, NULL); 2410 LOG(Plugins, "NPP_Destroy: %d", npErr); 2411 2412 if (Frame* frame = core([self webFrame])) 2413 frame->script()->cleanupScriptObjectsForPlugin(self); 2414 2415 free(plugin); 2416 plugin = NULL; 2417 } 2418 2419 - (NSBitmapImageRep *)_printedPluginBitmap 2420 { 2421 #ifdef NP_NO_QUICKDRAW 2422 return nil; 2423 #else 2424 // Cannot print plugins that do not implement NPP_Print 2425 if (![_pluginPackage.get() pluginFuncs]->print) 2426 return nil; 2427 2428 // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into. 2429 // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format. 2430 NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 2431 pixelsWide:window.width 2432 pixelsHigh:window.height 2433 bitsPerSample:8 2434 samplesPerPixel:4 2435 hasAlpha:YES 2436 isPlanar:NO 2437 colorSpaceName:NSDeviceRGBColorSpace 2438 bitmapFormat:NSAlphaFirstBitmapFormat 2439 bytesPerRow:0 2440 bitsPerPixel:0] autorelease]; 2441 ASSERT(bitmap); 2442 2443 // Create a GWorld with the same underlying buffer into which the plugin can draw 2444 ::Rect printGWorldBounds; 2445 SetRect(&printGWorldBounds, 0, 0, window.width, window.height); 2446 GWorldPtr printGWorld; 2447 if (NewGWorldFromPtr(&printGWorld, 2448 k32ARGBPixelFormat, 2449 &printGWorldBounds, 2450 NULL, 2451 NULL, 2452 0, 2453 (Ptr)[bitmap bitmapData], 2454 [bitmap bytesPerRow]) != noErr) { 2455 LOG_ERROR("Could not create GWorld for printing"); 2456 return nil; 2457 } 2458 2459 /// Create NPWindow for the GWorld 2460 NPWindow printNPWindow; 2461 printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr 2462 printNPWindow.x = 0; 2463 printNPWindow.y = 0; 2464 printNPWindow.width = window.width; 2465 printNPWindow.height = window.height; 2466 printNPWindow.clipRect.top = 0; 2467 printNPWindow.clipRect.left = 0; 2468 printNPWindow.clipRect.right = window.width; 2469 printNPWindow.clipRect.bottom = window.height; 2470 printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window 2471 2472 // Create embed-mode NPPrint 2473 NPPrint npPrint; 2474 npPrint.mode = NP_EMBED; 2475 npPrint.print.embedPrint.window = printNPWindow; 2476 npPrint.print.embedPrint.platformPrint = printGWorld; 2477 2478 // Tell the plugin to print into the GWorld 2479 [self willCallPlugInFunction]; 2480 { 2481 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 2482 [_pluginPackage.get() pluginFuncs]->print(plugin, &npPrint); 2483 } 2484 [self didCallPlugInFunction]; 2485 2486 // Don't need the GWorld anymore 2487 DisposeGWorld(printGWorld); 2488 2489 return bitmap; 2490 #endif 2491 } 2492 2493 - (void)_redeliverStream 2494 { 2495 if ([self dataSource] && _isStarted) { 2496 // Deliver what has not been passed to the plug-in up to this point. 2497 if (_dataLengthReceived > 0) { 2498 NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)]; 2499 _dataLengthReceived = 0; 2500 [self pluginView:self receivedData:data]; 2501 if (![[self dataSource] isLoading]) { 2502 if (_error) 2503 [self pluginView:self receivedError:_error.get()]; 2504 else 2505 [self pluginViewFinishedLoading:self]; 2506 } 2507 } 2508 } 2509 } 2510 2511 @end 2512 2513 #endif 2514