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 */ 88static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying bug in <rdar://problem/7288546> */ 89 90using namespace WebCore; 91using namespace WebKit; 92using namespace std; 93 94static 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 111static WebNetscapePluginView *currentPluginView = nil; 112 113typedef struct OpaquePortState* PortState; 114 115static const double ThrottledTimerInterval = 0.25; 116 117class PluginTimer : public TimerBase { 118public: 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 145private: 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 164typedef 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 176typedef 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 232static 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 258static 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 268static 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